Add start and pre commit hooks
[TortoiseGit.git] / src / TortoiseMerge / TortoiseMerge.cpp
blobb332af36775cdd850fc93cb97ae982a07de36dab
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2013 - TortoiseGit
4 // Copyright (C) 2006-2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include <dlgs.h>
22 #include "TortoiseMerge.h"
23 #include "MainFrm.h"
24 #include "AboutDlg.h"
25 #include "CmdLineParser.h"
26 #include "version.h"
27 #include "AppUtils.h"
28 #include "PathUtils.h"
29 #include "BrowseFolder.h"
30 #include "DirFileEnum.h"
31 #include "SelectFileFilter.h"
32 #include "FileDlgEventHandler.h"
33 #include "TempFile.h"
34 #include "TaskbarUUID.h"
36 #ifdef _DEBUG
37 #define new DEBUG_NEW
38 #endif
40 #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
42 BEGIN_MESSAGE_MAP(CTortoiseMergeApp, CWinAppEx)
43 ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
44 END_MESSAGE_MAP()
46 class PatchOpenDlgEventHandler : public CFileDlgEventHandler
48 public:
49 PatchOpenDlgEventHandler() {}
50 ~PatchOpenDlgEventHandler() {}
52 virtual STDMETHODIMP OnButtonClicked(IFileDialogCustomize* pfdc, DWORD dwIDCtl)
54 if (dwIDCtl == 101)
56 CComQIPtr<IFileOpenDialog> pDlg = pfdc;
57 if (pDlg)
59 pDlg->Close(S_OK);
62 return S_OK;
67 CTortoiseMergeApp::CTortoiseMergeApp()
68 : m_nAppLook(0)
70 EnableHtmlHelp();
71 m_bHiColorIcons = TRUE;
74 // The one and only CTortoiseMergeApp object
75 CTortoiseMergeApp theApp;
76 CString sOrigCWD;
77 #if ENABLE_CRASHHANLDER
78 CCrashReportTGit g_crasher(L"TortoiseGitMerge " _T(APP_X64_STRING), TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD, TGIT_VERDATE);
79 #endif
81 CString g_sGroupingUUID;
82 CString g_sGroupingIcon;
83 bool g_bGroupingRemoveIcon = false;
85 // CTortoiseMergeApp initialization
86 BOOL CTortoiseMergeApp::InitInstance()
88 SetDllDirectory(L"");
89 SetTaskIDPerUUID();
90 CCrashReport::Instance().AddUserInfoToReport(L"CommandLine", GetCommandLine());
93 DWORD len = GetCurrentDirectory(0, NULL);
94 if (len)
96 std::unique_ptr<TCHAR[]> originalCurrentDirectory(new TCHAR[len]);
97 if (GetCurrentDirectory(len, originalCurrentDirectory.get()))
99 sOrigCWD = originalCurrentDirectory.get();
100 sOrigCWD = CPathUtils::GetLongPathname(sOrigCWD);
105 //set the resource dll for the required language
106 CRegDWORD loc = CRegDWORD(_T("Software\\TortoiseGit\\LanguageID"), 1033);
107 long langId = loc;
108 CString langDll;
109 HINSTANCE hInst = NULL;
112 langDll.Format(_T("%sLanguages\\TortoiseMerge%ld.dll"), (LPCTSTR)CPathUtils::GetAppParentDirectory(), langId);
114 hInst = LoadLibrary(langDll);
115 CString sVer = _T(STRPRODUCTVER);
116 CString sFileVer = CPathUtils::GetVersionFromFile(langDll);
117 if (sFileVer.Compare(sVer)!=0)
119 FreeLibrary(hInst);
120 hInst = NULL;
122 if (hInst != NULL)
123 AfxSetResourceHandle(hInst);
124 else
126 DWORD lid = SUBLANGID(langId);
127 lid--;
128 if (lid > 0)
130 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
132 else
133 langId = 0;
135 } while ((hInst == NULL) && (langId != 0));
136 TCHAR buf[6] = { 0 };
137 _tcscpy_s(buf, _T("en"));
138 langId = loc;
139 CString sHelppath = CPathUtils::GetAppDirectory() + _T("TortoiseMerge_en.chm");
140 free((void*)m_pszHelpFilePath);
141 m_pszHelpFilePath=_tcsdup(sHelppath);
142 sHelppath = CPathUtils::GetAppParentDirectory() + _T("Languages\\TortoiseMerge_en.chm");
145 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO639LANGNAME, buf, _countof(buf));
146 CString sLang = _T("_");
147 sLang += buf;
148 sHelppath.Replace(_T("_en"), sLang);
149 if (PathFileExists(sHelppath))
151 free((void*)m_pszHelpFilePath);
152 m_pszHelpFilePath=_tcsdup(sHelppath);
153 break;
155 sHelppath.Replace(sLang, _T("_en"));
156 GetLocaleInfo(MAKELCID(langId, SORT_DEFAULT), LOCALE_SISO3166CTRYNAME, buf, _countof(buf));
157 sLang += _T("_");
158 sLang += buf;
159 sHelppath.Replace(_T("_en"), sLang);
160 if (PathFileExists(sHelppath))
162 free((void*)m_pszHelpFilePath);
163 m_pszHelpFilePath=_tcsdup(sHelppath);
164 break;
166 sHelppath.Replace(sLang, _T("_en"));
168 DWORD lid = SUBLANGID(langId);
169 lid--;
170 if (lid > 0)
172 langId = MAKELANGID(PRIMARYLANGID(langId), lid);
174 else
175 langId = 0;
176 } while (langId);
177 setlocale(LC_ALL, "");
178 // We need to explicitly set the thread locale to the system default one to avoid possible problems with saving files in its original codepage
179 // The problems occures when the language of OS differs from the regional settings
180 // See the details here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887
181 SetThreadLocale(LOCALE_SYSTEM_DEFAULT);
183 // InitCommonControls() is required on Windows XP if an application
184 // manifest specifies use of ComCtl32.dll version 6 or later to enable
185 // visual styles. Otherwise, any window creation will fail.
186 InitCommonControls();
188 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
189 CMFCButton::EnableWindowsTheming();
190 EnableTaskbarInteraction(FALSE);
192 // Initialize all Managers for usage. They are automatically constructed
193 // if not yet present
194 InitContextMenuManager();
195 InitKeyboardManager();
196 InitTooltipManager ();
197 CMFCToolTipInfo params;
198 params.m_bVislManagerTheme = TRUE;
200 GetTooltipManager ()->SetTooltipParams (
201 AFX_TOOLTIP_TYPE_ALL,
202 RUNTIME_CLASS (CMFCToolTipCtrl),
203 &params);
205 CCmdLineParser parser = CCmdLineParser(this->m_lpCmdLine);
207 g_sGroupingUUID = parser.GetVal(L"groupuuid");
209 if (parser.HasKey(_T("?")) || parser.HasKey(_T("help")))
211 CString sHelpText;
212 sHelpText.LoadString(IDS_COMMANDLINEHELP);
213 MessageBox(NULL, sHelpText, _T("TortoiseGitMerge"), MB_ICONINFORMATION);
214 return FALSE;
217 // Initialize OLE libraries
218 if (!AfxOleInit())
220 AfxMessageBox(IDP_OLE_INIT_FAILED);
221 return FALSE;
223 AfxEnableControlContainer();
224 // Standard initialization
225 // If you are not using these features and wish to reduce the size
226 // of your final executable, you should remove from the following
227 // the specific initialization routines you do not need
228 // Change the registry key under which our settings are stored
229 SetRegistryKey(_T("TortoiseGitMerge"));
231 if (CRegDWORD(_T("Software\\TortoiseGitMerge\\Debug"), FALSE)==TRUE)
232 AfxMessageBox(AfxGetApp()->m_lpCmdLine, MB_OK | MB_ICONINFORMATION);
234 // To create the main window, this code creates a new frame window
235 // object and then sets it as the application's main window object
236 CMainFrame* pFrame = new CMainFrame;
237 if (pFrame == NULL)
238 return FALSE;
239 m_pMainWnd = pFrame;
241 // create and load the frame with its resources
242 if (!pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL))
243 return FALSE;
245 // Fill in the command line options
246 pFrame->m_Data.m_baseFile.SetFileName(parser.GetVal(_T("base")));
247 pFrame->m_Data.m_baseFile.SetDescriptiveName(parser.GetVal(_T("basename")));
248 pFrame->m_Data.m_theirFile.SetFileName(parser.GetVal(_T("theirs")));
249 pFrame->m_Data.m_theirFile.SetDescriptiveName(parser.GetVal(_T("theirsname")));
250 pFrame->m_Data.m_yourFile.SetFileName(parser.GetVal(_T("mine")));
251 pFrame->m_Data.m_yourFile.SetDescriptiveName(parser.GetVal(_T("minename")));
252 pFrame->m_Data.m_mergedFile.SetFileName(parser.GetVal(_T("merged")));
253 pFrame->m_Data.m_mergedFile.SetDescriptiveName(parser.GetVal(_T("mergedname")));
254 pFrame->m_Data.m_sPatchPath = parser.HasVal(_T("patchpath")) ? parser.GetVal(_T("patchpath")) : _T("");
255 pFrame->m_Data.m_sPatchPath.Replace('/', '\\');
256 if (parser.HasKey(_T("patchoriginal")))
257 pFrame->m_Data.m_sPatchOriginal = parser.GetVal(_T("patchoriginal"));
258 if (parser.HasKey(_T("patchpatched")))
259 pFrame->m_Data.m_sPatchPatched = parser.GetVal(_T("patchpatched"));
260 pFrame->m_Data.m_sDiffFile = parser.GetVal(_T("diff"));
261 pFrame->m_Data.m_sDiffFile.Replace('/', '\\');
262 if (parser.HasKey(_T("oneway")))
263 pFrame->m_bOneWay = TRUE;
264 if (parser.HasKey(_T("diff")))
265 pFrame->m_bOneWay = FALSE;
266 if (parser.HasKey(_T("reversedpatch")))
267 pFrame->m_bReversedPatch = TRUE;
268 if (parser.HasKey(_T("saverequired")))
269 pFrame->m_bSaveRequired = true;
270 if (parser.HasKey(_T("saverequiredonconflicts")))
271 pFrame->m_bSaveRequiredOnConflicts = true;
272 if (pFrame->m_Data.IsBaseFileInUse() && !pFrame->m_Data.IsYourFileInUse() && pFrame->m_Data.IsTheirFileInUse())
274 pFrame->m_Data.m_yourFile.TransferDetailsFrom(pFrame->m_Data.m_theirFile);
277 if ((!parser.HasKey(_T("patchpath")))&&(parser.HasVal(_T("diff"))))
279 // a patchfile was given, but not folder path to apply the patch to
280 // If the patchfile is located inside a working copy, then use the parent directory
281 // of the patchfile as the target directory, otherwise ask the user for a path.
282 if (parser.HasKey(_T("wc")))
283 pFrame->m_Data.m_sPatchPath = pFrame->m_Data.m_sDiffFile.Left(pFrame->m_Data.m_sDiffFile.ReverseFind('\\'));
284 else
286 CBrowseFolder fbrowser;
287 fbrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
288 if (fbrowser.Show(NULL, pFrame->m_Data.m_sPatchPath)==CBrowseFolder::CANCEL)
289 return FALSE;
293 if ((parser.HasKey(_T("patchpath")))&&(!parser.HasVal(_T("diff"))))
295 // A path was given for applying a patchfile, but
296 // the patchfile itself was not.
297 // So ask the user for that patchfile
299 HRESULT hr;
300 // Create a new common save file dialog
301 CComPtr<IFileOpenDialog> pfd = NULL;
302 hr = pfd.CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER);
303 if (SUCCEEDED(hr))
305 // Set the dialog options
306 DWORD dwOptions;
307 if (SUCCEEDED(hr = pfd->GetOptions(&dwOptions)))
309 hr = pfd->SetOptions(dwOptions | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
312 // Set a title
313 if (SUCCEEDED(hr))
315 CString temp;
316 temp.LoadString(IDS_OPENDIFFFILETITLE);
317 pfd->SetTitle(temp);
319 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
320 hr = pfd->SetFileTypes(fileFilter.GetCount(), fileFilter);
321 bool bAdvised = false;
322 DWORD dwCookie = 0;
323 CComObjectStackEx<PatchOpenDlgEventHandler> cbk;
324 CComQIPtr<IFileDialogEvents> pEvents = cbk.GetUnknown();
327 CComPtr<IFileDialogCustomize> pfdCustomize;
328 hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdCustomize));
329 if (SUCCEEDED(hr))
331 // check if there's a unified diff on the clipboard and
332 // add a button to the fileopen dialog if there is.
333 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
334 if ((cFormat)&&(OpenClipboard(NULL)))
336 HGLOBAL hglb = GetClipboardData(cFormat);
337 if (hglb)
339 pfdCustomize->AddPushButton(101, CString(MAKEINTRESOURCE(IDS_PATCH_COPYFROMCLIPBOARD)));
340 hr = pfd->Advise(pEvents, &dwCookie);
341 bAdvised = SUCCEEDED(hr);
343 CloseClipboard();
348 // Show the save file dialog
349 if (SUCCEEDED(hr) && SUCCEEDED(hr = pfd->Show(pFrame->m_hWnd)))
351 // Get the selection from the user
352 CComPtr<IShellItem> psiResult = NULL;
353 hr = pfd->GetResult(&psiResult);
354 if (bAdvised)
355 pfd->Unadvise(dwCookie);
356 if (SUCCEEDED(hr))
358 PWSTR pszPath = NULL;
359 hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
360 if (SUCCEEDED(hr))
362 pFrame->m_Data.m_sDiffFile = pszPath;
363 CoTaskMemFree(pszPath);
366 else
368 // no result, which means we closed the dialog in our button handler
369 std::wstring sTempFile;
370 if (TrySavePatchFromClipboard(sTempFile))
371 pFrame->m_Data.m_sDiffFile = sTempFile.c_str();
374 else
376 if (bAdvised)
377 pfd->Unadvise(dwCookie);
378 return FALSE;
381 else
383 OPENFILENAME ofn = {0}; // common dialog box structure
384 TCHAR szFile[MAX_PATH] = {0}; // buffer for file name
385 // Initialize OPENFILENAME
386 ofn.lStructSize = sizeof(OPENFILENAME);
387 ofn.hwndOwner = pFrame->m_hWnd;
388 ofn.lpstrFile = szFile;
389 ofn.nMaxFile = _countof(szFile);
390 CString temp;
391 temp.LoadString(IDS_OPENDIFFFILETITLE);
392 if (temp.IsEmpty())
393 ofn.lpstrTitle = NULL;
394 else
395 ofn.lpstrTitle = temp;
397 ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;
398 if( HasClipboardPatch() ) {
399 ofn.Flags |= ( OFN_ENABLETEMPLATE | OFN_ENABLEHOOK );
400 ofn.hInstance = AfxGetResourceHandle();
401 ofn.lpTemplateName = MAKEINTRESOURCE(IDD_PATCH_FILE_OPEN_CUSTOM);
402 ofn.lpfnHook = CreatePatchFileOpenHook;
405 CSelectFileFilter fileFilter(IDS_PATCHFILEFILTER);
406 ofn.lpstrFilter = fileFilter;
407 ofn.nFilterIndex = 1;
409 // Display the Open dialog box.
410 if (GetOpenFileName(&ofn)==FALSE)
412 return FALSE;
414 pFrame->m_Data.m_sDiffFile = ofn.lpstrFile;
418 if ( pFrame->m_Data.m_baseFile.GetFilename().IsEmpty() && pFrame->m_Data.m_yourFile.GetFilename().IsEmpty() )
420 int nArgs;
421 LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
422 if( NULL == szArglist )
424 TRACE("CommandLineToArgvW failed\n");
426 else
428 if ( nArgs==3 || nArgs==4 )
430 // Four parameters:
431 // [0]: Program name
432 // [1]: BASE file
433 // [2]: my file
434 // [3]: THEIR file (optional)
435 // This is the same format CAppUtils::StartExtDiff
436 // uses if %base and %mine are not set and most
437 // other diff tools use it too.
438 if ( PathFileExists(szArglist[1]) && PathFileExists(szArglist[2]) )
440 pFrame->m_Data.m_baseFile.SetFileName(szArglist[1]);
441 pFrame->m_Data.m_yourFile.SetFileName(szArglist[2]);
442 if ( nArgs == 4 && PathFileExists(szArglist[3]) )
444 pFrame->m_Data.m_theirFile.SetFileName(szArglist[3]);
450 // Free memory allocated for CommandLineToArgvW arguments.
451 LocalFree(szArglist);
454 pFrame->m_bReadOnly = !!parser.HasKey(_T("readonly"));
455 if (GetFileAttributes(pFrame->m_Data.m_yourFile.GetFilename()) & FILE_ATTRIBUTE_READONLY)
456 pFrame->m_bReadOnly = true;
457 pFrame->m_bBlame = !!parser.HasKey(_T("blame"));
458 // diffing a blame means no editing!
459 if (pFrame->m_bBlame)
460 pFrame->m_bReadOnly = true;
462 pFrame->SetWindowTitle();
464 if (parser.HasKey(_T("createunifieddiff")))
466 // user requested to create a unified diff file
467 CString origFile = parser.GetVal(_T("origfile"));
468 CString modifiedFile = parser.GetVal(_T("modifiedfile"));
469 if (!origFile.IsEmpty() && !modifiedFile.IsEmpty())
471 CString outfile = parser.GetVal(_T("outfile"));
472 if (outfile.IsEmpty())
474 CCommonAppUtils::FileOpenSave(outfile, NULL, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, NULL);
476 if (!outfile.IsEmpty())
478 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outfile, false);
479 return FALSE;
484 pFrame->resolveMsgWnd = parser.HasVal(L"resolvemsghwnd") ? (HWND)parser.GetLongLongVal(L"resolvemsghwnd") : 0;
485 pFrame->resolveMsgWParam = parser.HasVal(L"resolvemsgwparam") ? (WPARAM)parser.GetLongLongVal(L"resolvemsgwparam") : 0;
486 pFrame->resolveMsgLParam = parser.HasVal(L"resolvemsglparam") ? (LPARAM)parser.GetLongLongVal(L"resolvemsglparam") : 0;
488 // The one and only window has been initialized, so show and update it
489 pFrame->ActivateFrame();
490 pFrame->ShowWindow(SW_SHOW);
491 pFrame->UpdateWindow();
492 pFrame->ShowDiffBar(!pFrame->m_bOneWay);
493 if (!pFrame->m_Data.IsBaseFileInUse() && pFrame->m_Data.m_sPatchPath.IsEmpty() && pFrame->m_Data.m_sDiffFile.IsEmpty())
495 pFrame->OnFileOpen();
496 return TRUE;
499 int line = -2;
500 if (parser.HasVal(_T("line")))
502 line = parser.GetLongVal(_T("line"));
503 line--; // we need the index
506 return pFrame->LoadViews(line);
509 // CTortoiseMergeApp message handlers
511 void CTortoiseMergeApp::OnAppAbout()
513 CAboutDlg aboutDlg;
514 aboutDlg.DoModal();
517 UINT_PTR CALLBACK
518 CTortoiseMergeApp::CreatePatchFileOpenHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM /*lParam*/)
520 if(uiMsg == WM_COMMAND && LOWORD(wParam) == IDC_PATCH_TO_CLIPBOARD)
522 HWND hFileDialog = GetParent(hDlg);
524 // if there's a patchfile in the clipboard, we save it
525 // to a temporary file and tell TortoiseMerge to use that one
526 std::wstring sTempFile;
527 if (TrySavePatchFromClipboard(sTempFile))
529 CommDlg_OpenSave_SetControlText(hFileDialog, edt1, sTempFile.c_str());
530 PostMessage(hFileDialog, WM_COMMAND, MAKEWPARAM(IDOK, BM_CLICK), (LPARAM)(GetDlgItem(hDlg, IDOK)));
533 return 0;
536 int CTortoiseMergeApp::ExitInstance()
538 // Look for temporary files left around by TortoiseMerge and
539 // remove them. But only delete 'old' files
540 CTempFiles::DeleteOldTempFiles(_T("*tsm*.*"));
542 return CWinAppEx::ExitInstance();
545 bool CTortoiseMergeApp::HasClipboardPatch()
547 // check if there's a patchfile in the clipboard
548 const UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
549 if (cFormat == 0)
550 return false;
552 if (OpenClipboard(NULL) == 0)
553 return false;
555 bool containsPatch = false;
556 UINT enumFormat = 0;
559 if (enumFormat == cFormat)
561 containsPatch = true; // yes, there's a patchfile in the clipboard
563 } while((enumFormat = EnumClipboardFormats(enumFormat))!=0);
564 CloseClipboard();
566 return containsPatch;
569 bool CTortoiseMergeApp::TrySavePatchFromClipboard(std::wstring& resultFile)
571 resultFile.clear();
573 UINT cFormat = RegisterClipboardFormat(_T("TSVN_UNIFIEDDIFF"));
574 if (cFormat == 0)
575 return false;
576 if (OpenClipboard(NULL) == 0)
577 return false;
579 HGLOBAL hglb = GetClipboardData(cFormat);
580 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
582 DWORD len = GetTempPath(0, NULL);
583 std::unique_ptr<TCHAR[]> path(new TCHAR[len+1]);
584 std::unique_ptr<TCHAR[]> tempF(new TCHAR[len+100]);
585 GetTempPath (len+1, path.get());
586 GetTempFileName (path.get(), _T("tsm"), 0, tempF.get());
587 std::wstring sTempFile = std::wstring(tempF.get());
589 FILE* outFile = 0;
590 _tfopen_s(&outFile, sTempFile.c_str(), _T("wb"));
591 if (outFile != 0)
593 size_t patchlen = strlen(lpstr);
594 size_t size = fwrite(lpstr, sizeof(char), patchlen, outFile);
595 if (size == patchlen)
596 resultFile = sTempFile;
598 fclose(outFile);
600 GlobalUnlock(hglb);
601 CloseClipboard();
603 return !resultFile.empty();