ATENCION: Esta faq ya no se mantiene. No se añaden nuevas entradas. La nueva ubicación de esta faq es mi blog dentro de Geeks.ms, selecciona la sección dedicada a C++ Geeks.ms es una comunidad online donde podrás encontrar los blogs y foros de un buen puñado de MVPs y MCTs.
Esta faq es mantenida por Rodrigo Corral González [MicrosoftMost Valuable Professional]
Esta faq se puede distribuir y modificar libremente, si la modificas seria buena idea que compartieses esas modificaciones. Si tienes alguna sugerencia no dudes en hacerla a través del grupo de news o usando el correo que puedes encontrar en el pie de pagina
Nuevo proyecto de formación con cursos creados en exclusiva por MVPs!!! Hechale un vistazo a la web de Campus MVP, hay gran variedad de cursos online impartidos por los profesionales mejor valorados por Microsoft!!! Además estos cursos pueden salirte gratis
¿Como puedo utilizar una DLL de Visual Basic desde VC++? ¿Como crear una DLL en VC++ que se pueda usar desde VBScript y Visual Basic? ¿Como puedo obtener el directorio en el que se encuentra el ejecutable de la aplicación? ¿Como detectar si una aplicación ya esta corriendo? ¿Como acceder a bases de datos desde VC++? ¿Cuales son las diferencias entre MFC/WTL/STL/ATL? ¿Como hago que una ventana sea siempre visible? ¿Cómo puedo hacer que la ejecución de una tarea en mi progama no me bloquee la ventana? ¿Como programo con hilos? ¿Es Visual C++ 'visual'? ¿Es una herramienta RAD? ¿Como obtengo el path de capetas del sistema (por ejemplo System, Archivos de Programa etc...)? ¿Como mostrar imagenes en una ventana? ¿Que son las DLL de extensión? ¿Que hago si no quiero depender de una DLL pero quiero hacer un modulo con funciones reutilizables? El combo no se despliega ¿qué ocurre? ¿Como minimizo las dll de las que depende mi ejecutable? ¿Cómo puedo acceder al puerto serie/paralelo? ¿Cómo hago que mi aplicación soporte multiples idiomas? ¿Cómo se si mi proceso fue lanzado con privilegios de administrador?
Las DLL construidas con VB exportan objetos COM.
La manera más simple de usar objetos COM desde VC++ es usando la directiva #import y los smart pointers de COM que genera esta directiva.
El mecanismo sería algo como:
#import "tuDLL.DLL" using namespace tuDLL ... //Crear un objeto _TuClaseVBPtr tuClase(__uuidof(TuClaseVB)); tuClase->MetodoDeTuClaseVB();
Basicamente tienes que crear una DLL COM. y tener en cuenta que las interfaces de los objetos que exporten deben de ser duales y debes marcarlas como oleautomation.
La manera más simple de crear objetos COM en VC++ es usar ATL.
Este link te sera de utilidad: Building COM Components That Take Full Advantage of Visual Basic and Scripting Eso si, si no estas familiarizado con el desarrollo COM en VC++, bien mediante MFC o ATL la tarea no es simple.
TCHAR szPath[MAX_PATH]; GetModuleFileName(NULL, szPath, MAX_PATH); //Quitamos el nombre del ejecutable //Tambien puedes usar PathRemoveFileSpec o splitpath si no estamos usando MFC o ATL CString strAppPath(szPath); strAppPath = strAppPath.Left(strAppPath.ReverseFind('\\'));
La forma más habitual de hacerlo es creando un objeto mutex global (con nombre) y verificar su estado. El siguiente fragmente de código Win32 muestra cómo hacerlo:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; // Codigo previo... // Controlar instancias previas HANDLE hMtx = CreateMutex( NULL, FALSE, "{FA531CC1-0497-11d3-A180-001052276C3E}" ); if( GetLastError() == ERROR_ALREADY_EXISTS ) return FALSE; // Resto de código... return msg.wParam; }
Un mutex con nombre es un objeto Kernel que puede ser verificado desde cualquier proceso. La primera aplicación lo creará sin problema, pero al intentar crearlo desde un segundo proceso, retornará ERROR_ALREADY_EXISTS. Retornando FALSE desde el WinMain provocamos la salida del programa.
Otro enfoque que tambien que se utiliza en ocasiones es buscar la ventana principal de tu aplicación con la función FindWindow. La ventaja es que una vez localizada la ventana puedes traer esta a primer plano, en lugar de simplemente informar al usuario de que no puede arracar otra instancia de la aplicación.
La mejor opción es usar ADO mediante la directiva #import. Lee el siguiente articulo para saber como hacerlo:
Artículo de Microsoft Knowledge Base - 169496 INFO: Using ActiveX Data Objects (ADO) via #import in VC++
MFC (Microsoft Fundation Classes): Libreria basada en jeraquia de clases, basicamente encapsula el API de windows en una forma orientada a objetos.
ATL (Active Template Library): Es una libreria basada en templates, que se creo para relizar controles y librerias ActiveX (COM) de bajo peso. El motivo fue el excesivo peso que MFC añadia a los ActiveX que perjidicaba su distribución desde internet.
WTL (Windows Template Library): Es una libreria en cierto modo similar a MFC solo que basada en templates y menos extensa, por ejemplo carece de clases para sockets, o bases de datos. Esta muy relacionada con ATL, a la que añade la posibilidad de trabajar con ventanas.
STL (Standar Template Libary): Es la libreria estandar de C++, se puede encontrar en practicamente cualquier compilador de C++. Contiene clases para multiples propositos como porejemplo trabajar con cadenas, algoritmos, clases de colecciones (listas,mapas...), los streams de C++ y muchas consas mas. Puedes mirar: http://www.sgi.com/tech/stl/ http://www.stlport.org/
Hay que usar el api SetWindowPos de la siguente manera:
Para hacer la ventan siempre visible
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE |WP_NOSIZE);
Para devolverle el comportamiento normal
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE| WP_NOSIZE);
Si estas trabajando en MFC la función SetWindowPos sera miembro de la clase que representa tu ventana.
Para evitarlo lo que tienes que hacer es crear un hilo de ejecución para realizar la tarea que deja bloqueada la ventana. Si no quieres complicarte con hilos y la tarea que realizas es de tipo iterativo puedes poner en cada paso de bucle llamadas a la siguiente función, que permite que se procesen los mensajes de ventana de manera que esta no se quede muerta, de todos modos te recomiento que uses los hilos, ya que este método consume mucha CPU. void CApplication::DoEvents() { MSG msg; while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { if ( !PumpMessage( ) ) { ::PostQuitMessage(0); break; } } // let MFC do its idle processing for (long lIdle = 0; OnIdle(lIdle); lIdle++ ){;} }
Basicamente, lo que necesitas conocer a es: 1) Como levantar hilos 2) Como sincronizar el trabajo realizado por hilos (conocer los objetos de sincronización) Esto depende de la plataforma en la que estes programando (C 'puro', C++, UNIX, Win32, MFC) ya que cada plataforma expone diferentes objetos y funciones para realizar estas tareas. Existen librerias que te abstraen de estas diferencias (p.e.: Boost.Threads http://www.boost.org/libs/thread/doc/index.html) Otro tema es el diseño de programas multihilo eficientes y sin problemas, que es un arte más que una ciencia y como tal depende mucho de la experiencia. Si quieres centrarte en plataforma Windows, busca Multitherading en la MSDN, tienes información para dar y colgar. Articulos interesantes en la MSDN son: Multithreading for Rookies Synchronization on the Fly Using Multithreading and C++ to Generate Live Objects Por ultimo dos libros, que te comento por que los he leido, no por que sean los mejores que eso no lo se. Este centrado en Windows: Multithreading Applications in Win32 : The Complete Guide to Threads by Jim Beveridge (Author), Robert Wiener (Author) Y este que habla de las posiblidades en diferentes sistemas operativos (ojo, no sirve para aprender) Multithreading Programming Techniques (J. Ranade Workstation Series) by Shashi Prasad, Shshi Prasad, Shashi Prashad
Debes usar la función SHGetFolderPath, si quieres añadir un subdirectorio al valor devuelto (p.e.: C:\Archivos de programa\Tu applicacion) lo puedes hacer directamente con la función SHGetFolderPathAndSubDir. Estas funciones tienen como requisito Windows 95 con Internet Explorer 5.0, Windows 98 con Internet Explorer 5.0, Windows 98 Second Edition (SE), Windows NT 4.0 con Internet Explorer 5.0, Windows NT 4.0 con Service Pack 4 (SP4).
Tambien puedes utilizar las siguientes funciones:
GetSystemDirectory(), para obtener el directorio de sistema. GetWindowsDirectory(), para obtener el directorio de windows. GetTempPath(), para obtener el directorio temporal.
Conozco 2 maneras de realizar esto: 1) Utilizar la clase CPiture (que encapsula la interfaz IPicture), escrita por Paul Dilascia, que permite usar JPG, GIFF y BITMAP y sobre la que puedes encontrar información en los siguientes vinculos: Displaying a JPG in your MFC Application 2) Utilizar el nuevo API GDI+ que permite trabajar con BMP, JPEG, PNG, GIF y TIFF y estas disponible como un runtime de aproximadamente 1 Mb de tamaño. Esta libreria es propocionada sin ningún royalti como distribuible por Microsoft. Mira el siguiente vinculo: GDI+ Para cargar una imagen JPEG y mostarla en una ventana conocido el handle de su DC en puedes usar la siguiente función: void ShowJPG(HANDLE hDC) { Graphics graphics(hDC); Image image("photo.jpg"); graphics.DrawImage(&image, 10, 10); }
Las DLL de extensión de MFC tipicamente exponen classes derivadas de clases standard existente en MFC. Solo se pueden linkar de manera dinamica con lavDLL de runtima de MFC y solo aplicacines u otras DLL linkadas de manera dinamica con la DLL de runtime de MFC pueden usar DLL de extensión. El pricipal proposito de las DLL de extensión es derivar tu propias clases de clases de MFC y permitir que otras aplicaciones usen estas clases extendidas a través de la DLL de extensión. Las DLL de extensión tambien son utiles cuando en la interfaz de la DLL se exponen objetos de MFC (p.e. una función que toma como parametros un CEdit), aunque en mi opinión esto es un mal diseño en la mayoria de los casos. Excepto en los casos expuesto arriba, es preferible usar DLL normales.
Crea un libreria estática (.LIB) en lugar de una libreria dinámica. Tambien puedes convertir tu librerias dinamicas en estaticas sin tener que cambiar el código.
Cuando pones un control combo sobre un dialogo en el editor de dialogos, tienes que dimensionar el tamaño del desplegable, que por defecto es muy pequeño. Para ello selecciona el control en el editor, pulsa sobre el icono de depliegue del combo y arrastra los cuadraditos de dimensionar.
Tambien lo puedes hacer por código:
ComboBox.SetWindowPos(NULL,0,0,100,200,SWP_NOMOVE | SWP_NOZORDER);
La solución consiste en linkar estáticamente el runtima de MFC (menú Project\Settings\Microsoft foundation class) y el runtime de C/C++ (Project\Settings\C/C++\Category\Code Generation\Use Runtime Library) Esto no evitará las dependencias de DLLs o controles ActiveX (OCX) que deberás distribuir con tu ejecutable.
Si usas librerias dinámicas (DLL) propias conviertelas en librerias estaticas (LIB), lee la FAQ #17 para saber como.
Tienes que usar las funciones del API de Win32 habituales para trabajar con ficheros, OpenFile, ReadFile, WriteFile, expecificando como nombre de fichero el nombre del puerto que quieres usar "LPTx" o "COMx".
Este articulo sobre el tema es muy intersante Serial Communication in Windows By Ashish Dhar
Un enfoque que se utiliza bastante es el de tener una dll de recursos para cada idioma que se desea soportar. Esta dll se carga dinamicamente con LoadLibray (o AfxLoadLibray en MFC) y se establece como origen de los recursos. Para saber como crear esta dll lee Creating a Resource-Only DLL en la MSDN. Tipicamente harás esto en InitInstace: BOOL CMyApp::InitInstance() { //Default Appwizard code. HINSTANCE hRes = NULL; hRes= LoadLibrary("ResourceD.dll"); if(hRes) AfxSetResourceHandle(hRes); //Rest of wizard code return CWinApp::InitInstance(); } En ATL/WTL para establecer la dll cargada como origen de los recursos se utiliza la función CAtlBaseModule::SetResourceInstance. Usando el API se utiliza LoadResource, LoadString etc... para cargar los recursos, pasando como primer parametro el handle de la libreria de recursosque previamente hemos cargado con LoadLibrary.
Aqui va una función que permite averiguarlo:
//Comprueba que el usuario que inicio el proceso sea administrador bool UserIsAdmin(void) { bool bSuccess = false; //Obetenemos el token del proceso HANDLE hToken = NULL; if ( !OpenProcessToken ( GetCurrentProcess ( ), TOKEN_QUERY, &hToken ) ) return false;
//Obtenemos el tamaño necesarios para la información del grupos //asociados al token del proceso DWORD dwSize = 0; bSuccess = GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize) != 0; if( !bSuccess && ERROR_INSUFFICIENT_BUFFER != ::GetLastError() ) return false;
//Dimensionamos el buffer para los tokens de los grupos PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS) new BYTE[dwSize];
//Obtenemos la inforamción sobre los grupos bSuccess = GetTokenInformation ( hToken, TokenGroups, (LPVOID)pGroups, dwSize, &dwSize ) != 0;
//Cerramos el token del proceso CloseHandle ( hToken );
if ( !bSuccess ) return false;
//Obtenemos el sid de aministrador SID_IDENTIFIER_AUTHORITY siaNtAuth = SECURITY_NT_AUTHORITY; PSID pAdminSid = NULL; if ( !AllocateAndInitializeSid ( &siaNtAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0,0,0,0,0,0, &pAdminSid ) ) return false;
bSuccess = false; //Comprobamos si el token de administrador esta entre los del //proceso, si es asi el proceso tiene privilegios de administrador for ( DWORD i = 0; (i < pGroups->GroupCount) && !bSuccess; i++ ) { if ( EqualSid ( pAdminSid, pGroups->Groups[i].Sid ) ) bSuccess = true; }
//Liberamos el sid FreeSid ( pAdminSid ); //Liberamos la memoria delete[] pGroups; return bSuccess;
}