Tutorial C++ 4 Creación de archivos DUMP
Categorías : Windows, Programación, C y C++.

Los archivos DUMP son una forma de depurar aplicaciones con windows. La idea consiste en que cuando una aplicación falla críticamente se guarda un archivo con el contenido de la memoria de esta.
Más tarde podemos abrir esos archivos con el Visual Studio y depurar la aplicación (siempre que tengamos su código fuente a mano).
Por ejemplo BubaTronik si tiene un error critico, crea un archivo dump y pide al usuario permiso para mandarme este archivo dump a mi cuenta de correo. Cosa que es muy útil para depurar errores que yo no he visto en mi maquina.
Para ver cómo funciona este proceso, vamos a hacer una aplicación muy básica que consistirá en una ventana vacía, la cual al ser clickeada con el botón izquierdo provocara un error crítico.
Función MAIN
int APIENTRY WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpCmdLine, int nCmdShow){// Asignación del filtro de erroresLPTOP_LEVEL_EXCEPTION_FILTER Filtro;Filtro = SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)FiltroError);// Creación de la clase ventanaWNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hIns;wcex.hIcon = NULL;wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName = NULL;wcex.lpszClassName = TEXT("VentanaDump");wcex.hIconSm = NULL;RegisterClassEx(&wcex);// Creación de la ventanaHWND hWndVentana = CreateWindowEx( NULL, TEXT("VentanaDump"), TEXT("Ventana Dump"),WS_OVERLAPPEDWINDOW | WS_VISIBLE,100, 100, 200, 200, NULL, NULL,GetModuleHandle(NULL), NULL);// Bucle de mensajes principalMSG msg;while (GetMessage(&msg, NULL, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return 0;}
Lo más destacable de esta función WinMain son las
Luego llamamos a la API SetUnhandledExceptionFilter para asignar nuestra función para filtrar los errores, y esta nos devuelve la dirección del filtro de errores por defecto que tenía asignado la aplicación.
Con esto acabamos de re-direccionar los errores críticos a nuestra propia función, en la cual podemos entre otras osas escribir el archivo DUMP.
Función FiltroError
LONG WINAPI FiltroError(struct _EXCEPTION_POINTERS* ExceptionInfo) {TCHAR PathAppData[MAX_PATH];TCHAR PathDump[MAX_PATH];if (ExceptionInfo == NULL) return -1;// Obtenemos la carpeta APP_DATA donde podemos escribir cosas sin necesidad de permisos de administradorSHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, PathAppData);swprintf_s(PathDump, MAX_PATH, TEXT("%s\\DUMP.dmp"), PathAppData);// Creamos el archivo DUMP.dmp para empezar a escribir los datosHANDLE hFile = CreateFile(PathDump, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);// Si no hay ningún error al crear el archivo..if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) {// Rellenamos la estructura con la información de la excepciónMINIDUMP_EXCEPTION_INFORMATION mdei;mdei.ThreadId = GetCurrentThreadId();mdei.ExceptionPointers = ExceptionInfo;mdei.ClientPointers = FALSE;// Rellenamos la estructura con la información del callbackMINIDUMP_CALLBACK_INFORMATION mci;mci.CallbackRoutine = NULL;mci.CallbackParam = 0;// Creamos una variable con las opciones que se usaran para crear el dumpMINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory | MiniDumpNormal);// Escribimos el dump en el archivoBOOL rv;rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, &mci);// Cerramos el archivo dumpCloseHandle(hFile);// Abrimos la carpeta donde se ha guardado el DUMP.dmpShellExecute(NULL, TEXT("open"), PathAppData, TEXT(""), PathAppData, SW_SHOW);}return TRUE;}
Esta función en primer lugar obtiene la ruta del directorio
Una vez tiene la ruta donde guardar el archivo
Cuando se ha usado la API CreateFile,
- La primera estructura es del tipo MINIDUMP_EXCEPTION_INFORMATION donde se guardan la ID del thread actual, y la información de la excepción.
- La segunda estructura es del tipo MINIDUMP_CALLBACK_INFORMATION donde se puede indicar un callback para añadir más datos al dump, pero que en este caso no vamos a utilizar.
- Luego se crea una variable del tipo
MINIDUMP_TYPE que contiene las opciones que se usaran para crear el dump.
Una vez tenemos las variables inicializadas se utiliza la API MiniDumpWriteDump para escribir el volcado de memoria dentro del archivo que creamos anteriormente.
Cuando tenemos los datos en el archivo, lo cerramos utilizando la API CloseHandle.
Y por ultimo se utiliza la API ShellExecute para mostrarnos el directorio donde ha guardado el archivo DUMP.dmp, para que de esta forma nos sea más fácil de ejecutarlo.
Ahora que ya hemos visto la función FiltroError, nos falta por ver como provocamos el error en el WndProc :
Función WndProc
// Window procedure para la ventana que provoca el error.LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {int *i = NULL;switch (message) {case WM_LBUTTONUP :MessageBox( hWnd, TEXT("Se va a provocar un error, para que podáis abrir el archivo DUMP.dmp con el visual studio"),TEXT("Provocando error"), MB_OK);*i = 2; // intentamos asignar el valor 2 a un puntero que no se ha inicializado, lo que provoca un error criticoreturn 0;case WM_CLOSE :PostQuitMessage(0);return 0;default :return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}
Ahora que tenemos una idea bastante más clara de cómo funciona todo esto, vamos a ver cómo hay que utilizar estos archivos 'dmp'.
En primer lugar esto solo funciona si la aplicación está corriendo fuera del depurador, puede ser tanto debug como release, pero es necesario que no la estemos ejecutando desde VisualStudio, así que lo primero será compilar el ejecutable (para hacer la prueba mejor lo compilaremos en modo Release), una vez compilado nos vamos a la carpeta donde tenemos los tutoriales WINAPI, y entramos dentro de la carpeta 'Release'. Allí buscamos el archivo '4 Tutorial crear archivo DUMP.exe', y lo ejecutamos. Una vez ejecutado hacemos un click izquierdo dentro de la ventana que nos sale, para provocar el error, y nos aparecerá un MessageBox. Una vez aceptemos el MessageBox se abrirá una carpeta del explorador, y si buscamos en esta encontraremos un archivo llamado 'DUMP.dmp'. Al hacer dobleclick en el archivo se abriría el Visual Studio y nos mostrara una pantalla similar a esta :


A la derecha de esta pantalla debería salir un menú que nos da tres opciones : 'Depurar con Solo Nativo', 'Establecer rutas de acceso de símbolos' y 'Copiar todo en el Portapapeles'.
La razón de que veamos esta pantalla es porque el Visual Studio no ha sabido encontrar los símbolos para poder depurar la aplicación, por lo que vamos a tener que indicarle la ruta de donde puede encontrar estos símbolos.
Por lo que vamos a hacer click en 'Establecer rutas de acceso de símbolos', y nos mostrara una pantalla similar a esta, aunque probablemente vacía :

En esta pantalla vamos a tener que añadir el directorio donde se encuentra el ejecutable '4 Tutorial crear archivo DUMP.exe', que también es el mismo donde se han creado los símbolos de depuración.
Finalmente ya podemos hacer click en "Depurar solo nativo" y voila, vemos la línea de código que ha petado.
Y esto es todo!, en un futuro si encuentro un servidor de correo publico que permita enviar emails mediante smtp veremos cómo podemos mandarnos archivos dump al correo en caso de que una de nuestras aplicaciones tenga un error critico.
De todas formas ya os aviso que si queréis hacer algo así, el tema de enviar el correo se tiene que hacer ejecutando una nueva instancia de la aplicación, y no desde el filtro de errores.