转载:https://www.easyicon.net/(免费icon)

转载:https://www.codeproject.com/Articles/5260/XCrashReport-Exception-Handling-and-Crash-Report-4(codeproject示例demo)

转载:https://blog.csdn.net/agan4014/article/details/2614770

转载:http://blog.sina.com.cn/s/blog_5513eb7b0100nu80.html

转载:https://blog.csdn.net/sunflover454/article/details/51200663

实现原理:在程序运行过程中异常,通过捕获异常,并在回调函数启动BugReport程序

实现效果

第一步:导入上面的源码文件到自己的工程中

CrashFileNames.h

#ifndef CRASHFILENAMES_H
#define CRASHFILENAMES_H#define XCRASHREPORT_MINI_DUMP_FILE        _T("CRASH.DMP")//Crash文件
#define XCRASHREPORT_ERROR_LOG_FILE        _T("ERRORLOG.TXT")//日志文件
#define XCRASHREPORT_CRASH_REPORT_APP      _T("xxxBugReport.exe")//你自己BugReport程序名字#endif //CRASHFILENAMES_H

ExceptionHandler.h

#ifndef    EXCEPTIONHANDLER_H
#define    EXCEPTIONHANDLER_Htypedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS data, const TCHAR *Message);#endif

ExceptionHandler.cpp

#pragma warning(disable : 4514)
#pragma warning(disable : 4201)#define _WIN32_WINDOWS 0x0500    // for IsDebuggerPresent// comment out this line if you don't want minidumps
#define XCRASHREPORT_WRITE_MINIDUMP
//#define XCRASHREPORT_WRITE_ERROR_LOG// does not require MFC;  use 'Not using precompiled headers'

#include "windows.h"
#include <tchar.h>
#include "GetWinVer.h"
#include "miniversion.h"
#include <DbgHelp.h>
#include "CrashFileNames.h"#pragma comment(lib, "Dbghelp.lib")#ifndef _countof
#define _countof(array) (sizeof(array)/sizeof(array[0]))
#endifconst int NumCodeBytes = 16;    // Number of code bytes to record.
const int MaxStackDump = 3072;    // Maximum number of DWORDS in stack dumps.
const int StackColumns = 4;        // Number of columns in stack dump.#define    ONEK            1024
#define    SIXTYFOURK        (64*ONEK)
#define    ONEM            (ONEK*ONEK)
#define    ONEG            (ONEK*ONEK*ONEK)///
// lstrrchr (avoid the C Runtime )
static TCHAR * lstrrchr(LPCTSTR string, int ch)
{TCHAR *start = (TCHAR *)string;while (*string++)                       /* find end of string */;/* search towards front */while (--string != start && *string != (TCHAR) ch);if (*string == (TCHAR) ch)                /* char found ? */return (TCHAR *)string;return NULL;
}#define HPRINTF_BUFFER_SIZE (8*1024)                // must be at least 2048
static TCHAR hprintf_buffer[HPRINTF_BUFFER_SIZE];    // wvsprintf never prints more than one K.
static int  hprintf_index = 0;///
// hflush
static void hflush(HANDLE LogFile)
{if (hprintf_index > 0){DWORD NumBytes;WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer), &NumBytes, 0);hprintf_index = 0;}
}///
// hprintf
static void hprintf(HANDLE LogFile, LPCTSTR Format, ...)
{if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024)){DWORD NumBytes;WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer), &NumBytes, 0);hprintf_index = 0;}va_list arglist;va_start( arglist, Format);hprintf_index += wvsprintf(&hprintf_buffer[hprintf_index], Format, arglist);va_end( arglist);
}#ifdef XCRASHREPORT_WRITE_MINIDUMP///
// DumpMiniDump
static void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
{if (excpInfo == NULL) {// Generate exception to get proper context in dump
        __try {OutputDebugString(_T("raising exception\r\n"));RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);} __except(DumpMiniDump(hFile, GetExceptionInformation()),EXCEPTION_CONTINUE_EXECUTION) {}} else{OutputDebugString(_T("writing minidump\r\n"));MINIDUMP_EXCEPTION_INFORMATION eInfo;eInfo.ThreadId = GetCurrentThreadId();eInfo.ExceptionPointers = excpInfo;eInfo.ClientPointers = FALSE;// note:  MiniDumpWithIndirectlyReferencedMemory does not work on Win98
        MiniDumpWriteDump(GetCurrentProcess(),GetCurrentProcessId(),hFile,MiniDumpNormal,excpInfo ? &eInfo : NULL,NULL,NULL);}
}#endif    // XCRASHREPORT_WRITE_MINIDUMP///
// FormatTime
//
// Format the specified FILETIME to output in a human readable format,
// without using the C run time.
static void FormatTime(LPTSTR output, FILETIME TimeToPrint)
{output[0] = _T('\0');WORD Date, Time;if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) &&FileTimeToDosDateTime(&TimeToPrint, &Date, &Time)){wsprintf(output, _T("%d/%d/%d %02d:%02d:%02d"),(Date / 32) & 15, Date & 31, (Date / 512) + 1980,(Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2);}
}///
// DumpModuleInfo
//
// Print information about a code module (DLL or EXE) such as its size,
// location, time stamp, etc.
static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo)
{bool rc = false;TCHAR szModName[MAX_PATH*2];ZeroMemory(szModName, sizeof(szModName));__try{if (GetModuleFileName(ModuleHandle, szModName, sizeof(szModName)-2) > 0){// If GetModuleFileName returns greater than zero then this must// be a valid code module address. Therefore we can try to walk// our way through its structures to find the link time stamp.IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle;if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic)return false;IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((TCHAR *)DosHeader+ DosHeader->e_lfanew);if (IMAGE_NT_SIGNATURE != NTHeader->Signature)return false;// open the code module file so that we can get its file date and sizeHANDLE ModuleFile = CreateFile(szModName, GENERIC_READ,FILE_SHARE_READ, 0, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0);TCHAR TimeBuffer[100];TimeBuffer[0] = _T('\0');DWORD FileSize = 0;if (ModuleFile != INVALID_HANDLE_VALUE){FileSize = GetFileSize(ModuleFile, 0);FILETIME LastWriteTime;if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime)){FormatTime(TimeBuffer, LastWriteTime);}CloseHandle(ModuleFile);}hprintf(LogFile, _T("Module %d\r\n"), nModuleNo);hprintf(LogFile, _T("%s\r\n"), szModName);hprintf(LogFile, _T("Image Base: 0x%08x  Image Size: 0x%08x\r\n"), NTHeader->OptionalHeader.ImageBase, NTHeader->OptionalHeader.SizeOfImage), hprintf(LogFile, _T("Checksum:   0x%08x  Time Stamp: 0x%08x\r\n"), NTHeader->OptionalHeader.CheckSum,NTHeader->FileHeader.TimeDateStamp);hprintf(LogFile, _T("File Size:  %-10d  File Time:  %s\r\n"),FileSize, TimeBuffer);hprintf(LogFile, _T("Version Information:\r\n"));CMiniVersion ver(szModName);TCHAR szBuf[200];WORD dwBuf[4];ver.GetCompanyName(szBuf, sizeof(szBuf)-1);hprintf(LogFile, _T("   Company:    %s\r\n"), szBuf);ver.GetProductName(szBuf, sizeof(szBuf)-1);hprintf(LogFile, _T("   Product:    %s\r\n"), szBuf);ver.GetFileDescription(szBuf, sizeof(szBuf)-1);hprintf(LogFile, _T("   FileDesc:   %s\r\n"), szBuf);ver.GetFileVersion(dwBuf);hprintf(LogFile, _T("   FileVer:    %d.%d.%d.%d\r\n"), dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);ver.GetProductVersion(dwBuf);hprintf(LogFile, _T("   ProdVer:    %d.%d.%d.%d\r\n"), dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]);ver.Release();hprintf(LogFile, _T("\r\n"));rc = true;}}// Handle any exceptions by continuing from this point.
    __except(EXCEPTION_EXECUTE_HANDLER){}return rc;
}///
// DumpModuleList
//
// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used
// to find all the blocks of address space that were reserved or committed,
// and ShowModuleInfo will display module information if they are code
// modules.
static void DumpModuleList(HANDLE LogFile)
{SYSTEM_INFO    SystemInfo;GetSystemInfo(&SystemInfo);const size_t PageSize = SystemInfo.dwPageSize;// Set NumPages to the number of pages in the 4GByte address space,// while being careful to avoid overflowing intsconst size_t NumPages = 4 * size_t(ONEG / PageSize);size_t pageNum = 0;void *LastAllocationBase = 0;int nModuleNo = 1;while (pageNum < NumPages){MEMORY_BASIC_INFORMATION MemInfo;if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo,sizeof(MemInfo))){if (MemInfo.RegionSize > 0){// Adjust the page number to skip over this block of memorypageNum += MemInfo.RegionSize / PageSize;if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase >LastAllocationBase){// Look for new blocks of committed memory, and try// recording their module names - this will fail// gracefully if they aren't code modulesLastAllocationBase = MemInfo.AllocationBase;if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo)){nModuleNo++;}}}elsepageNum += SIXTYFOURK / PageSize;}elsepageNum += SIXTYFOURK / PageSize;// If VirtualQuery fails we advance by 64K because that is the// granularity of address space doled out by VirtualAlloc()
    }
}///
// DumpSystemInformation
//
// Record information about the user's system, such as processor type, amount
// of memory, etc.
static void DumpSystemInformation(HANDLE LogFile)
{FILETIME CurrentTime;GetSystemTimeAsFileTime(&CurrentTime);TCHAR szTimeBuffer[100];FormatTime(szTimeBuffer, CurrentTime);hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer);TCHAR szModuleName[MAX_PATH*2];ZeroMemory(szModuleName, sizeof(szModuleName));if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)lstrcpy(szModuleName, _T("Unknown"));TCHAR szUserName[200];ZeroMemory(szUserName, sizeof(szUserName));DWORD UserNameSize = _countof(szUserName)-2;if (!GetUserName(szUserName, &UserNameSize))lstrcpy(szUserName, _T("Unknown"));hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName);// print out operating systemTCHAR szWinVer[50], szMajorMinorBuild[50];int nWinVer;GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild);hprintf(LogFile, _T("Operating system:  %s (%s).\r\n"), szWinVer, szMajorMinorBuild);SYSTEM_INFO    SystemInfo;GetSystemInfo(&SystemInfo);hprintf(LogFile, _T("%d processor(s), type %d.\r\n"),SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType);MEMORYSTATUS MemInfo;MemInfo.dwLength = sizeof(MemInfo);GlobalMemoryStatus(&MemInfo);// Print out info on memory, rounded up.hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad);hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys +ONEM - 1) / ONEM);hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"), (MemInfo.dwAvailPhys + ONEM - 1) / ONEM);hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile +ONEM - 1) / ONEM);hprintf(LogFile, _T("%d MBytes paging file free.\r\n"), (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM);hprintf(LogFile, _T("%d MBytes user address space.\r\n"), (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM);hprintf(LogFile, _T("%d MBytes user address space free.\r\n"), (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM);
}///
// GetExceptionDescription
//
// Translate the exception code into something human readable
static const TCHAR *GetExceptionDescription(DWORD ExceptionCode)
{struct ExceptionNames{DWORD    ExceptionCode;TCHAR *    ExceptionName;};#if 0  // from winnt.h
#define STATUS_WAIT_0                    ((DWORD   )0x00000000L)
#define STATUS_ABANDONED_WAIT_0          ((DWORD   )0x00000080L)
#define STATUS_USER_APC                  ((DWORD   )0x000000C0L)
#define STATUS_TIMEOUT                   ((DWORD   )0x00000102L)
#define STATUS_PENDING                   ((DWORD   )0x00000103L)
#define STATUS_SEGMENT_NOTIFICATION      ((DWORD   )0x40000005L)
#define STATUS_GUARD_PAGE_VIOLATION      ((DWORD   )0x80000001L)
#define STATUS_DATATYPE_MISALIGNMENT     ((DWORD   )0x80000002L)
#define STATUS_BREAKPOINT                ((DWORD   )0x80000003L)
#define STATUS_SINGLE_STEP               ((DWORD   )0x80000004L)
#define STATUS_ACCESS_VIOLATION          ((DWORD   )0xC0000005L)
#define STATUS_IN_PAGE_ERROR             ((DWORD   )0xC0000006L)
#define STATUS_INVALID_HANDLE            ((DWORD   )0xC0000008L)
#define STATUS_NO_MEMORY                 ((DWORD   )0xC0000017L)
#define STATUS_ILLEGAL_INSTRUCTION       ((DWORD   )0xC000001DL)
#define STATUS_NONCONTINUABLE_EXCEPTION  ((DWORD   )0xC0000025L)
#define STATUS_INVALID_DISPOSITION       ((DWORD   )0xC0000026L)
#define STATUS_ARRAY_BOUNDS_EXCEEDED     ((DWORD   )0xC000008CL)
#define STATUS_FLOAT_DENORMAL_OPERAND    ((DWORD   )0xC000008DL)
#define STATUS_FLOAT_DIVIDE_BY_ZERO      ((DWORD   )0xC000008EL)
#define STATUS_FLOAT_INEXACT_RESULT      ((DWORD   )0xC000008FL)
#define STATUS_FLOAT_INVALID_OPERATION   ((DWORD   )0xC0000090L)
#define STATUS_FLOAT_OVERFLOW            ((DWORD   )0xC0000091L)
#define STATUS_FLOAT_STACK_CHECK         ((DWORD   )0xC0000092L)
#define STATUS_FLOAT_UNDERFLOW           ((DWORD   )0xC0000093L)
#define STATUS_INTEGER_DIVIDE_BY_ZERO    ((DWORD   )0xC0000094L)
#define STATUS_INTEGER_OVERFLOW          ((DWORD   )0xC0000095L)
#define STATUS_PRIVILEGED_INSTRUCTION    ((DWORD   )0xC0000096L)
#define STATUS_STACK_OVERFLOW            ((DWORD   )0xC00000FDL)
#define STATUS_CONTROL_C_EXIT            ((DWORD   )0xC000013AL)
#define STATUS_FLOAT_MULTIPLE_FAULTS     ((DWORD   )0xC00002B4L)
#define STATUS_FLOAT_MULTIPLE_TRAPS      ((DWORD   )0xC00002B5L)
#define STATUS_ILLEGAL_VLM_REFERENCE     ((DWORD   )0xC00002C0L)
#endifExceptionNames ExceptionMap[] ={{0x40010005, _T("a Control-C")},{0x40010008, _T("a Control-Break")},{0x80000002, _T("a Datatype Misalignment")},{0x80000003, _T("a Breakpoint")},{0xc0000005, _T("an Access Violation")},{0xc0000006, _T("an In Page Error")},{0xc0000017, _T("a No Memory")},{0xc000001d, _T("an Illegal Instruction")},{0xc0000025, _T("a Noncontinuable Exception")},{0xc0000026, _T("an Invalid Disposition")},{0xc000008c, _T("a Array Bounds Exceeded")},{0xc000008d, _T("a Float Denormal Operand")},{0xc000008e, _T("a Float Divide by Zero")},{0xc000008f, _T("a Float Inexact Result")},{0xc0000090, _T("a Float Invalid Operation")},{0xc0000091, _T("a Float Overflow")},{0xc0000092, _T("a Float Stack Check")},{0xc0000093, _T("a Float Underflow")},{0xc0000094, _T("an Integer Divide by Zero")},{0xc0000095, _T("an Integer Overflow")},{0xc0000096, _T("a Privileged Instruction")},{0xc00000fD, _T("a Stack Overflow")},{0xc0000142, _T("a DLL Initialization Failed")},{0xe06d7363, _T("a Microsoft C++ Exception")},};for (int i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++)if (ExceptionCode == ExceptionMap[i].ExceptionCode)return ExceptionMap[i].ExceptionName;return _T("an Unknown exception type");
}///
// GetFilePart
static TCHAR * GetFilePart(LPCTSTR source)
{TCHAR *result = lstrrchr(source, _T('\\'));if (result)result++;elseresult = (TCHAR *)source;return result;
}///
// DumpStack
static void DumpStack(HANDLE LogFile, DWORD *pStack)
{hprintf(LogFile, _T("\r\n\r\nStack:\r\n"));__try{// Esp contains the bottom of the stack, or at least the bottom of// the currently used area.DWORD* pStackTop;__asm{// Load the top (highest address) of the stack from the// thread information block. It will be found there in// Win9x and Windows NT.mov    eax, fs:[4]mov pStackTop, eax}if (pStackTop > pStack + MaxStackDump)pStackTop = pStack + MaxStackDump;int Count = 0;DWORD* pStackStart = pStack;int nDwordsPrinted = 0;while (pStack + 1 <= pStackTop){if ((Count % StackColumns) == 0){pStackStart = pStack;nDwordsPrinted = 0;hprintf(LogFile, _T("0x%08x: "), pStack);}if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop){hprintf(LogFile, _T("%08x "), *pStack);nDwordsPrinted++;int n = nDwordsPrinted;while (n < 4){hprintf(LogFile, _T("         "));n++;}for (int i = 0; i < nDwordsPrinted; i++){DWORD dwStack = *pStackStart;for (int j = 0; j < 4; j++){char c = (char)(dwStack & 0xFF);if (c < 0x20 || c > 0x7E)c = '.';
#ifdef _UNICODEWCHAR w = (WCHAR)c;hprintf(LogFile, _T("%c"), w);
#elsehprintf(LogFile, _T("%c"), c);
#endifdwStack = dwStack >> 8;}pStackStart++;}hprintf(LogFile, _T("\r\n"));}else{hprintf(LogFile, _T("%08x "), *pStack);nDwordsPrinted++;}pStack++;}hprintf(LogFile, _T("\r\n"));}__except(EXCEPTION_EXECUTE_HANDLER){hprintf(LogFile, _T("Exception encountered during stack dump.\r\n"));}
}///
// DumpRegisters
static void DumpRegisters(HANDLE LogFile, PCONTEXT Context)
{// Print out the register values in an XP error window compatible format.hprintf(LogFile, _T("\r\n"));hprintf(LogFile, _T("Context:\r\n"));hprintf(LogFile, _T("EDI:    0x%08x  ESI: 0x%08x  EAX:   0x%08x\r\n"),Context->Edi, Context->Esi, Context->Eax);hprintf(LogFile, _T("EBX:    0x%08x  ECX: 0x%08x  EDX:   0x%08x\r\n"),Context->Ebx, Context->Ecx, Context->Edx);hprintf(LogFile, _T("EIP:    0x%08x  EBP: 0x%08x  SegCs: 0x%08x\r\n"),Context->Eip, Context->Ebp, Context->SegCs);hprintf(LogFile, _T("EFlags: 0x%08x  ESP: 0x%08x  SegSs: 0x%08x\r\n"),Context->EFlags, Context->Esp, Context->SegSs);
}///
///
//
// RecordExceptionInfo
//
///
///int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, LPCTSTR lpszMessage)
{static bool bFirstTime = true;if (!bFirstTime)    // Going recursive! That must mean this routine crashed!return EXCEPTION_CONTINUE_SEARCH;bFirstTime = false;// Create a filename to record the error information to.// Storing it in the executable directory works well.
TCHAR szModuleName[MAX_PATH*2];ZeroMemory(szModuleName, sizeof(szModuleName));if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0)lstrcpy(szModuleName, _T("Unknown"));TCHAR *pszFilePart = GetFilePart(szModuleName);// Extract the file name portion and remove it's file extensionTCHAR szFileName[MAX_PATH*2];lstrcpy(szFileName, pszFilePart);TCHAR *lastperiod = lstrrchr(szFileName, _T('.'));if (lastperiod)lastperiod[0] = 0;#ifdef XCRASHREPORT_WRITE_ERROR_LOG// Replace the executable filename with our error log file name
    lstrcpy(pszFilePart, XCRASHREPORT_ERROR_LOG_FILE);HANDLE hLogFile = CreateFile(szModuleName, GENERIC_WRITE, 0, 0,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);if (hLogFile == INVALID_HANDLE_VALUE){OutputDebugString(_T("Error creating exception report\r\n"));return EXCEPTION_CONTINUE_SEARCH;}// Append to the error logSetFilePointer(hLogFile, 0, 0, FILE_END);// Print out a blank line to separate this error log from any previous ones//hprintf(hLogFile, _T("\r\n"));
PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord;PCONTEXT          Context   = pExceptPtrs->ContextRecord;TCHAR szCrashModulePathName[MAX_PATH*2];ZeroMemory(szCrashModulePathName, sizeof(szCrashModulePathName));TCHAR *pszCrashModuleFileName = _T("Unknown");MEMORY_BASIC_INFORMATION MemInfo;// VirtualQuery can be used to get the allocation base associated with a// code address, which is the same as the ModuleHandle. This can be used// to get the filename of the module that the crash happened in.if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) &&(GetModuleFileName((HINSTANCE)MemInfo.AllocationBase,szCrashModulePathName,sizeof(szCrashModulePathName)-2) > 0)){pszCrashModuleFileName = GetFilePart(szCrashModulePathName);}// Print out the beginning of the error log in a Win95 error window// compatible format.hprintf(hLogFile, _T("%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n"),szFileName, GetExceptionDescription(Exception->ExceptionCode),Exception->ExceptionCode,pszCrashModuleFileName, Context->SegCs, Context->Eip);hprintf(hLogFile, _T("Exception handler called in %s.\r\n"), lpszMessage);DumpSystemInformation(hLogFile);// If the exception was an access violation, print out some additional// information, to the error log and the debugger.if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION &&Exception->NumberParameters >= 2){TCHAR szDebugMessage[1000];const TCHAR* readwrite = _T("Read from");if (Exception->ExceptionInformation[0])readwrite = _T("Write to");wsprintf(szDebugMessage, _T("%s location %08x caused an access violation.\r\n"),readwrite, Exception->ExceptionInformation[1]);#ifdef    _DEBUG// The Visual C++ debugger doesn't actually tell you whether a read// or a write caused the access violation, nor does it tell what// address was being read or written. So I fixed that.OutputDebugString(_T("Exception handler: "));OutputDebugString(szDebugMessage);
#endifhprintf(hLogFile, _T("%s"), szDebugMessage);}DumpRegisters(hLogFile, Context);// Print out the bytes of code at the instruction pointer. Since the// crash may have been caused by an instruction pointer that was bad,// this code needs to be wrapped in an exception handler, in case there// is no memory to read. If the dereferencing of code[] fails, the// exception handler will print '??'.hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n"));BYTE * code = (BYTE *)Context->Eip;for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++){__try{hprintf(hLogFile, _T("%02x "), code[codebyte]);}__except(EXCEPTION_EXECUTE_HANDLER){hprintf(hLogFile, _T("?? "));}}// Time to print part or all of the stack to the error log. This allows// us to figure out the call stack, parameters, local variables, etc.// Esp contains the bottom of the stack, or at least the bottom of// the currently used areaDWORD* pStack = (DWORD *)Context->Esp;DumpStack(hLogFile, pStack);DumpModuleList(hLogFile);hprintf(hLogFile, _T("\r\n===== [end of %s] =====\r\n"), XCRASHREPORT_ERROR_LOG_FILE);hflush(hLogFile);CloseHandle(hLogFile);
#endif //XCRASHREPORT_WRITE_ERROR_LOG///
    //// write minidump//
    ///

#ifdef XCRASHREPORT_WRITE_MINIDUMP// Replace the filename with our minidump file name
    lstrcpy(pszFilePart, XCRASHREPORT_MINI_DUMP_FILE);// Create the fileHANDLE hMiniDumpFile = CreateFile(szModuleName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,NULL);// Write the minidump to the fileif (hMiniDumpFile != INVALID_HANDLE_VALUE){DumpMiniDump(hMiniDumpFile, pExceptPtrs);// Close file
        CloseHandle(hMiniDumpFile);}#endif    // XCRASHREPORT_WRITE_MINIDUMPif (IsDebuggerPresent()){// let the debugger catch this -// return the magic value which tells Win32 that this handler didn't// actually handle the exception - so that things will proceed as per// normal.return EXCEPTION_CONTINUE_SEARCH;}else{///////  pop up our crash report app//
        ///// Replace the filename with our crash report exe file name
        lstrcpy(pszFilePart, XCRASHREPORT_CRASH_REPORT_APP);TCHAR szCommandLine[MAX_PATH];lstrcpy(szCommandLine, szModuleName);lstrcat(szCommandLine, _T(" \""));    // surround app name with quotesZeroMemory(szModuleName, sizeof(szModuleName));GetModuleFileName(0, szModuleName, _countof(szModuleName)-2);lstrcat(szCommandLine,     GetFilePart(szModuleName));lstrcat(szCommandLine, _T("\""));//MessageBox(NULL,szCommandLine,L"",MB_OK);
STARTUPINFO si;ZeroMemory(&si, sizeof(si));si.cb = sizeof(si);si.dwFlags = STARTF_USESHOWWINDOW;si.wShowWindow = SW_SHOW;PROCESS_INFORMATION pi;ZeroMemory(&pi, sizeof(pi));if (CreateProcess(NULL,                    // name of executable moduleszCommandLine,            // command line stringNULL,                    // process attributesNULL,                    // thread attributesFALSE,                    // handle inheritance option0,                        // creation flagsNULL,                    // new environment blockNULL,                    // current directory name&si,                    // startup information&pi))                    // process information
        {// XCrashReport.exe was successfully started, so// suppress the standard crash dialogreturn EXCEPTION_EXECUTE_HANDLER;}else{// XCrashReport.exe was not started - let// the standard crash dialog appearreturn EXCEPTION_CONTINUE_SEARCH;}}
}

GetWinVer.h

#ifndef GETWINVER_H
#define GETWINVER_H#define WUNKNOWNSTR    _T("unknown Windows version")#define W95STR            _T("Windows 95")
#define W95SP1STR        _T("Windows 95 SP1")
#define W95OSR2STR        _T("Windows 95 OSR2")
#define W98STR            _T("Windows 98")
#define W98SP1STR        _T("Windows 98 SP1")
#define W98SESTR        _T("Windows 98 SE")
#define WMESTR            _T("Windows ME")#define WNT351STR        _T("Windows NT 3.51")
#define WNT4STR            _T("Windows NT 4")
#define W2KSTR            _T("Windows 2000")
#define WXPSTR            _T("Windows XP")
#define W2003SERVERSTR    _T("Windows 2003 Server")#define WCESTR            _T("Windows CE")#define WUNKNOWN    0#define W9XFIRST    1
#define W95            1
#define W95SP1        2
#define W95OSR2        3
#define W98            4
#define W98SP1        5
#define W98SE        6
#define WME            7
#define W9XLAST        99#define WNTFIRST    101
#define WNT351        101
#define WNT4        102
#define W2K            103
#define WXP            104
#define W2003SERVER    105
#define WNTLAST        199#define WCEFIRST    201
#define WCE            201
#define WCELAST        299BOOL GetWinVer(LPTSTR pszVersion, int *nVersion, LPTSTR pszMajorMinorBuild);#endif //GETWINVER_H

GetWinVer.cpp

#include "stdafx.h"
#include "tchar.h"
#include "GetWinVer.h"#pragma warning(disable : 4996)
// from winbase.h
#ifndef VER_PLATFORM_WIN32s
#define VER_PLATFORM_WIN32s             0
#endif
#ifndef VER_PLATFORM_WIN32_WINDOWS
#define VER_PLATFORM_WIN32_WINDOWS      1
#endif
#ifndef VER_PLATFORM_WIN32_NT
#define VER_PLATFORM_WIN32_NT           2
#endif
#ifndef VER_PLATFORM_WIN32_CE
#define VER_PLATFORM_WIN32_CE           3
#endif// GetWinVer
BOOL GetWinVer(LPTSTR pszVersion, int *nVersion, LPTSTR pszMajorMinorBuild)
{if (!pszVersion || !nVersion || !pszMajorMinorBuild)return FALSE;lstrcpy(pszVersion, WUNKNOWNSTR);*nVersion = WUNKNOWN;OSVERSIONINFO osinfo;osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);if (!GetVersionEx(&osinfo))return FALSE;DWORD dwPlatformId   = osinfo.dwPlatformId;DWORD dwMinorVersion = osinfo.dwMinorVersion;DWORD dwMajorVersion = osinfo.dwMajorVersion;DWORD dwBuildNumber  = osinfo.dwBuildNumber & 0xFFFF;    // Win 95 needs this
wsprintf(pszMajorMinorBuild, _T("%u.%u.%u"), dwMajorVersion, dwMinorVersion, dwBuildNumber);if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMajorVersion == 4)){if ((dwMinorVersion < 10) && (dwBuildNumber == 950)){lstrcpy(pszVersion, W95STR);*nVersion = W95;}else if ((dwMinorVersion < 10) && ((dwBuildNumber > 950) && (dwBuildNumber <= 1080))){lstrcpy(pszVersion, W95SP1STR);*nVersion = W95SP1;}else if ((dwMinorVersion < 10) && (dwBuildNumber > 1080)){lstrcpy(pszVersion, W95OSR2STR);*nVersion = W95OSR2;}else if ((dwMinorVersion == 10) && (dwBuildNumber == 1998)){lstrcpy(pszVersion, W98STR);*nVersion = W98;}else if ((dwMinorVersion == 10) && ((dwBuildNumber > 1998) && (dwBuildNumber < 2183))){lstrcpy(pszVersion, W98SP1STR);*nVersion = W98SP1;}else if ((dwMinorVersion == 10) && (dwBuildNumber >= 2183)){lstrcpy(pszVersion, W98SESTR);*nVersion = W98SE;}else if (dwMinorVersion == 90){lstrcpy(pszVersion, WMESTR);*nVersion = WME;}}else if (dwPlatformId == VER_PLATFORM_WIN32_NT){if ((dwMajorVersion == 3) && (dwMinorVersion == 51)){lstrcpy(pszVersion, WNT351STR);*nVersion = WNT351;}else if ((dwMajorVersion == 4) && (dwMinorVersion == 0)){lstrcpy(pszVersion, WNT4STR);*nVersion = WNT4;}else if ((dwMajorVersion == 5) && (dwMinorVersion == 0)){lstrcpy(pszVersion, W2KSTR);*nVersion = W2K;}else if ((dwMajorVersion == 5) && (dwMinorVersion == 1)){lstrcpy(pszVersion, WXPSTR);*nVersion = WXP;}else if ((dwMajorVersion == 5) && (dwMinorVersion == 2)){lstrcpy(pszVersion, W2003SERVERSTR);*nVersion = W2003SERVER;}}else if (dwPlatformId == VER_PLATFORM_WIN32_CE){lstrcpy(pszVersion, WCESTR);*nVersion = WCE;}return TRUE;
}

MiniVersion.h

#ifndef MINIVERSION_H
#define MINIVERSION_Hclass CMiniVersion
{
// constructors
public:CMiniVersion(LPCTSTR lpszPath = NULL);BOOL Init();void Release();// operations
public:// attributes
public:// fixed infoBOOL GetFileVersion(WORD *pwVersion);BOOL GetProductVersion(WORD* pwVersion);BOOL GetFileFlags(DWORD& rdwFlags);BOOL GetFileOS(DWORD& rdwOS);BOOL GetFileType(DWORD& rdwType);BOOL GetFileSubtype(DWORD& rdwType);    // string infoBOOL GetCompanyName(LPTSTR lpszCompanyName, int nSize);BOOL GetFileDescription(LPTSTR lpszFileDescription, int nSize);BOOL GetProductName(LPTSTR lpszProductName, int nSize);// implementation
protected:BOOL GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo);BOOL GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszValue);BYTE*        m_pData;DWORD        m_dwHandle;WORD        m_wFileVersion[4];WORD        m_wProductVersion[4];DWORD        m_dwFileFlags;DWORD        m_dwFileOS;DWORD        m_dwFileType;DWORD        m_dwFileSubtype;TCHAR        m_szPath[MAX_PATH*2];TCHAR        m_szCompanyName[MAX_PATH*2];TCHAR        m_szProductName[MAX_PATH*2];TCHAR        m_szFileDescription[MAX_PATH*2];
};#endif

MiniVersion.cpp

#include "stdafx.h"
#include "MiniVersion.h"#pragma message("automatic link to VERSION.LIB")
#pragma comment(lib, "version.lib")///
// ctor
CMiniVersion::CMiniVersion(LPCTSTR lpszPath)
{ZeroMemory(m_szPath, sizeof(m_szPath));if (lpszPath && lpszPath[0] != 0){lstrcpyn(m_szPath, lpszPath, sizeof(m_szPath)-1);}else{}m_pData = NULL;m_dwHandle = 0;for (int i = 0; i < 4; i++){m_wFileVersion[i] = 0;m_wProductVersion[i] = 0;}m_dwFileFlags = 0;m_dwFileOS = 0;m_dwFileType = 0;m_dwFileSubtype = 0;ZeroMemory(m_szCompanyName, sizeof(m_szCompanyName));ZeroMemory(m_szProductName, sizeof(m_szProductName));ZeroMemory(m_szFileDescription, sizeof(m_szFileDescription));Init();
}///
// Init
BOOL CMiniVersion::Init()
{DWORD dwHandle;DWORD dwSize;BOOL rc;dwSize = ::GetFileVersionInfoSize(m_szPath, &dwHandle);if (dwSize == 0)return FALSE;m_pData = new BYTE [dwSize + 1];    ZeroMemory(m_pData, dwSize+1);rc = ::GetFileVersionInfo(m_szPath, dwHandle, dwSize, m_pData);if (!rc)return FALSE;// get fixed info
VS_FIXEDFILEINFO FixedInfo;if (GetFixedInfo(FixedInfo)){m_wFileVersion[0] = HIWORD(FixedInfo.dwFileVersionMS);m_wFileVersion[1] = LOWORD(FixedInfo.dwFileVersionMS);m_wFileVersion[2] = HIWORD(FixedInfo.dwFileVersionLS);m_wFileVersion[3] = LOWORD(FixedInfo.dwFileVersionLS);m_wProductVersion[0] = HIWORD(FixedInfo.dwProductVersionMS);m_wProductVersion[1] = LOWORD(FixedInfo.dwProductVersionMS);m_wProductVersion[2] = HIWORD(FixedInfo.dwProductVersionLS);m_wProductVersion[3] = LOWORD(FixedInfo.dwProductVersionLS);m_dwFileFlags   = FixedInfo.dwFileFlags;m_dwFileOS      = FixedInfo.dwFileOS;m_dwFileType    = FixedInfo.dwFileType;m_dwFileSubtype = FixedInfo.dwFileSubtype;}elsereturn FALSE;// get string info
GetStringInfo(_T("CompanyName"),     m_szCompanyName);GetStringInfo(_T("FileDescription"), m_szFileDescription);GetStringInfo(_T("ProductName"),     m_szProductName);return TRUE;
}///
// Release
void CMiniVersion::Release()
{// do this manually, because we can't use objects requiring// a dtor within an exception handlerif (m_pData)delete [] m_pData;m_pData = NULL;
}///
// GetFileVersion
BOOL CMiniVersion::GetFileVersion(WORD * pwVersion)
{for (int i = 0; i < 4; i++)*pwVersion++ = m_wFileVersion[i];return TRUE;
}                          ///
// GetProductVersion
BOOL CMiniVersion::GetProductVersion(WORD * pwVersion)
{for (int i = 0; i < 4; i++)*pwVersion++ = m_wProductVersion[i];return TRUE;
}///
// GetFileFlags
BOOL CMiniVersion::GetFileFlags(DWORD& rdwFlags)
{rdwFlags = m_dwFileFlags;return TRUE;
}///
// GetFileOS
BOOL CMiniVersion::GetFileOS(DWORD& rdwOS)
{rdwOS = m_dwFileOS;return TRUE;
}///
// GetFileType
BOOL CMiniVersion::GetFileType(DWORD& rdwType)
{rdwType = m_dwFileType;return TRUE;
}///
// GetFileSubtype
BOOL CMiniVersion::GetFileSubtype(DWORD& rdwType)
{rdwType = m_dwFileSubtype;return TRUE;
}///
// GetCompanyName
BOOL CMiniVersion::GetCompanyName(LPTSTR lpszCompanyName, int nSize)
{if (!lpszCompanyName)return FALSE;ZeroMemory(lpszCompanyName, nSize);lstrcpyn(lpszCompanyName, m_szCompanyName, nSize-1);return TRUE;
}///
// GetFileDescription
BOOL CMiniVersion::GetFileDescription(LPTSTR lpszFileDescription, int nSize)
{if (!lpszFileDescription)return FALSE;ZeroMemory(lpszFileDescription, nSize);lstrcpyn(lpszFileDescription, m_szFileDescription, nSize-1);return TRUE;
}///
// GetProductName
BOOL CMiniVersion::GetProductName(LPTSTR lpszProductName, int nSize)
{if (!lpszProductName)return FALSE;ZeroMemory(lpszProductName, nSize);lstrcpyn(lpszProductName, m_szProductName, nSize-1);return TRUE;
}///
///
//
// protected methods
//
///
//////
// GetFixedInfo
BOOL CMiniVersion::GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo)
{BOOL rc;UINT nLength;VS_FIXEDFILEINFO *pFixedInfo = NULL;if (!m_pData)return FALSE;if (m_pData)rc = ::VerQueryValue(m_pData, _T("\\"), (void **) &pFixedInfo, &nLength);elserc = FALSE;if (rc)memcpy (&rFixedInfo, pFixedInfo, sizeof (VS_FIXEDFILEINFO));    return rc;
}///
// GetStringInfo
BOOL CMiniVersion::GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszReturnValue)
{BOOL rc;DWORD *pdwTranslation;UINT nLength;LPTSTR lpszValue;if (m_pData == NULL)return FALSE;if (!lpszReturnValue)return FALSE;if (!lpszKey)return FALSE;*lpszReturnValue = 0;rc = ::VerQueryValue(m_pData, _T("\\VarFileInfo\\Translation"), (void**) &pdwTranslation, &nLength);if (!rc)return FALSE;TCHAR szKey[2000];wsprintf(szKey, _T("\\StringFileInfo\\%04x%04x\\%s"),LOWORD (*pdwTranslation), HIWORD (*pdwTranslation),lpszKey);rc = ::VerQueryValue(m_pData, szKey, (void**) &lpszValue, &nLength);if (!rc)return FALSE;lstrcpy(lpszReturnValue, lpszValue);return TRUE;
}

第二步:在main函数处加入try catch 捕获异常

// Duilib_Login.cpp : 定义应用程序的入口点。
//
#include "CefBrowserApp.h"
#include "MainFrameWnd.h"
#include "ExceptionHandler.h"//#define CEF_USE_SANDBOX 560int Mymain(HINSTANCE hInstance)
{CPaintManagerUI::SetInstance(hInstance);CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath());HRESULT Hr = ::CoInitialize(NULL);if (FAILED(Hr)) return 0;/***************************************begin初始化cef*******************************************/void* sandbox_info = NULL;
#if defined(CEF_USE_SANDBOX)CefScopedSandboxInfo scoped_sandbox;sandbox_info = scoped_sandbox.sandbox_info();
#endifCefMainArgs main_args(hInstance);CefRefPtr<CCefBrowserApp> spApp(new CCefBrowserApp);// Execute the secondary process, if any.int exit_code = CefExecuteProcess(main_args, spApp.get(), sandbox_info);if (exit_code >= 0)return exit_code;CefRefPtr<CefCommandLine> command_line;command_line = CefCommandLine::CreateCommandLine();command_line->AppendSwitch("no-proxy-server");//加载慢,关闭代理试试//command_line->AppendSwitch("--disable-web-security");//关闭同源策略//command_line->AppendSwitchWithValue("ppapi-flash-version", "28.0.0.137");//PepperFlash\manifest.json中的version//command_line->AppendSwitchWithValue("ppapi-flash-path", "PepperFlash\\pepflashplayer.dll");
CefSettings cSettings;const char* loc = "zh-CN";cSettings.no_sandbox = true;cSettings.multi_threaded_message_loop = true;//cSettings.single_process = false;cSettings.log_severity = LOGSEVERITY_DISABLE;//设置日志级别,解决安装启动佰卓数安后桌面出现一个debug.log文件(调试阶段可以去掉)CefString(&cSettings.locale).FromASCII(loc);cef_string_from_ascii(loc, strlen(loc), &cSettings.locale);// Execute the secondary process, if any.CefInitialize(main_args, cSettings, spApp.get(), sandbox_info);/***************************************结束初始化cef*******************************************/CMainFrameWnd  Main;Main.Create(NULL, _T("MainFrameWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES);Main.CenterWindow();Main.ShowModal();//if (!cSettings.multi_threaded_message_loop) {//    // Run the CEF message loop. This function will block until the application//    // recieves a WM_QUIT message.//    CefRunMessageLoop();//}//else {//    DuiLib::CPaintManagerUI::MessageLoop();//}//CefShutdown();
::CoUninitialize();return 0;
}int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR    lpCmdLine, int       nCmdShow)
{__try{Mymain(hInstance);}__except (RecordExceptionInfo(GetExceptionInformation(),_T("DuilibDemo.cpp - _tWinMain"))){// Do nothing here - RecordExceptionInfo() has already done// everything that is needed. Actually this code won't even// get called unless you return EXCEPTION_EXECUTE_HANDLER from// the __except clause.
    }return 0;
}

手动让程序崩溃:

*(int*)0 = 5;

BugReport源码:点击下载

给自己的程序添加BugReport相关推荐

  1. linux下 为自己编写的程序 添加tab自动补全 功能

    linux下 为自己编写的程序 添加tab自动补全功能 入门 complete 在我的tmp下随便写了一个a.sh, 为他补全 edit /etc/bash_completion.d/foo _foo ...

  2. firebase 推送_如何使用Firebase向Web应用程序添加推送通知?

    firebase 推送 by Leonardo Cardoso 由莱昂纳多·卡多佐(Leonardo Cardoso) 如何使用Firebase向Web应用程序添加推送通知? (How to add ...

  3. 为.NET部署应用程序添加个卸载程序

    地球人都知道.NET自带的部署程序用起来很方便很简单,但.NET部署程序是没有卸载文件. 下面的方法可以很简单的为你的程序添加1个卸载文件,.NET是利用windows Installer来部署程序的 ...

  4. 关于把程序添加到打开方式的解决办法

    对于如果想用其他某个程序打开某种文件,找到程序在点击确定之后,回到打开方式对话框中,里面没有你刚才选择的程序,无法将你要的程序添加到打开方式里面,只能自己单独×××该程序,在程序里面再打开你要用的文件 ...

  5. 给你的Flex程序添加深链接和分析-其实比你想象的要简单

    Adobe Flash Builder 4 简体中文正式版 Windows版点击下载:http://g.csdn.net/5134151 Adobe Flash Builder 4 简体中文正式版 M ...

  6. 【OpenGL】二十四、OpenGL 纹理贴图 ( 读取文件内容 | 桌面程序添加控制台窗口 | ‘fopen‘: This function may be unsafe 错误处理 )

    文章目录 一.文件读取 二.报错处理 ( 'fopen': This function or variable may be unsafe. ) 三.桌面程序添加控制台窗口 四.相关资源 一.文件读取 ...

  7. 为ios 应用程序添加图标和添加名字

    为应用程序添加图标方法: 1.找到ios工程文件对应的 ***.plist文件.查看是否有 iconfile 或者iconfiles 键值,如果有,请看下一步.如果没有,则随便选中一个键值,然后点击 ...

  8. qt修改程序图标名称_解决Qt应用程序添加icon图标,修改窗口图标以及添加系统托盘问题...

    一.Qt应用程序添加icon图标的方法: 首先,我们需要先准备两个文件,一个是icon图标,另一个是rc文件,我分别命名为"myApp.rc"和"soft.ico&quo ...

  9. 微信小程序——添加按钮格式

    微信小程序--添加按钮 步骤一:在wxml文件中添加按钮代码(图后提供可复制代码) <button class = "button_cat">炸炸呼呼</butt ...

最新文章

  1. Python 列表、字典、元组的一些小技巧
  2. Linux下如何执行Shell脚本
  3. 微型计算机中的外存储器 现在普遍采用,2010澳门特别行政区全国计算机等级考试二级笔试试卷VB最新考试试题库(完整版)...
  4. linux下C code block环境,linux 使用 codeblocks
  5. 云计算机教室怎么布线,云和县实验小学计算机教室综合布线及计算机安装预算方案.doc...
  6. 去贵阳参观大数据到哪参观_您必须参观的四个世界
  7. 【转】1.B(译).NET4.X并行任务Task需要释放吗?
  8. 关于数据库中的锁,你不知道的是...
  9. c++引用另一个类的方法_利用CVE20191132:Windows内核中的另一个NULL指针取消引用...
  10. 基于Bmob从零开始写一个博客小程序
  11. Linux上 MongoDB 实现远程连接并设置账号密码
  12. JavaWeb——jsp原理
  13. ExtJS 在grid中想要取消checkbox选中的方法
  14. 设计模式学习(四)工厂模式
  15. 解决centos 7 打开php文件直接下载问题
  16. android屏幕大小字体大小,Android字体大小自适应不同分辨率的解决办法
  17. 解析域名,获得公网地址
  18. 现在的学生能把老师活活气死
  19. win10输入法不能切换_怎么解决win10系统微软输入法打不出汉字
  20. 详解HTML的相对路径写法

热门文章

  1. Centos-6.7下_Oracle 11gR2静默详细安装过程及排错
  2. imuxsock lost 353 messages from pid 20261 due to rate-limiting 解决办法
  3. PostgreSql 功能和操作
  4. 使用Response.Redirect 两种方法打开一个新窗口
  5. 如何设置VSS源代码管理工具使用KDiff3
  6. 服务器中使用Com组建处理Excel 常见问题
  7. iframe解决跨域ajax请求的方法
  8. 使用扩展的 CodeIgniter 框架实现 RESTful 框架
  9. maven详解之生命周期与插件
  10. 【原创+整理】简述何为调用约定,函数导出名以及extern C