Search code examples
c++winapiminidump

Creating a MiniDump of a running process


Im trying to make a tool for my end users that can create a MiniDump of my application if it hangs (i.e. external to the app). Im using the same code as the internal MiniDumper but with the handle and processid of the app but i keep getting error code 0xD0000024 when calling MiniDumpWriteDump. Any ideas?

void produceDump( const char* exe )
{
    DWORD processId = 0;
    HANDLE process = findProcess(exe, processId);

    if (!process || processId == 0)
    {
        printf("Unable to find exe %s to produce dump.\n", exe);
        return;
    }

    LONG retval = EXCEPTION_CONTINUE_SEARCH;
    HWND hParent = NULL;                        // find a better value for your app

    // firstly see if dbghelp.dll is around and has the function we need
    // look next to the EXE first, as the one in System32 might be old 
    // (e.g. Windows 2000)
    HMODULE hDll = NULL;
    char szDbgHelpPath[_MAX_PATH];

    if (GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ))
    {
        char *pSlash = _tcsrchr( szDbgHelpPath, '\\' );
        if (pSlash)
        {
            _tcscpy( pSlash+1, "DBGHELP.DLL" );
            hDll = ::LoadLibrary( szDbgHelpPath );
        }
    }

    if (hDll==NULL)
    {
        // load any version we can
        hDll = ::LoadLibrary( "DBGHELP.DLL" );
    }

    LPCTSTR szResult = NULL;

    int err = 0;

    if (hDll)
    {
        MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
        if (pDump)
        {
            char szDumpPath[_MAX_PATH];
            char szScratch [_MAX_PATH];

            time_t rawtime;
            struct tm * timeinfo;

            time ( &rawtime );
            timeinfo = localtime ( &rawtime );

            char comAppPath[MAX_PATH];
            SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA , NULL, SHGFP_TYPE_CURRENT, comAppPath );


            //COMMONAPP_PATH
            _snprintf(szDumpPath, _MAX_PATH, "%s\\DN", comAppPath);
            CreateDirectory(szDumpPath, NULL);

            _snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D", comAppPath);
            CreateDirectory(szDumpPath, NULL);

            _snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps", comAppPath);
            CreateDirectory(szDumpPath, NULL);

            char fileName[_MAX_PATH];
            _snprintf(fileName, _MAX_PATH, "%s_Dump_%04d%02d%02d_%02d%02d%02d.dmp", exe, timeinfo->tm_year+1900, timeinfo->tm_mon, timeinfo->tm_mday,  timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec );
            _snprintf(szDumpPath, _MAX_PATH, "%s\\DN\\D\\dumps\\%s", comAppPath, fileName);

            // create the file
            HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
            if (hFile!=INVALID_HANDLE_VALUE)
            {
                MINIDUMP_CALLBACK_INFORMATION mci; 

                mci.CallbackRoutine     = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback; 
                mci.CallbackParam       = 0; 

                MINIDUMP_TYPE mdt       = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory | 
                                                          MiniDumpWithDataSegs | 
                                                          MiniDumpWithHandleData |
                                                          //MiniDumpWithFullMemoryInfo | 
                                                          //MiniDumpWithThreadInfo | 
                                                          MiniDumpWithProcessThreadData |
                                                          MiniDumpWithUnloadedModules ); 

                // write the dump
                BOOL bOK = pDump( process, processId, hFile, mdt, NULL, NULL, &mci );
                DWORD lastErr = GetLastError();

                if (bOK)
                {
                    printf("Crash dump saved to: %s\n", szDumpPath);
                    return;
                }
                else
                {
                    _snprintf( szScratch, _MAX_PATH, "Failed to save dump file to '%s' (error %u)", szDumpPath, lastErr);
                    szResult = szScratch;
                    err = ERR_CANTSAVEFILE;
                }
                ::CloseHandle(hFile);
            }
            else
            {
                _snprintf( szScratch, _MAX_PATH, "Failed to create dump file '%s' (error %u)", szDumpPath, GetLastError());
                szResult = szScratch;
                err = ERR_CANTMAKEFILE;
            }
        }
        else
        {
            szResult = "DBGHELP.DLL too old";
            err = ERR_DBGHELP_TOOLD;
        }
    }
    else
    {
        szResult = "DBGHELP.DLL not found";
        err = ERR_DBGHELP_NOTFOUND;
    }

    printf("Could not produce a crash dump of %s.\n\n[error: %u %s].\n", exe, err, szResult);
    return;
}

this code works 100% when its internal to the process (i.e. with SetUnhandledExceptionFilter)


Solution

  • Are you opening the process with the necessary access rights? MiniDumpWriteDump() needs the process handle to be opened using PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights. When using GetCurrentProcess(), I think these are granted automatically, but when using OpenProcess() to open another process, you have to request these rights.

    To do so, you might also have to enable SeDebugPrivilege, which would cause problems for users whose accounts don't have that privilege. But the documentation doesn't seem to be clear on whether SeDebugPrivilege is necessary for PROCESS_QUERY_INFORMATION and PROCESS_VM_READ rights specifically (as opposed to all process access rights), particularly when opening a process that is running as the same user account.