|
Introduction
In Part 3, I said that I thought that article was the last one about exception handling and crash reporting. But I had ignored one important question: how are you going to get your customers to send you the crash dumps and error logs? This really worried me, until I saw the wonderful solution here that Mike Carruth came up with. His idea was to replace the standard crash dialog that looks like this:
with one that prompts you to email the crash files to the software vendor. The really brilliant thing about Carruth's approach is that it allows the customer to review in detail what is being sent, and to choose what files are sent.
End of the Grail?
OK, now you are thinking, "Good! We can just use what Carruth has done." For me, I had some problems with his implementation.
Problem #1: It uses WTL. Not a big deal, but it is one more thing to configure and deal with, and I am very reluctant to tweak some of the older MFC apps I maintain.
Problem #2: The replacement crash dialog runs in the same process as the crashed app. This is a big concern, because you really want to do as little as possible in the exception handler, and GUI dialogs cross the line.
Problem #3: It uses zlib.dll to zip the crash files. This is one more file to add to the installation, and I get twitchy when I think about dealing with that.
Problem #4: There is extra stuff in the implementation that was not essential for me. For example, output of the crash info to XML using Microsoft's MSXML. While I think this is a very interesting use of XML, I wanted to keep the exception handler (and the code it pulls in) to a very small footprint.
Preparation
My first requirement was to run the crash dialog as a separate app, to avoid any problems with the app that just crashed; my second was to use only MFC.
Running as a separate app was no problem. Since the exception handler was doing all the work of creating the crash files, all the new crash dialog app had to do was to find them, zip them, and email the zip. To make the crash dialog app (I am calling it XCrashReport from now on) as independent as possible, the last thing the exception handler does is to invoke XCrashReport with the name of the crashed app on the command line. By default, I decided that the crashed app, the crash files, and XCrashReport must all reside in the same directory. This design decision could easily be changed, but so far it seems to work well for my needs.
Next came the MFC stuff. I knew there would be two dialogs - the main window, and then a dialog that showed the details of the files being sent, and allowed the user to select which ones to send. For the last, I saw that my XListCtrl would be perfect, using checkboxes to allow selection. I could also use my new XHyperLink and XColorStatic controls. And I could use my XZip code to zip up the crash files.
I would like to thank Grant McDorman for his enhancements to Mike Carruth's code, which you can find here. In particular, McDorman added the ability to dump a section of the registry to a text file, which can then be added to the error report.
Theory Into Practice
After modifying the ExceptionHandler.cpp code once more, to start up XCrashReport.exe, the new crash report dialog will be displayed if it is found in the same directory as the crashed app:
The user can then click on the click here link to review the contents of the error report before it is sent. This shows the ERRORLOG.TXT file being displayed:
This shows the CRASH.DMP file being displayed:
This shows the REGISTRY.TXT file being displayed:
Summary
This is now a complete solution to catching application exceptions, generating a detailed error report, and prompting the user to send the report to you. Thanks to the work of Bruce Dawson, Mike Carruth, and Grant McDorman, XCrashReport offers a minimal footprint along with a user-friendly crash dialog.
Implementation Notes
Dismissing the Exception
At the end of the RecordExceptionInfo() function in ExceptionHandler.cpp, there is code that will detect whether the app is running under a debugger, and dismiss the exception accordingly:
- If running under a debugger, the handler returns
EXCEPTION_CONTINUE_SEARCH. This tells Win32 that this handler didn't actually handle the exception, so that things will proceed as per normal, and the debugger will catch the exception.
- If not running under a debugger, the handler attempts to run XCrashReport.exe. If this is successful, the handler returns
EXCEPTION_EXECUTE_HANDLER, which suppresses the standard crash dialog. If not successful, the handler returns EXCEPTION_CONTINUE_SEARCH, which again lets things proceed as per normal (the standard crash dialog will pop up).
Customizable Files
There are some files that you may want to customize for your own application and company:
- CrashFileNames.h - this contains the names of the error output files:
#define XCRASHREPORT_MINI_DUMP_FILE _T("CRASH.DMP")
#define XCRASHREPORT_ERROR_LOG_FILE _T("ERRORLOG.TXT")
#define XCRASHREPORT_REGISTRY_DUMP_FILE _T("REGISTRY.TXT")
#define XCRASHREPORT_CRASH_REPORT_APP _T("XCrashReport.exe")
- EmailDefines.h - this contains strings for email headers:
#define XCRASHREPORT_SEND_TO_NAME _T("Software Support")
#define XCRASHREPORT_SEND_TO_ADDRESS _T("support@softwarevendor.com")
- IniDefines.h - this contains defines for the XCrashReport.ini file:
#define INI_FILE_NAME _T("XCrashReport.ini")
#define INI_FILE_SECTION _T("FilesToAdd")
#define INI_FILE_TEMPLATE _T("File%03d")
#define INI_REG_SECTION _T("RegistryToAdd")
#define INI_REG_TEMPLATE _T("Registry%03d")
#define MAX_INI_ITEMS 999
- RegistryDefines.h - this contains the define which specifies whether any registry sections will be dumped, and also the default registry section:
#define XCRASHREPORT_DUMP_REGISTRY
#define XCRASHREPORT_REGISTRY_KEY \
_T("HKCU\\Software\\CodeProject\\XCrashReportTest")
- XCrashReport.ini - this ini file is read by XCrashReport.exe and contains the registry sections to be dumped to file and the files that are to be included in the error report zip. It is not necessary to include the registry files in the [FilesToAdd] section.
[RegistryToAdd]
;Registry001=HKCU\Software\CodeProject\XCrashReportTest,Main reg key
;Registry002=HKCU\Software\CodeProject\XCrashReportTest\Program,new reg key
[FilesToAdd]
;File001=CRASH.DMP,Crash Dump,DMP File
;File002=ERRORLOG.TXT,Crash log,Text Document
Using XCrashReport in Debug Mode
To simplify working on XCrashReport, in debug builds XCrashReport will simulate receiving the string "XCrashReportTest.exe" on the command line.
How To Use
- Set up your release build to generate debug symbols (pdb)
- Include these files in your project:
- Recompile the entire project.
- In your VC++ project, go to Project | Settings. Make sure the Release configuration is selected in the Settings For combobox on the left. Go to the C/C++ tab, select the General category, and select Program Database in the Debug Info combobox. This tells the compiler to generate debug information.
- Go to the Link tab and check Generate debug info. This tells the linker to collate debug information into .pdb files. The linker also puts the name of the .pdb file in the executable, so the debugger can find it.
- On the same Link tab, enter /OPT:REF at the end of the Project Options list. This tells the linker to eliminate functions and/or data that are never referenced. This is the usually the default for release builds, but it gets turned off when you tell the linker to generate debug information. Failing to specify /OPT:REF will cause your executables and DLLs to get 10-20% larger.
- ExceptionAttacher.cpp
- ExceptionHandler.cpp - should be set to Not using precompiled headers on the C/C++ tab (Precompiled Headers).
- ExceptionHandler.h
- GetWinVer.cpp
- GetWinVer.h
- MiniVersion.cpp
- MiniVersion.h
- CrashFileNames.h
Tuck away the exe and pdb files. Do not ship the pdb file to customers - this is both unnecessary and may be helpful to someone wanting to reverse-engineer your program.
A Note on dbghelp.dll
The same download for WinDbg also includes the latest dbghelp.dll. You can download it here. In this download, the redist.txt file states that dbghelp.dll version 6.2.13.1 is redistributable.
The download also includes the latest dbghelp.lib and dbghelp.h.
Known Limitations
- XCrashReport.exe must reside in the same directory as the application's exe.
- The Unicode implementation is not complete or tested.
- The amount of text that can be entered in the user's comments editbox is limited to 64 KB characters.
- The amount of the file that will be displayed in the Error Report Contents dialog is limited to 64 KB.
- There must be a default email client installed in order for the error report to be sent by email. This has been tested with Outlook, Outlook Express, and Eudora. If there is no default email client installed, the user is requested to email the zip file, but there is no attempt to configure MAPI or create an email session.
Frequently Asked Questions
- Can I use ExceptionHandler.cpp in non-MFC apps?
Yes! It has been implemented to compile with any Win32 program. See Part 1 for an example.
- How can I add some additional files to the error report?
How can I add some additional registry sections to the error report? Use the optional XCrashReport.ini file. (There is a sample XCrashReport.ini file included.) When you specify files in the XCrashReport.ini file, the default files (CRASH.DMP and ERRORLOG.TXT) are not included - you must add them to the XCrashReport.ini file if you want to include them. Also, When you specify registry sections in XCrashReport.ini file, the default registry section (defined by XCRASHREPORT_REGISTRY_KEY) is not included - you must add it to the XCrashReport.ini file if you want to include it. On the target system, the XCrashReport.ini file must be placed in the exe directory.
Here is an example XCrashReport.ini file:
;
; sample XCrashReport.ini file
;
[FilesToAdd]
File001=CRASH.DMP,Crash Dump,DMP File
File002=ERRORLOG.TXT,Crash log,Text Document
File003=BozoSoft.db,Database File,DB File
[RegistryToAdd]
Registry001=HKCU\Software\BozoSoft\BozoApp1,Main reg key
Registry002=HKCU\Software\BozoSoft\BozoApp2\Program,another reg key
There is no limit on the size of the file that may be added, although internally only the first 64 KB of the file will be displayed on the Error Report Contents dialog.
- When I try to include ExceptionHandler.cpp in my MFC project, I get the compiler error
ExceptionHandler.cpp(823) : fatal error C1010: unexpected end of file while looking for precompiled header directive. How can I fix this? When using ExceptionHandler in project that uses precompiled headers, you must change C/C++ Precompiled Headers settings to Not using precompiled headers for ExceptionHandler.cpp. Be sure to do this for All Configurations.
- I don't need registry dumps. Can I exclude them?
Yes. Comment out the following line in RegistryDefines.h:
#define XCRASHREPORT_DUMP_REGISTRY
This file is included in the XCrashReport.exe project.
- I don't need minidumps. Can I exclude them?
Yes. Comment out the following line at the top of ExceptionHandler.cpp:
#define XCRASHREPORT_WRITE_MINIDUMP
This file is one of the files that you include in your app's project.
- I want to rename the default error files that are created. How can I do this?
You can edit the file CrashFileNames.h:
#ifndef CRASHFILENAMES_H
#define CRASHFILENAMES_H
#define XCRASHREPORT_MINI_DUMP_FILE _T("CRASH.DMP")
#define XCRASHREPORT_ERROR_LOG_FILE _T("ERRORLOG.TXT")
#define XCRASHREPORT_REGISTRY_DUMP_FILE _T("REGISTRY.TXT")
#define XCRASHREPORT_CRASH_REPORT_APP _T("XCrashReport.exe")
#endif
This file is one of the files that you include in your app's project, and it is also used in XCrashReport.exe. Both will need to be recompiled.
- How do I change the registry section that gets dumped?
You can use the XCrashReport.ini file (see above), or you can edit the file RegistryDefines.h:
#ifndef REGISTRYDEFINES_H
#define REGISTRYDEFINES_H
#define XCRASHREPORT_DUMP_REGISTRY
#define XCRASHREPORT_REGISTRY_KEY \
_T("HKCU\\Software\\CodeProject\\XCrashReportTest")
#endif
This file is used in XCrashReport.exe.
- Can we use XCrashReport in our (shareware/commercial) app?
Yes, you can use XCrashReport without charge or license fee. It would be nice to acknowledge my Copyright in your About box or splash screen, but this is up to you.
Links
This is a cumulative list of all the links that have been mentioned in Parts 1 - 4.
Revision History
Version 1.1 - 2003 October 19
Usage
This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 162 (Total in Forum: 162) (Refresh) | FirstPrevNext |
|
 |
|
|
Hello there
With this environment, I managed to get a clean build, but wen I try to run and click any of the buttons, the MS message comes up, BEFORE the nice dialog I expected, and the CRT takes over...
Any way to get around this?
Cheers Alex
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
The links to the previous parts of this article series are become broken. Probably the portal engine changed under the article, so replacing "*.asp" links with "*.aspx" solves the problem. This is also true for the previous parts of the series which are also refer to the other previous and next parts.
Apart from this, I really respect and appreciate this whole article series, rate to Excellent, great! This is professional! These techniques are _essential_ for _real_, _heavy_weight_ wide spreaded softwares! Thank you Hans for these articles!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
First of all, a great piece of work. I have been using your project for several years now. It got my 5.
mrkmmix posted some rhetoric about Stack Overflow in 2005 and he didn't get any replies.
I am having big problems with stack overflow in some 3rd party libraries and it would be great to get the crash dumps following these. I guess the problem is that if the stack has overflown then anything in the exception handler that uses stack memory throws another exception and the application then vanishes without a trace.
I completely agree with Hans' approach of keeping the code in the exception handler to a minimum so that it has the best chance of completing its job following a serious error. It appears that the exception handler doesn't always have enough space to complete this small amount of work following a stack overflow.
Has anyone managed to work around this type of problem? I don't want to recover from the stack overflow exception... just need the crash dump. Is there any way to squeeze enough extra life from the stack to get the crash dump and report written out to disk?
Thanks for any suggestions.
|
| Sign In·View Thread·PermaLink | 4.67/5 (2 votes) |
|
|
|
 |
|
|
Hi, first of all thanks for sharing your work. I have two questions about it. How can I use this with MFC Extensions DLL ? If it's not possible how can I debug DLL (using map files ?). I added an error in my code and I was able to find it using dump file and your tutorial. Now I got another error (this time I didn't insert it intentionally) but when I type .ecxr I got the following line
Module load completed but symbols could not be loaded
And I couldn't get to the error. What does this mean ?
Thanks in advance,
Stefano
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Excellent articles!
I've got a problem using XCrashReport to send a report via Outlook and Exchange Server. The email is opened up correctly for sending, but if I click "send" I get a delivery failure report from Outlook. If I double-click on the email address of the recipient in the To box, it shows that Outlook has set the "Email Type" to the mail recipient's email address, not to "SMTP". How can I fix this please?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hello
I have used your technique to debug my program and it was working well couple of years back. But did not use it for last 2 years. Now it does not work anymore. I have my exectuable and .pdb file and crash dump file all in the same folder. When i load the crash.dmp file in WinDbg it says Symbol file could not be found.
I am using the latest version of WinDbg version no 6.8.0004.0. My compiler is Visual studio 6.0.
I just created a test crash in my program and windbg fails to report. Instead it says
*** ERROR: Symbol file could not be found. Defaulted to export symbols for mfc42.dll -
But your test program still works fine.
Please help me fix this.
Shashi
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Excellent articles and extremely useful code. Thanks.
Anyone using these in Vista applications? Users are not allowed to write in the Program Files\AppName\ folder typically the place where the executable is kept. One could choose anothe standard location such as 'My Documents' or 'App Data' - or some how pass on the folder name to the exception handler function?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Yes, I modified the code to work with Vista and privileged XP. I used the windows temp folder instead of the app folder. Unfortunately there were a quite few places to change and the code was not designed with this in mind (mostly due to reusing the module path to locate other files).
This is all a quick hack. It would be nice for someone to mod the original code, add a path passed in so any path can be used instead of windows temp, and uploaded for all. But in the spirit of sharing, here is what I did:
--- In your application stub ---
In ExceptionHandler.cpp In RecordExceptionInfo() after TCHAR *pszFilePart = GetFilePart(szModuleName); add TCHAR szTempPathName[MAX_PATH*2]; GetTempPath(MAX_PATH*2, szTempPathName); TCHAR* pszTempFilePath = szTempPathName + strlen(szTempPathName);
lstrcpy(pszFilePart, XCRASHREPORT_ERROR_LOG_FILE); becomes lstrcpy(pszTempFilePath, XCRASHREPORT_ERROR_LOG_FILE);
HANDLE hLogFile = CreateFile(szModuleName, GENERIC_WRITE, 0, 0, becomes HANDLE hLogFile = CreateFile(szTempPathName, GENERIC_WRITE, 0, 0,
lstrcpy(pszFilePart, XCRASHREPORT_MINI_DUMP_FILE); becomes lstrcpy(pszTempFilePath, XCRASHREPORT_MINI_DUMP_FILE);
HANDLE hMiniDumpFile = CreateFile( szModuleName, becomes HANDLE hMiniDumpFile = CreateFile( szTempPathName,
--- In the XCrashReport project ---
In Dlg.h after TCHAR m_szModulePath[MAX_PATH*2]; add TCHAR m_szTempFilePath[MAX_PATH*2];
In Dlg.cpp In CDlg::OnInitDialog() after TCHAR *cp = _tcsrchr(m_szModulePath, _T('\\')); if (*cp) { *(cp+1) = _T('\0'); // remove file name } add GetTempPath(MAX_PATH*2, m_szTempFilePath);
In CDlg::GetFileDetails() CString strFilePath = m_szModulePath; strFilePath += XCRASHREPORT_ERROR_LOG_FILE; becomes CString strFilePath = m_szTempFilePath; strFilePath += XCRASHREPORT_ERROR_LOG_FILE;
strFilePath = m_szModulePath; strFilePath += XCRASHREPORT_MINI_DUMP_FILE; becomes strFilePath = m_szTempFilePath; strFilePath += XCRASHREPORT_MINI_DUMP_FILE;
In CDlg::GetRegistryDetails() CString strFile = XCRASHREPORT_REGISTRY_DUMP_FILE; if (!strFile.IsEmpty()) { strFilePath = m_szModulePath; strFilePath += strFile; becomes CString strFile = XCRASHREPORT_REGISTRY_DUMP_FILE; if (!strFile.IsEmpty()) { strFilePath = m_szTempFilePath; strFilePath += strFile;
In CDlg::ZipFiles() m_strZipFile = m_szModulePath; m_strZipFile += theApp.m_pszModule; becomes m_strZipFile = m_szTempFilePath; m_strZipFile += theApp.m_pszModule;
I think that was all. Make sure you test the code, log the file paths and see that files with names like: ERRORLOG.TXT, CRASH.DMP, Registry.txt, MYApp.zip end up in the correct location, and the xcrashreport.exe runs correctly.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Not sure if i ever bothered to thank you before now, but in case i forgot, here's a hearty Thanks! from someone who has benefited from your work. I've been using a modified version of this system in an internal app for close to a year now, and it has provided an excellent starting place for building our group's crash tracking and resolution system. So, in case you haven't heard it enough, you rock!
----
It appears that everybody is under the impression that I approve of the documentation. You probably also blame Ken Burns for supporting slavery.
--Raymond Chen on MSDN
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Hans Dietrich,
XCrashreport looks great! I'd love to use it, but unfortunately it does not seem to copile under VS 2005.
I just extracted the sources from codeproject, opened XCrashReport.dsp and converted it to a VS2005 project. I did not do any changes to the source code.
Upon building I got multiple compilation errors such as this one:
Error 1 error C2440: '=' : cannot convert from 'const char *' to 'LPTSTR' e:\temp\xcrashreport2\xcrashreport\xtrace.h 73
Or am I missing something?
Thanks in advance for your help! 
Pluggy
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
The function hflush and hprintf should be the following when UNICODE defined, or else we get wrong string length written to file(just half of them):
/////////////////////////////////////////////////////////////////////////////// // hflush static void hflush(HANDLE LogFile) { if (hprintf_index > 0) { DWORD NumBytes; WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer) * sizeof(TCHAR), &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) * sizeof(TCHAR), &NumBytes, 0); hprintf_index = 0; }
va_list arglist; va_start( arglist, Format); hprintf_index += wvsprintf(&hprintf_buffer[hprintf_index], Format, arglist); va_end( arglist); }
I added the two "* sizeof(TCHAR)"
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
First off, excellent series of articles and terrific application. Since there has been no response since a January posting of the same question, is it possible to generate the crash report from a thread created via AfxBeginThread? Everything is working as expect from the main thread, but nothing is created when the application crashes due to an error in another thread. Any help is appreciated.
Thank You
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
I have worked around the multi-thread problem with code similar to the following:
static LONG WINAPI RecordDefaultExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs) { return RecordExceptionInfo (pExceptPtrs, "Default"); }
main() { SetUnhandledExceptionFilter (&RecordDefaultExceptionInfo); . . . }
To be clear, put RecordDefaultExceptionInfo anywhere, and call SetUnhandledExceptionFilter as soon as possible. This will cause your handler to be invoked for all crashes, regardless of which thread blows up. You can still use the __try/__except blocks if you want to describe which thread (or other block of code) crashed, but they are no longer necessary.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Another option is to place another __try/__except block in the function named in the pfnThreadProc parameter to AfxBeginThread, similar to what you did to AfxWinMain.
|
| Sign In·View Thread·PermaLink | 3.00/5 (2 votes) |
|
|
|
 |
|
|
Please change the code like this: File: SendMail.cpp Line: 160 Add a line: recip.lpszAddress = NULL;//zhangwenchao
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Can you go into more detail? I also have a problem with the email address being mangled by Outlook (only if using Exchange Server).
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
I think I encountered the same problem under Outlook/Exchange Server. The email it generated looked good, but when I sent it, it bounced as undeliverable. The workaround was to add "SMTP:" to the email address. Example:
#define XCRASHREPORT_SEND_TO_ADDRESS _T("SMTP:support@softwarevendor.com")
According to the MAPI docs, all email addresses should look like this. Maybe other client/server combinations are more forgiving. I'll test this fix against other email clients later.
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
I found that the exception code include 'EXCEPTION_BREAKPOINT',but when a exception occurs, the __try __exception can not get the exception.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
I've added the files into my project as described in Part 1 of the tutorial for non-mfc projects, but I'm getting linking error. How can I fix this? The link error is below.
ExceptionAttacher.obj : error LNK2001: unresolved external symbol __afxForceEXCLUDE GetWinVer.obj : error LNK2001: unresolved external symbol __afxForceEXCLUDE MiniVersion.obj : error LNK2001: unresolved external symbol __afxForceEXCLUDE StdAfx.obj : error LNK2001: unresolved external symbol __afxForceEXCLUDE ExceptionAttacher.obj : error LNK2001: unresolved external symbol "void __stdcall AfxWinTerm(void)" (?AfxWinTerm@@YGXXZ) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "int __stdcall AfxUnlockTempMaps(int)" (?AfxUnlockTempMaps@@YGHH@Z) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "void __stdcall AfxLockTempMaps(void)" (?AfxLockTempMaps@@YGXXZ) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "class AFX_MODULE_THREAD_STATE * __stdcall AfxGetModuleThreadState(void)" (?AfxGetModuleThreadState@@YGPAVAFX_MODULE_THREAD_STATE@@XZ) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "void __stdcall AfxAssertValidObject(class CObject const *,char const *,int)" (?AfxAssertValidObject@@YGXPBVCObject@@PBDH@Z) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "int __stdcall AfxWinInit(struct HINSTANCE__ *,struct HINSTANCE__ *,char *,int)" (?AfxWinInit@@YGHPAUHINSTANCE__@@0PADH@Z) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "class CWinApp * __stdcall AfxGetApp(void)" (?AfxGetApp@@YGPAVCWinApp@@XZ) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "int __stdcall AfxAssertFailedLine(char const *,int)" (?AfxAssertFailedLine@@YGHPBDH@Z) ExceptionAttacher.obj : error LNK2001: unresolved external symbol "void __cdecl AfxTrace(char const *,...)" (?AfxTrace@@YAXPBDZZ) MiniVersion.obj : error LNK2001: unresolved external symbol _GetFileVersionInfoA@16 MiniVersion.obj : error LNK2001: unresolved external symbol _GetFileVersionInfoSizeA@8 MiniVersion.obj : error LNK2001: unresolved external symbol _VerQueryValueA@16
Many thanks in advance.
Thoeun.
|
| Sign In·View Thread·PermaLink | 2.75/5 (3 votes) |
|
|
|
 |
| | |