1 // Copyright 2014 Idol Software, Inc.
3 // This file is part of Doctor Dump SDK.
5 // Doctor Dump SDK is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Serializer.h"
20 #include "DumpWriter.h"
21 #include "../../CommonLibs/Zlib/ZipUnzip.h"
22 #include "../../CommonLibs/Log/log.h"
23 #include "../../CommonLibs/Log/log_media.h"
24 #include "../../CommonLibs/Stat/stat.h"
25 #include "SendReportDlg.h"
26 #include "DoctorDump.h"
27 #include "../CrashHandler/CrashHandler.h"
28 #include "CrashInfo.h"
34 typedef BOOL (WINAPI
*LPFN_ISWOW64PROCESS
) (HANDLE
, PBOOL
);
35 LPFN_ISWOW64PROCESS fnIsWow64Process
= NULL
;
37 BOOL bIsWow64
= FALSE
;
39 HMODULE hKernel32
= GetModuleHandle(TEXT("kernel32"));
40 if (NULL
!= hKernel32
)
41 fnIsWow64Process
= (LPFN_ISWOW64PROCESS
)GetProcAddress(hKernel32
,"IsWow64Process");
43 if (NULL
!= fnIsWow64Process
)
44 fnIsWow64Process(GetCurrentProcess(), &bIsWow64
);
49 CAtlStringW
ToString(int n
)
52 result
.Format(L
"%i", n
);
56 bool g_cancel
= false;
58 class UI
: public doctor_dump::IUploadProgress
60 CHandle m_progressWindowThread
;
61 CWindow
* volatile m_progressWindow
;
62 Translator m_translator
;
63 bool m_additionalInfoAlreadyApproved
;
65 CStringW m_privacyPolicyUrl
;
69 : m_progressWindow(NULL
)
70 , m_translator(config
.AppName
, config
.Company
, GetModuleHandle(NULL
), IDR_TRANSLATE_INI
, config
.LangFilePath
)
71 , m_additionalInfoAlreadyApproved(config
.SendAdditionalDataWithoutApproval
!= FALSE
)
72 , m_serviceMode(!!config
.ServiceMode
)
73 , m_privacyPolicyUrl(config
.PrivacyPolicyUrl
)
79 CloseProgressWindow(true);
82 void OnProgress(SIZE_T total
, SIZE_T sent
) override
85 m_progressWindow
->PostMessage(WM_USER
, total
, sent
);
88 void ShowInitialProgressWindow(bool wasAssert
)
93 CloseProgressWindow();
94 m_progressWindowThread
.Attach(CreateThread(NULL
, 0, wasAssert
? SendAssertReportDlgThread
: SendReportDlgThread
, this, 0, NULL
));
95 if (!m_progressWindowThread
)
96 throw runtime_error("Failed to create thread");
99 void ShowFullDumpUploadProgressWindow()
104 CloseProgressWindow();
105 m_progressWindowThread
.Attach(CreateThread(NULL
, 0, SendFullDumpDlgThread
, this, 0, NULL
));
106 if (!m_progressWindowThread
)
107 throw runtime_error("Failed to create thread");
108 Sleep(100); // Give time to draw a dialog before writing a dump (it will freeze a dialog for unknown reasons)
111 bool AskSendFullDump()
116 CloseProgressWindow();
118 if (!m_additionalInfoAlreadyApproved
&& IDCANCEL
== CAskSendFullDumpDlg(m_translator
, m_privacyPolicyUrl
).DoModal())
121 m_additionalInfoAlreadyApproved
= true;
126 bool AskGetSolution(CSolutionDlg::Type type
)
131 CloseProgressWindow();
132 return IDOK
== CSolutionDlg(m_translator
, type
).DoModal();
137 ShowInitialProgressWindow(false);
142 ShowInitialProgressWindow(true);
149 ShowFullDumpUploadProgressWindow();
151 for (int i
= 0; m_progressWindow
&& i
< 100; ++i
)
153 m_progressWindow
->PostMessage(WM_USER
, 100, i
);
157 AskGetSolution(CSolutionDlg::Read
);
159 AskGetSolution(CSolutionDlg::Install
);
165 CSendReportDlg
dlg(m_translator
);
166 assert(!m_progressWindow
);
167 m_progressWindow
= &dlg
;
168 if (dlg
.DoModal() == IDCANCEL
)
170 m_progressWindow
= NULL
;
173 static DWORD WINAPI
SendReportDlgThread(LPVOID param
)
175 static_cast<UI
*>(param
)->SendReportDlg();
179 void SendAssertReportDlg()
181 CSendAssertReportDlg
dlg(m_translator
);
182 assert(!m_progressWindow
);
183 m_progressWindow
= &dlg
;
184 if (dlg
.DoModal() == IDCANCEL
)
186 m_progressWindow
= NULL
;
189 static DWORD WINAPI
SendAssertReportDlgThread(LPVOID param
)
191 static_cast<UI
*>(param
)->SendAssertReportDlg();
195 void SendFullDumpDlg()
197 CSendFullDumpDlg
dlg(m_translator
);
198 assert(!m_progressWindow
);
199 m_progressWindow
= &dlg
;
200 if (dlg
.DoModal() == IDCANCEL
)
202 m_progressWindow
= NULL
;
205 static DWORD WINAPI
SendFullDumpDlgThread(LPVOID param
)
207 static_cast<UI
*>(param
)->SendFullDumpDlg();
211 void CloseProgressWindow(bool cancel
= false)
213 if (!m_progressWindowThread
)
215 Sleep(100); // It could be that m_pProgressDlg->DoModal not yet created a window
216 if (m_progressWindow
)
217 m_progressWindow
->PostMessage(WM_CLOSE
);
218 WaitForSingleObject(m_progressWindowThread
, INFINITE
);
219 m_progressWindowThread
.Close();
230 unique_ptr
<CrashInfo
> m_crashInfo
;
231 CStringW m_workingFolder
;
232 CStringW m_miniDumpFile
;
233 CStringW m_miniDumpZipFile
;
234 CStringW m_fullDumpFile
;
235 CStringW m_fullDumpZipFile
;
239 DumpWriter m_dumpWriter
;
240 doctor_dump::DumpUploaderWebServiceEx m_dumpUploader
;
243 CrashProcessor(Log
& log
, Config
& config
)
247 , m_dumpUploader(log
)
253 if (!m_fullDumpFile
.IsEmpty())
254 DeleteFile(m_fullDumpFile
);
255 if (!m_miniDumpFile
.IsEmpty())
256 DeleteFile(m_miniDumpFile
);
257 if (!m_config
.LeaveDumpFilesInTempFolder
)
259 if (!m_fullDumpZipFile
.IsEmpty())
260 DeleteFile(m_fullDumpZipFile
);
261 if (!m_miniDumpZipFile
.IsEmpty())
262 DeleteFile(m_miniDumpZipFile
);
263 if (!m_workingFolder
.IsEmpty())
264 RemoveDirectory(m_workingFolder
);
270 GetTempPath(MAX_PATH
, m_workingFolder
.GetBuffer(MAX_PATH
));
271 m_workingFolder
.ReleaseBuffer();
277 t
.Format(L
"%ls_%d.%d.%d.%d_%04d%02d%02d_%02d%02d%02d",
278 (LPCWSTR
)m_config
.Prefix
,
279 m_config
.V
[0], m_config
.V
[1], m_config
.V
[2], m_config
.V
[3],
280 (int)st
.wYear
, (int)st
.wMonth
, (int)st
.wDay
, (int)st
.wHour
, (int)st
.wMinute
, (int)st
.wSecond
);
282 m_workingFolder
+= t
;
283 m_miniDumpFile
= m_workingFolder
+ _T("\\") + t
+ _T(".mini.dmp");
284 m_miniDumpZipFile
= m_workingFolder
+ _T("\\doctor_dump_mini.zip");
285 m_fullDumpFile
= m_workingFolder
+ _T("\\") + t
+ _T(".full.dmp");
286 m_fullDumpZipFile
= m_workingFolder
+ _T("\\doctor_dump_full.zip");
287 m_infoDll
= m_workingFolder
+ _T("\\info.dll");
288 m_infoFile
= m_workingFolder
+ _T("\\info.zip");
289 m_patch
= m_workingFolder
+ _T("\\solution.exe");
291 if (!CreateDirectory(m_workingFolder
, NULL
) && GetLastError() != ERROR_ALREADY_EXISTS
)
292 throw std::runtime_error("failed to create temp directory");
295 int GetUserPCID() const
302 DWORD param
= IsWow64() ? KEY_WOW64_64KEY
: 0;
304 if (ERROR_SUCCESS
== RegOpenKeyEx(HKEY_LOCAL_MACHINE
, _T("SOFTWARE\\Microsoft\\Cryptography"), 0, KEY_READ
|param
, &hCryptography
))
307 DWORD size
= sizeof(guid
);
308 if (ERROR_SUCCESS
== RegQueryValueEx(hCryptography
, _T("MachineGuid"), NULL
, NULL
, (LPBYTE
)guid
, &size
))
310 DWORD D1
= 0, D2
= 0, D3
= 0, D4
= 0, D5
= 0, D6
= 0;
311 _stscanf_s(guid
, _T("%08x-%04x-%04x-%04x-%04x%08x"), &D1
, &D2
, &D3
, &D4
, &D5
, &D6
);
312 DWORD DD1
= MAKELONG(D2
, D3
);
313 DWORD DD2
= MAKELONG(D4
, D5
);
314 PCID
= D1
^ MAKELONG(D3
, D2
) ^ MAKELONG(D5
, D4
) ^ D6
;
316 RegCloseKey(hCryptography
);
318 m_log
.Info(_T("Machine ID 0x%08X"), PCID
);
322 void AppendUserInfo(const CStringW
& dumpFile
, Zip
& zip
)
324 CStringW crashInfoFile
= m_workingFolder
+ L
"\\crashinfo.xml";
325 if (m_crashInfo
->GetCrashInfoFile(crashInfoFile
))
326 m_config
.FilesToAttach
.push_back(std::make_pair
<CStringW
, CStringW
>(CStringW(crashInfoFile
), L
"crashinfo.xml"));
328 CStringW crashUserInfoFile
= m_workingFolder
+ L
"\\crashuserinfo.xml";
329 if (m_config
.UserInfo
.size())
332 if (0 == _wfopen_s(&f
, crashUserInfoFile
, L
"wt,ccs=UTF-8"))
334 fwprintf_s(f
, L
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<UserInfo>\n");
335 for (auto it
= m_config
.UserInfo
.cbegin(), end
= m_config
.UserInfo
.cend(); it
!= end
; ++it
)
337 m_log
.Info(_T("Adding UserInfo \"%ls\" as \"%ls\"..."), static_cast<LPCWSTR
>(it
->first
), static_cast<LPCWSTR
>(it
->second
));
340 static_cast<LPCWSTR
>(it
->first
),
341 static_cast<LPCWSTR
>(it
->second
),
342 static_cast<LPCWSTR
>(it
->first
));
344 fwprintf_s(f
, L
"</UserInfo>");
346 m_config
.FilesToAttach
.push_back(std::make_pair
<CStringW
, CStringW
>(CStringW(crashUserInfoFile
), L
"crashuserinfo.xml"));
351 FindClose(FindFirstFileW(dumpFile
, &ff
));
352 __int64 attachedSizeLimit
= max(1024*1024I64
, (static_cast<__int64
>(ff
.nFileSizeHigh
) << 32) | ff
.nFileSizeLow
);
354 m_log
.Info(_T("Adding %d attaches..."), m_config
.FilesToAttach
.size());
355 for (auto it
= m_config
.FilesToAttach
.begin(), end
= m_config
.FilesToAttach
.end(); it
!= end
; ++it
)
357 m_log
.Info(_T("Adding \"%ls\" as \"%ls\"..."), static_cast<LPCWSTR
>(it
->first
), static_cast<LPCWSTR
>(it
->second
));
359 HANDLE hFind
= FindFirstFileW(it
->first
, &ff
);
360 if (hFind
== INVALID_HANDLE_VALUE
)
362 m_log
.Warning(_T("File \"%ls\" not found..."), static_cast<LPCWSTR
>(it
->first
));
366 __int64 size
= (static_cast<__int64
>(ff
.nFileSizeHigh
) << 32) | ff
.nFileSizeLow
;
367 if (size
> attachedSizeLimit
)
369 m_log
.Warning(_T("File \"%ls\" not skipped by size..."), static_cast<LPCWSTR
>(it
->first
));
372 attachedSizeLimit
-= size
;
374 zip
.AddFile(it
->first
, it
->second
, &g_cancel
);
375 m_log
.Info(_T("Done."));
377 DeleteFile(crashInfoFile
);
381 const CString
& dumpFile
,
382 const CString
& zipFile
,
385 MINIDUMP_EXCEPTION_INFORMATION
* pExceptInfo
,
387 MINIDUMP_CALLBACK_INFORMATION
* pCallback
,
391 throw std::runtime_error("canceled");
393 m_log
.Info(_T("Writing dump (%s)..."), static_cast<LPCTSTR
>(dumpFile
));
394 if (!m_dumpWriter
.WriteMiniDump(hProcess
, processId
, pExceptInfo
, dumpFile
, type
, pCallback
))
397 // 0x80070070 - There is not enough space on the disk
398 DWORD err
= GetLastError();
399 m_log
.Error(_T("WriteMiniDump has failed with error %d"), err
);
401 text
.Format("WriteMiniDump has failed with error %d", err
);
402 throw std::runtime_error(text
);
404 m_log
.Info(_T("Dump ready."));
407 throw std::runtime_error("canceled");
409 m_log
.Info(_T("Zipping dump (%s)..."), static_cast<LPCTSTR
>(zipFile
));
412 zip
.AddFile(dumpFile
, dumpFile
.Mid(dumpFile
.ReverseFind(_T('\\')) + 1));
414 AppendUserInfo(dumpFile
, zip
);
416 m_log
.Info(_T("Zipping done."));
419 throw std::runtime_error("canceled");
422 void PrepareMiniDump(
425 MINIDUMP_EXCEPTION_INFORMATION
* pExceptInfo
)
427 m_log
.Info(_T("Prepare mini dump..."));
429 DumpFilter
dumpFilter(g_cancel
, pExceptInfo
? pExceptInfo
->ThreadId
: 0);
430 PrepareDump(m_miniDumpFile
, m_miniDumpZipFile
, hProcess
, processId
, pExceptInfo
, MiniDumpFilterMemory
, dumpFilter
, false);
433 void PrepareFullDump(
436 MINIDUMP_EXCEPTION_INFORMATION
* pExceptInfo
,
439 m_log
.Info(_T("Prepare full dump..."));
441 DumpFilter
dumpFilter(g_cancel
);
442 PrepareDump(m_fullDumpFile
, m_fullDumpZipFile
, hProcess
, dwProcessId
, pExceptInfo
, (MINIDUMP_TYPE
)m_config
.FullDumpType
, dumpFilter
, attachFiles
);
445 typedef bool (WINAPI
*pfnCollectInfo
)(LPCWSTR file
, LPCWSTR cfg
, HANDLE process
, DWORD processId
);
446 void PrepareAdditionalInfo(const doctor_dump::NeedMoreInfoResponse
& response
, HANDLE process
, DWORD processId
)
448 m_log
.Info(_T("Prepare additional info..."));
449 HMODULE hInfoDll
= NULL
;
452 CAtlFile
hFile(CreateFile(m_infoDll
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
));
453 if (hFile
== INVALID_HANDLE_VALUE
)
454 throw runtime_error("failed to create info.dll file");
455 if (FAILED(hFile
.Write(&response
.infoModule
[0], static_cast<DWORD
>(response
.infoModule
.size()))))
456 throw runtime_error("failed to write info.dll file");
459 hInfoDll
= LoadLibraryW(m_infoDll
);
461 throw runtime_error("failed to load info.dll");
463 pfnCollectInfo CollectInfo
= (pfnCollectInfo
) GetProcAddress(hInfoDll
, "CollectInfo");
465 throw runtime_error("failed to get CollectInfo from info.dll");
467 if (!CollectInfo(m_infoFile
, response
.infoModuleCfg
.c_str(), process
, processId
))
468 throw runtime_error("CollectInfo failed");
470 FreeLibrary(hInfoDll
);
471 DeleteFile(m_infoDll
);
476 FreeLibrary(hInfoDll
);
477 DeleteFile(m_infoDll
);
482 void ProcessSolution(UI
& ui
, const doctor_dump::HaveSolutionResponse
& solution
)
484 m_log
.Info(_T("Process solution..."));
485 switch (solution
.type
)
487 case ns4__HaveSolutionResponse_SolutionType__Url
:
488 if (!solution
.askConfirmation
|| ui
.AskGetSolution(CSolutionDlg::Read
))
490 CAtlStringW url
= solution
.url
.c_str();
491 url
.Replace(L
"{ClientID}", solution
.clientID
.c_str());
492 url
.Replace(L
"{ProblemID}", ToString(solution
.problemID
));
493 url
.Replace(L
"{DumpGroupID}", ToString(solution
.dumpGroupID
));
494 url
.Replace(L
"{DumpID}", ToString(solution
.dumpID
));
495 ShellExecute(NULL
, _T("open"), CW2CT(url
), NULL
, NULL
, SW_SHOWNORMAL
);
498 case ns4__HaveSolutionResponse_SolutionType__Exe
:
499 if (!solution
.askConfirmation
|| ui
.AskGetSolution(CSolutionDlg::Install
))
501 CAtlFile
hFile(CreateFile(m_patch
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
));
502 if (hFile
== INVALID_HANDLE_VALUE
)
503 throw runtime_error("failed to create solution.exe file");
504 if (FAILED(hFile
.Write(&solution
.exe
[0], static_cast<DWORD
>(solution
.exe
.size()))))
505 throw runtime_error("failed to write solution.exe file");
510 PROCESS_INFORMATION pi
= {};
511 if (!CreateProcess(NULL
, m_patch
.GetBuffer(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
512 throw runtime_error("failed to start solution.exe");
513 CloseHandle(pi
.hThread
);
514 CloseHandle(pi
.hProcess
);
518 throw runtime_error("Unknown SolutionType");
522 bool Process(Params
& params
)
524 HANDLE hProcess
= params
.Process
;
525 DWORD dwProcessId
= params
.ProcessId
;
526 MINIDUMP_EXCEPTION_INFORMATION
* pExceptInfo
= ¶ms
.ExceptInfo
;
527 bool wasAssert
= !!params
.WasAssert
;
529 m_dumpWriter
.Init(m_config
.DbgHelpPath
);
531 // we need to get CrashInfo before writing the dumps, since dumps writing will change WorkingSet
532 m_crashInfo
.reset(new CrashInfo(hProcess
));
538 ui
.ShowInitialProgressWindow(wasAssert
);
540 PrepareMiniDump(hProcess
, dwProcessId
, pExceptInfo
);
541 if (m_config
.ServiceMode
)
543 PrepareFullDump(hProcess
, dwProcessId
, pExceptInfo
, true);
544 TerminateProcess(hProcess
, E_FAIL
); // It is necessary for DUMPPARSER to terminate app, because it should process our dump, and it could not do it since it crashes.
545 CloseHandle(hProcess
);
549 doctor_dump::Application app
;
550 app
.applicationGUID
= m_config
.ApplicationGUID
;
551 app
.v
[0] = m_config
.V
[0];
552 app
.v
[1] = m_config
.V
[1];
553 app
.v
[2] = m_config
.V
[2];
554 app
.v
[3] = m_config
.V
[3];
555 app
.hotfix
= m_config
.Hotfix
;
556 app
.processName
= m_config
.ProcessName
;
557 m_log
.Info(_T("App %d.%d.%d.%d %ls"), app
.v
[0], app
.v
[1], app
.v
[2], app
.v
[3], app
.applicationGUID
);
559 doctor_dump::DumpAdditionalInfo addInfo
;
560 addInfo
.crashDate
= time(NULL
);
561 addInfo
.PCID
= GetUserPCID();
562 addInfo
.submitterID
= m_config
.SubmitterID
;
563 addInfo
.group
= CA2W(params
.Group
);
564 addInfo
.description
= m_config
.CustomInfo
;
566 std::unique_ptr
<doctor_dump::Response
> response
= m_dumpUploader
.Hello(app
, (LPCWSTR
)m_config
.AppName
, (LPCWSTR
)m_config
.Company
, addInfo
);
569 switch (response
->GetResponseType())
571 case doctor_dump::Response::HaveSolutionResponseType
:
572 ProcessSolution(ui
, static_cast<doctor_dump::HaveSolutionResponse
&>(*response
));
575 case doctor_dump::Response::NeedMiniDumpResponseType
:
576 response
= m_dumpUploader
.UploadMiniDump(response
->context
, app
, addInfo
, (LPCWSTR
)m_miniDumpZipFile
);
579 case doctor_dump::Response::NeedFullDumpResponseType
:
580 if (!ui
.AskSendFullDump())
582 response
= m_dumpUploader
.RejectedToSendAdditionalInfo(response
->context
, app
, response
->dumpID
);
586 ui
.ShowFullDumpUploadProgressWindow();
588 if (!m_config
.ServiceMode
)
590 auto& resp
= static_cast<doctor_dump::NeedFullDumpResponse
&>(*response
);
591 m_config
.FullDumpType
= m_config
.FullDumpType
& (~resp
.restrictedDumpType
);
592 PrepareFullDump(hProcess
, dwProcessId
, pExceptInfo
, resp
.attachUserInfo
);
593 CloseHandle(hProcess
);
596 SetEvent(params
.ReportReady
);
597 response
= m_dumpUploader
.UploadFullDump(response
->context
, app
, response
->dumpID
, (LPCWSTR
)m_fullDumpZipFile
, &ui
);
600 case doctor_dump::Response::NeedMoreInfoResponseType
:
601 if (!ui
.AskSendFullDump())
603 response
= m_dumpUploader
.RejectedToSendAdditionalInfo(response
->context
, app
, response
->dumpID
);
607 ui
.ShowFullDumpUploadProgressWindow();
609 PrepareAdditionalInfo(static_cast<doctor_dump::NeedMoreInfoResponse
&>(*response
), hProcess
, dwProcessId
);
610 response
= m_dumpUploader
.UploadAdditionalInfo(response
->context
, app
, response
->dumpID
, (LPCWSTR
)m_infoFile
, &ui
);
613 case doctor_dump::Response::ErrorResponseType
:
614 throw runtime_error((const char*)CW2A(static_cast<doctor_dump::ErrorResponse
&>(*response
).error
.c_str()));
616 case doctor_dump::Response::StopResponseType
:
622 if (!m_config
.ServiceMode
&& !response
->urlToProblem
.empty() && m_config
.OpenProblemInBrowser
)
623 ShellExecuteW(NULL
, L
"open", response
->urlToProblem
.c_str(), NULL
, NULL
, SW_SHOWNORMAL
);
629 bool IsNetworkProblemException()
635 catch (doctor_dump::SoapException
& ex
)
637 if (ex
.IsNetworkProblem())
647 DWORD
SendReportImpl(Log
& log
, Params
& params
, Config
& config
, bool isDebug
)
651 log
.Info(_T("SENDRPT was invoked to send report, PID 0x%x"), params
.ProcessId
);
652 CrashProcessor(log
, config
).Process(params
);
656 catch (std::exception
& ex
)
658 log
.Error(_T("Problem occurred: %hs"), ex
.what());
659 OutputDebugStringA(ex
.what());
663 ::MessageBoxA(0, ex
.what(), "SendRpt: Error", MB_ICONERROR
);
667 bool statSend
= statistics::SendExceptionToGoogleAnalytics("UA-25460132-5", "sendrpt", ex
.what(), true);
668 if (!statSend
&& !IsNetworkProblemException())
669 ::MessageBoxA(0, ex
.what(), "SendRpt: Error", MB_ICONERROR
);
676 int __cdecl
SendReport(int argc
, wchar_t* argv
[])
678 Log
log(NULL
, _T("sendrpt"));
680 bool isDebug
= false;
682 if (ERROR_SUCCESS
== reg
.Open(HKEY_LOCAL_MACHINE
, _T("Software\\Idol Software\\DumpUploader"), KEY_READ
))
684 DWORD attachDebugger
= FALSE
;
685 reg
.QueryDWORDValue(_T("AttachDebugger"), attachDebugger
);
686 if (attachDebugger
!= FALSE
)
688 if (IDYES
== ::MessageBoxA(0, "Do you want to debug?\n\nYour should attach debugger to sendrpt.exe before choosing Yes.", "SendRpt: SendReport", MB_ICONWARNING
| MB_YESNO
))
692 DWORD traceEnable
= FALSE
;
693 if (ERROR_SUCCESS
== reg
.QueryDWORDValue(_T("TraceEnable"), traceEnable
) && traceEnable
!= FALSE
)
698 if (ERROR_SUCCESS
== reg
.QueryStringValue(_T("TraceFolder"), str
.GetBuffer(size
), &size
))
700 str
.ReleaseBuffer(size
-1);
702 log
.SetParams(LogMediaPtr(new FileMedia(str
+ _T("\\sendrpt.log"), true, false)));
710 if (argc
>= 2 && wcscmp(argv
[1], L
"-uitest") == 0)
712 config
.AppName
= "Test App";
713 config
.Company
= "Long Company Name";
715 config
.LangFilePath
= argv
[2];
721 Serializer
ser(argv
[1]);
722 ser
<< params
<< config
;
723 params
.ExceptInfo
.ClientPointers
= TRUE
;
725 return SendReportImpl(log
, params
, config
, isDebug
);