aboutsummaryrefslogtreecommitdiff
path: root/src/ext/UI/ca
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/UI/ca')
-rw-r--r--src/ext/UI/ca/CustomMsiErrors.h10
-rw-r--r--src/ext/UI/ca/DriveCheck.cpp126
-rw-r--r--src/ext/UI/ca/PrintEula.cpp556
-rw-r--r--src/ext/UI/ca/cost.h9
-rw-r--r--src/ext/UI/ca/dllmain.cpp26
-rw-r--r--src/ext/UI/ca/precomp.h18
-rw-r--r--src/ext/UI/ca/uica.def8
-rw-r--r--src/ext/UI/ca/uica.v3.ncrunchproject5
-rw-r--r--src/ext/UI/ca/uica.vcxproj58
9 files changed, 816 insertions, 0 deletions
diff --git a/src/ext/UI/ca/CustomMsiErrors.h b/src/ext/UI/ca/CustomMsiErrors.h
new file mode 100644
index 00000000..b568d01c
--- /dev/null
+++ b/src/ext/UI/ca/CustomMsiErrors.h
@@ -0,0 +1,10 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4#define msierrSQLFailedCreateDatabase 26201
5#define msierrSQLFailedDropDatabase 26202
6#define msierrSQLFailedConnectDatabase 26203
7#define msierrSQLFailedExecString 26204
8#define msierrSQLDatabaseAlreadyExists 26205
9
10//Last available is 26250 \ No newline at end of file
diff --git a/src/ext/UI/ca/DriveCheck.cpp b/src/ext/UI/ca/DriveCheck.cpp
new file mode 100644
index 00000000..fafd73c2
--- /dev/null
+++ b/src/ext/UI/ca/DriveCheck.cpp
@@ -0,0 +1,126 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static HRESULT PathIsRemote(__in LPCWSTR pTargetFolder, __inout BOOL* fPathRemote);
6static HRESULT PathIsRemovable(__in LPCWSTR pTargetFolder, __inout BOOL* fPathRemovable);
7
8/********************************************************************
9 ValidatePath - Custom Action entry point
10
11********************************************************************/
12UINT __stdcall ValidatePath(MSIHANDLE hInstall)
13{
14 HRESULT hr = S_OK;
15
16 LPWSTR pwszWixUIDir = NULL;
17 LPWSTR pwszInstallPath = NULL;
18 BOOL fInstallPathIsRemote = TRUE;
19 BOOL fInstallPathIsRemoveable = TRUE;
20
21 hr = WcaInitialize(hInstall, "ValidatePath");
22 ExitOnFailure(hr, "failed to initialize");
23
24 hr = WcaGetProperty(L"WIXUI_INSTALLDIR", &pwszWixUIDir);
25 ExitOnFailure(hr, "failed to get WixUI Installation Directory");
26
27 hr = WcaGetProperty(pwszWixUIDir, &pwszInstallPath);
28 ExitOnFailure(hr, "failed to get Installation Directory");
29
30 hr = PathIsRemote(pwszInstallPath, &fInstallPathIsRemote);
31 if (FAILED(hr))
32 {
33 TraceError(hr, "Unable to determine if path is remote");
34 //reset HR, as we need to continue and find out if is a UNC path
35 hr = S_OK;
36 }
37
38 hr = PathIsRemovable(pwszInstallPath, &fInstallPathIsRemoveable);
39 if (FAILED(hr))
40 {
41 TraceError(hr, "Unable to determine if path is removable");
42 //reset HR, as we need to continue and find out if is a UNC path
43 hr = S_OK;
44 }
45
46 // If the path does not point to a network drive, mapped drive, or removable drive,
47 // then set WIXUI_INSTALLDIR_VALID to "1" otherwise set it to 0
48 BOOL fInstallPathIsUnc = PathIsUNCW(pwszInstallPath);
49 if (!fInstallPathIsUnc && !fInstallPathIsRemote && !fInstallPathIsRemoveable)
50 {
51 // path is valid
52 hr = WcaSetProperty(L"WIXUI_INSTALLDIR_VALID", L"1");
53 ExitOnFailure(hr, "failed to set WIXUI_INSTALLDIR_VALID");
54 }
55 else
56 {
57 // path is invalid; we can't log it because we're being called from a DoAction control event
58 // but we can at least call WcaLog to get it to write to the debugger from a debug build
59 WcaLog(LOGMSG_STANDARD, "Installation path %ls is invalid: it is %s UNC path, %s remote path, or %s path on a removable drive, and must be none of these.",
60 pwszInstallPath, fInstallPathIsUnc ? "a" : "not a", fInstallPathIsRemote ? "a" : "not a", fInstallPathIsRemoveable ? "a" : "not a");
61 hr = WcaSetProperty(L"WIXUI_INSTALLDIR_VALID", L"0");
62 ExitOnFailure(hr, "failed to set WIXUI_INSTALLDIR_VALID");
63 }
64
65LExit:
66 ReleaseStr(pwszInstallPath);
67 ReleaseStr(pwszWixUIDir);
68
69 return WcaFinalize(SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE);
70}
71
72/********************************************************************
73 PathIsRemote - helper function for ValidatePath
74
75********************************************************************/
76static HRESULT PathIsRemote(__in LPCWSTR pTargetFolder, __inout BOOL* fPathRemote)
77{
78 HRESULT hr = S_OK;
79 LPWSTR pStrippedTargetFolder = NULL;
80
81 hr = StrAllocString(&pStrippedTargetFolder, pTargetFolder, 0);
82
83 // Terminate the path at the root
84 if(!::PathStripToRootW(pStrippedTargetFolder))
85 {
86 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
87 ExitOnFailure(hr, "failed to parse target folder");
88 }
89
90 UINT uResult = GetDriveTypeW(pStrippedTargetFolder);
91
92 *fPathRemote = (DRIVE_REMOTE == uResult) ;
93
94LExit:
95 ReleaseStr(pStrippedTargetFolder);
96
97 return hr;
98}
99
100/********************************************************************
101 PathIsRemovable - helper function for ValidatePath
102
103********************************************************************/
104static HRESULT PathIsRemovable(__in LPCWSTR pTargetFolder, __inout BOOL* fPathRemovable)
105{
106 HRESULT hr = S_OK;
107 LPWSTR pStrippedTargetFolder = NULL;
108
109 hr = StrAllocString(&pStrippedTargetFolder, pTargetFolder, 0);
110
111 // Terminate the path at the root
112 if(!::PathStripToRootW(pStrippedTargetFolder))
113 {
114 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
115 ExitOnFailure(hr, "failed to parse target folder");
116 }
117
118 UINT uResult = GetDriveTypeW(pStrippedTargetFolder);
119
120 *fPathRemovable = ((DRIVE_CDROM == uResult) || (DRIVE_REMOVABLE == uResult) || (DRIVE_RAMDISK == uResult) || (DRIVE_UNKNOWN == uResult));
121
122LExit:
123 ReleaseStr(pStrippedTargetFolder);
124
125 return hr;
126}
diff --git a/src/ext/UI/ca/PrintEula.cpp b/src/ext/UI/ca/PrintEula.cpp
new file mode 100644
index 00000000..b19de9a6
--- /dev/null
+++ b/src/ext/UI/ca/PrintEula.cpp
@@ -0,0 +1,556 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// Constants
6LPCWSTR vcsEulaQuery = L"SELECT `Text` FROM `Control` WHERE `Control`='LicenseText'";
7
8
9enum eEulaQuery { eqText = 1};
10const int IDM_POPULATE = 100;
11const int IDM_PRINT = 101;
12const int CONTROL_X_COORDINATE = 0;
13const int CONTROL_Y_COORDINATE = 0;
14const int CONTROL_WIDTH = 500;
15const int CONTROL_HEIGHT = 500;
16const int ONE_INCH = 1440; // 1440 TWIPS = 1 inch.
17const int TEXT_RECORD_POS = 1;
18const int STRING_CAPACITY = 512;
19const int NO_OF_COPIES = 1;
20const LPWSTR WINDOW_CLASS = L"PrintEulaRichText";
21
22//Forward declarations of functions, check the function definitions for the comments
23static LRESULT CALLBACK WndProc(__in HWND hWnd, __in UINT message, __in WPARAM wParam, __in LPARAM lParam);
24static HRESULT ReadEulaText(__in MSIHANDLE hInstall, __out LPSTR* ppszEulaText);
25static DWORD CALLBACK ReadStreamCallback(__in DWORD Cookie, __out LPBYTE pbBuff, __in LONG cb, __out LONG FAR *pcb);
26static HRESULT CreateRichTextWindow(__out HWND* phWndMain, __out BOOL* pfRegisteredClass);
27static HRESULT PrintRichText(__in HWND hWndMain);
28static void Print(__in_opt HWND hWnd);
29static void LoadEulaText(__in_opt HWND hWnd);
30static void ShowErrorMessage(__in HRESULT hr);
31
32//Global variables
33PRINTDLGEXW* vpPrintDlg = NULL; //Parameters for print (needed on both sides of WndProc callbacks)
34LPSTR vpszEulaText = NULL;
35LPCWSTR vwzRichEditClass = NULL;
36HRESULT vhr = S_OK; //Global hr, used by the functions called from WndProc to set errorcode
37
38
39/********************************************************************
40 PrintEula - Custom Action entry point
41
42********************************************************************/
43extern "C" UINT __stdcall PrintEula(MSIHANDLE hInstall)
44{
45 //AssertSz(FALSE, "Debug PrintEula");
46
47 HRESULT hr = S_OK;
48 HWND hWndMain = NULL;
49 HMODULE hRichEdit = NULL;
50 BOOL fRegisteredClass = FALSE;
51
52 hr = WcaInitialize(hInstall, "PrintEula");
53 ExitOnFailure(hr, "failed to initialize");
54
55 // Initialize then display print dialog.
56 vpPrintDlg = (PRINTDLGEXW*)GlobalAlloc(GPTR, sizeof(PRINTDLGEXW)); // MSDN says to allocate on heap.
57 ExitOnNullWithLastError(vpPrintDlg, hr, "Failed to allocate memory for print dialog struct.");
58
59 vpPrintDlg->lStructSize = sizeof(PRINTDLGEX);
60 vpPrintDlg->hwndOwner = ::FindWindowW(L"MsiDialogCloseClass", NULL);
61 vpPrintDlg->Flags = PD_RETURNDC | PD_COLLATE | PD_NOCURRENTPAGE | PD_ALLPAGES | PD_NOPAGENUMS | PD_NOSELECTION;
62 vpPrintDlg->nCopies = NO_OF_COPIES;
63 vpPrintDlg->nStartPage = START_PAGE_GENERAL;
64
65 hr = ::PrintDlgExW(vpPrintDlg);
66 ExitOnFailure(hr, "Failed to show print dialog");
67
68 // If user said they want to print.
69 if (PD_RESULT_PRINT == vpPrintDlg->dwResultAction)
70 {
71 // Get the stream for Eula
72 hr = ReadEulaText(hInstall, &vpszEulaText);
73 ExitOnFailure(hr, "failed to read Eula text from MSI database");
74
75 // Have to load Rich Edit since we'll be creating a Rich Edit control in the window
76 hr = LoadSystemLibrary(L"Msftedit.dll", &hRichEdit);
77 if (SUCCEEDED(hr))
78 {
79 vwzRichEditClass = MSFTEDIT_CLASS;
80 }
81 else
82 {
83 hr = LoadSystemLibrary(L"Riched20.dll", &hRichEdit);
84 ExitOnFailure(hr, "failed to load rich edit 2.0 library");
85
86 vwzRichEditClass = RICHEDIT_CLASSW;
87 }
88
89 hr = CreateRichTextWindow(&hWndMain, &fRegisteredClass);
90 ExitOnFailure(hr, "failed to create rich text window for printing");
91
92 hr = PrintRichText(hWndMain);
93 if (FAILED(hr)) // Since we've already shown the print dialog, we better show them a dialog explaining why it didn't print
94 {
95 ShowErrorMessage(hr);
96 }
97 }
98
99LExit:
100 ReleaseNullStr(vpszEulaText);
101 if (vpPrintDlg)
102 {
103 if (vpPrintDlg->hDevMode)
104 {
105 ::GlobalFree(vpPrintDlg->hDevMode);
106 }
107
108 if (vpPrintDlg->hDevNames)
109 {
110 ::GlobalFree(vpPrintDlg->hDevNames);
111 }
112
113 if (vpPrintDlg->hDC)
114 {
115 ::DeleteDC(vpPrintDlg->hDC);
116 }
117
118 ::GlobalFree(vpPrintDlg);
119 vpPrintDlg = NULL;
120 }
121
122 if (fRegisteredClass)
123 {
124 ::UnregisterClassW(WINDOW_CLASS, NULL);
125 }
126
127 vwzRichEditClass = NULL;
128 if (NULL != hRichEdit)
129 {
130 ::FreeLibrary(hRichEdit);
131 }
132
133 // Always return success since we dont want to stop the
134 // installation even if the Eula printing fails.
135 return WcaFinalize(ERROR_SUCCESS);
136}
137
138
139
140/********************************************************************
141CreateRichTextWindow - Creates Window and Child RichText control.
142
143********************************************************************/
144HRESULT CreateRichTextWindow(
145 __out HWND* phWndMain,
146 __out BOOL* pfRegisteredClass
147 )
148{
149 HRESULT hr = S_OK;
150 HWND hWndMain = NULL;
151 WNDCLASSEXW wcex;
152
153 //
154 // Register the window class
155 //
156 wcex.cbSize = sizeof(WNDCLASSEXW);
157 wcex.style = CS_HREDRAW | CS_VREDRAW;
158 wcex.lpfnWndProc = (WNDPROC)WndProc;
159 wcex.cbClsExtra = 0;
160 wcex.cbWndExtra = 0;
161 wcex.hInstance = NULL;
162 wcex.hIcon = NULL;
163 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
164 wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
165 wcex.lpszMenuName = NULL;
166 wcex.lpszClassName = WINDOW_CLASS;
167 wcex.hIconSm = NULL;
168
169 if (0 == ::RegisterClassExW(&wcex))
170 {
171 DWORD dwResult = ::GetLastError();
172
173 // If we get "Class already exists" error ignore it. We might
174 // encounter this when the user tries to print more than once
175 // in the same setup instance and we are unable to clean up fully.
176 if (dwResult != ERROR_CLASS_ALREADY_EXISTS)
177 {
178 ExitOnFailure(hr = HRESULT_FROM_WIN32(dwResult), "failed to register window class");
179 }
180 }
181
182 *pfRegisteredClass = TRUE;
183
184 // Perform application initialization:
185 hWndMain = ::CreateWindowW(WINDOW_CLASS, NULL, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
186 ExitOnNullWithLastError(hWndMain, hr, "failed to create window for printing");
187
188 ::ShowWindow(hWndMain, SW_HIDE);
189 if (!::UpdateWindow(hWndMain))
190 {
191 ExitWithLastError(hr, "failed to update window");
192 }
193
194 *phWndMain = hWndMain;
195
196LExit:
197 return hr;
198}
199
200
201/********************************************************************
202 PrintRichText - Sends messages to load the Eula text, print it, and
203 close the window.
204
205 NOTE: Returns errors that have occured while attempting to print,
206 which were saved in vhr by the print callbacks.
207********************************************************************/
208HRESULT PrintRichText(
209 __in HWND hWndMain
210 )
211{
212 MSG msg;
213
214 // Populate the RichEdit control
215 ::SendMessageW(hWndMain, WM_COMMAND, IDM_POPULATE, 0);
216
217 // Print Eula
218 ::SendMessageW(hWndMain, WM_COMMAND, IDM_PRINT, 0);
219
220 // Done! Lets close the Window
221 ::SendMessage(hWndMain, WM_CLOSE, 0, 0);
222 // Main message loop:
223 while (::GetMessageW(&msg, NULL, 0, 0))
224 {
225// if (!::TranslateAcceleratorW(msg.hwnd, NULL, &msg))
226// {
227// ::TranslateMessage(&msg);
228// ::DispatchMessageW(&msg);
229// }
230 }
231
232
233 // return any errors encountered in the print callbacks
234 return vhr;
235}
236
237
238/********************************************************************
239 WndProc - Windows callback procedure
240
241********************************************************************/
242LRESULT CALLBACK WndProc(
243 __in HWND hWnd,
244 __in UINT message,
245 __in WPARAM wParam,
246 __in LPARAM lParam
247 )
248{
249 static HWND hWndRichEdit = NULL;
250 int wmId, wmEvent;
251 PAINTSTRUCT ps;
252 HDC hdc;
253
254 switch (message)
255 {
256 case WM_CREATE:
257 hWndRichEdit = ::CreateWindowExW(WS_EX_CLIENTEDGE, vwzRichEditClass, L"", ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_VSCROLL, CONTROL_X_COORDINATE, CONTROL_Y_COORDINATE, CONTROL_WIDTH, CONTROL_HEIGHT, hWnd, NULL, NULL, NULL);
258 break;
259 case WM_COMMAND:
260 wmId = LOWORD(wParam);
261 wmEvent = HIWORD(wParam);
262 switch (wmId)
263 {
264 case IDM_POPULATE:
265 LoadEulaText(hWndRichEdit);
266 break;
267 case IDM_PRINT:
268 Print(hWndRichEdit);
269 break;
270 default:
271 return ::DefWindowProcW(hWnd, message, wParam, lParam);
272 break;
273 }
274 break;
275 case WM_PAINT:
276 hdc = ::BeginPaint(hWnd, &ps);
277 ::EndPaint(hWnd, &ps);
278 break;
279 case WM_DESTROY:
280 ::PostQuitMessage(0);
281 break;
282 default:
283 return ::DefWindowProcW(hWnd, message, wParam, lParam);
284 }
285
286 return 0;
287}
288
289
290/********************************************************************
291 ReadStreamCallback - Callback function to read data to the RichText control
292
293 NOTE: Richtext control uses this function to read data from the buffer
294********************************************************************/
295DWORD CALLBACK ReadStreamCallback(
296 __in DWORD /*Cookie*/,
297 __out LPBYTE pbBuff,
298 __in LONG cb,
299 __out LONG FAR *pcb
300 )
301{
302 static LPCSTR pszTextBuf = NULL;
303 DWORD er = ERROR_SUCCESS;
304
305 // If it's null set it to the beginning of the EULA buffer
306 if (pszTextBuf == NULL)
307 {
308 pszTextBuf = vpszEulaText;
309 }
310
311 LONG lTextLength = (LONG)lstrlen(pszTextBuf);
312
313 if (cb < 0)
314 {
315 *pcb = 0;
316 er = 1;
317 }
318 else if (lTextLength < cb ) // If the size to be written is less than then length of the buffer, write the rest
319 {
320 *pcb = lTextLength;
321 memcpy(pbBuff, pszTextBuf, *pcb);
322 pszTextBuf = NULL;
323 }
324 else // Only write the amount being asked for and move the pointer along
325 {
326 *pcb = cb;
327 memcpy(pbBuff, pszTextBuf, *pcb);
328 pszTextBuf = pszTextBuf + cb;
329 }
330
331 return er;
332}
333
334
335/********************************************************************
336 LoadEulaText - Reads data for Richedit control
337
338********************************************************************/
339void LoadEulaText(
340 __in HWND hWnd
341 )
342{
343 HRESULT hr = S_OK;
344
345 ExitOnNull(hWnd, hr, ERROR_INVALID_HANDLE, "Invalid Handle passed to LoadEulaText");
346
347 // Docs say this doesn't return any value
348 ::SendMessageW(hWnd, EM_LIMITTEXT, static_cast<WPARAM>(lstrlen(vpszEulaText)), 0);
349
350 EDITSTREAM es;
351 ::ZeroMemory(&es, sizeof(es));
352 es.pfnCallback = (EDITSTREAMCALLBACK)ReadStreamCallback;
353 es.dwCookie = (DWORD)0;
354 ::SendMessageW(hWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
355
356 if (0 != es.dwError)
357 {
358 ExitOnLastError(hr, "failed to load the EULA into the control");
359 }
360
361LExit:
362 vhr = hr;
363}
364
365
366/********************************************************************
367 ReadEulaText - Reads Eula text from the MSI
368
369********************************************************************/
370HRESULT ReadEulaText(
371 __in MSIHANDLE /*hInstall*/,
372 __out LPSTR* ppszEulaText
373 )
374{
375 HRESULT hr = S_OK;
376 PMSIHANDLE hDB;
377 PMSIHANDLE hView;
378 PMSIHANDLE hRec;
379 LPWSTR pwzEula = NULL;
380
381 hr = WcaOpenExecuteView(vcsEulaQuery, &hView);
382 ExitOnFailure(hr, "failed to open and execute view for PrintEula query");
383
384 hr = WcaFetchSingleRecord(hView, &hRec);
385 ExitOnFailure(hr, "failed to fetch the row containing the LicenseText");
386
387 hr = WcaGetRecordString(hRec, 1, &pwzEula);
388 ExitOnFailure(hr, "failed to get LicenseText in PrintEula");
389
390 hr = StrAnsiAllocString(ppszEulaText, pwzEula, 0, CP_ACP);
391 ExitOnFailure(hr, "failed to convert LicenseText to ANSI code page");
392
393LExit:
394 return hr;
395}
396
397
398/********************************************************************
399 Print - Function that sends the data from richedit control to the printer
400
401 NOTE: Any errors encountered are saved to the vhr variable
402********************************************************************/
403void Print(
404 __in_opt HWND hRtfWnd
405 )
406{
407 HRESULT hr = S_OK;
408 FORMATRANGE fRange;
409 RECT rcPage;
410 RECT rcPrintablePage;
411 GETTEXTLENGTHEX gTxex;
412 HDC hPrinterDC = vpPrintDlg->hDC;
413 int nHorizRes = ::GetDeviceCaps(hPrinterDC, HORZRES);
414 int nVertRes = ::GetDeviceCaps(hPrinterDC, VERTRES);
415 int nLogPixelsX = ::GetDeviceCaps(hPrinterDC, LOGPIXELSX);
416 //int nLogPixelsY = ::GetDeviceCaps(hPrinterDC, LOGPIXELSY);
417 LONG_PTR lTextLength = 0; // Length of document.
418 LONG_PTR lTextPrinted = 0; // Amount of document printed.
419 DOCINFOW dInfo;
420 LPDEVNAMES pDevnames;
421 LPWSTR sczProductName = NULL;
422 BOOL fStartedDoc = FALSE;
423 BOOL fPrintedSomething = FALSE;
424
425 // Ensure the printer DC is in MM_TEXT mode.
426 if (0 == ::SetMapMode(hPrinterDC, MM_TEXT))
427 {
428 ExitWithLastError(hr, "failed to set map mode");
429 }
430
431 // Rendering to the same DC we are measuring.
432 ::ZeroMemory(&fRange, sizeof(fRange));
433 fRange.hdc = fRange.hdcTarget = hPrinterDC;
434
435 // Set up the page.
436 rcPage.left = rcPage.top = 0;
437 rcPage.right = MulDiv(nHorizRes, ONE_INCH, nLogPixelsX);
438 rcPage.bottom = MulDiv(nVertRes, ONE_INCH, nLogPixelsX);
439
440 // Set up 1" margins all around.
441 rcPrintablePage.left = rcPage.left + ONE_INCH;
442 rcPrintablePage.top = rcPage.top + ONE_INCH;
443 rcPrintablePage.right = rcPage.right - ONE_INCH;
444 rcPrintablePage.bottom = rcPage.bottom - ONE_INCH;
445
446 // Set up the print job (standard printing stuff here).
447 ::ZeroMemory(&dInfo, sizeof(dInfo));
448 dInfo.cbSize = sizeof(DOCINFO);
449 hr = WcaGetProperty(L"ProductName", &sczProductName);
450 if (FAILED(hr))
451 {
452 // If we fail to get the product name, don't fail, just leave it blank;
453 dInfo.lpszDocName = L"";
454 hr = S_OK;
455 }
456 else
457 {
458 dInfo.lpszDocName = sczProductName;
459 }
460
461 pDevnames = (LPDEVNAMES)::GlobalLock(vpPrintDlg->hDevNames);
462 ExitOnNullWithLastError(pDevnames, hr, "failed to get global lock");
463
464 dInfo.lpszOutput = (LPWSTR)pDevnames + pDevnames->wOutputOffset;
465
466 if (0 == ::GlobalUnlock(pDevnames))
467 {
468 ExitWithLastError(hr, "failed to release global lock");
469 }
470
471 // Start the document.
472 if (0 >= ::StartDocW(hPrinterDC, &dInfo))
473 {
474 ExitWithLastError(hr, "failed to start print document");
475 }
476
477 fStartedDoc = TRUE;
478
479 ::ZeroMemory(&gTxex, sizeof(gTxex));
480 gTxex.flags = GTL_NUMCHARS | GTL_PRECISE;
481 lTextLength = ::SendMessageW(hRtfWnd, EM_GETTEXTLENGTHEX, (LONG_PTR)&gTxex, 0);
482
483 while (lTextPrinted < lTextLength)
484 {
485 // Start the page.
486 if (0 >= ::StartPage(hPrinterDC))
487 {
488 ExitWithLastError(hr, "failed to start print page");
489 }
490
491 // Always reset to the full printable page and start where the
492 // last text left off (or zero at the beginning).
493 fRange.rc = rcPrintablePage;
494 fRange.rcPage = rcPage;
495 fRange.chrg.cpMin = (LONG)lTextPrinted;
496 fRange.chrg.cpMax = -1;
497
498 // Print as much text as can fit on a page. The return value is
499 // the index of the first character on the next page. Using TRUE
500 // for the wParam parameter causes the text to be printed.
501 lTextPrinted = ::SendMessageW(hRtfWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fRange);
502 fPrintedSomething = TRUE;
503
504 // If text wasn't printed (i.e. we didn't move past the point we started) then
505 // something must have gone wrong.
506 if (lTextPrinted <= fRange.chrg.cpMin)
507 {
508 hr = E_FAIL;
509 ExitOnFailure(hr, "failed to print some text");
510 }
511
512 // Print last page.
513 if (0 >= ::EndPage(hPrinterDC))
514 {
515 ExitWithLastError(hr, "failed to end print page");
516 }
517 }
518
519LExit:
520 // Tell the control to release cached information, if we actually tried to
521 // print something.
522 if (fPrintedSomething)
523 {
524 ::SendMessageW(hRtfWnd, EM_FORMATRANGE, 0, (LPARAM)NULL);
525 }
526
527 if (fStartedDoc)
528 {
529 ::EndDoc(hPrinterDC);
530 }
531
532 ReleaseStr(sczProductName);
533
534 vhr = hr;
535}
536
537
538/********************************************************************
539 ShowErrorMessage - Display MessageBox showing the message for hr.
540
541********************************************************************/
542void ShowErrorMessage(
543 __in HRESULT hr
544 )
545{
546 WCHAR wzMsg[STRING_CAPACITY];
547
548#pragma prefast(push)
549#pragma prefast(disable:25028)
550 if (0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, hr, 0, wzMsg, countof(wzMsg), 0))
551#pragma prefast(pop)
552 {
553 HWND hWnd = ::GetForegroundWindow();
554 ::MessageBoxW(hWnd, wzMsg, L"PrintEULA", MB_OK | MB_ICONWARNING);
555 }
556}
diff --git a/src/ext/UI/ca/cost.h b/src/ext/UI/ca/cost.h
new file mode 100644
index 00000000..8b8e8874
--- /dev/null
+++ b/src/ext/UI/ca/cost.h
@@ -0,0 +1,9 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5const UINT COST_SECUREOBJECT = 1000;
6const UINT COST_SERVICECONFIG = 1000;
7const UINT COST_XMLFILE = 1000;
8const UINT COST_CLOSEAPP = 500;
9
diff --git a/src/ext/UI/ca/dllmain.cpp b/src/ext/UI/ca/dllmain.cpp
new file mode 100644
index 00000000..df53f872
--- /dev/null
+++ b/src/ext/UI/ca/dllmain.cpp
@@ -0,0 +1,26 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5/********************************************************************
6DllMain - standard entry point for all WiX CustomActions
7
8********************************************************************/
9extern "C" BOOL WINAPI DllMain(
10 IN HINSTANCE hInst,
11 IN ULONG ulReason,
12 IN LPVOID)
13{
14 switch(ulReason)
15 {
16 case DLL_PROCESS_ATTACH:
17 WcaGlobalInitialize(hInst);
18 break;
19
20 case DLL_PROCESS_DETACH:
21 WcaGlobalFinalize();
22 break;
23 }
24
25 return TRUE;
26}
diff --git a/src/ext/UI/ca/precomp.h b/src/ext/UI/ca/precomp.h
new file mode 100644
index 00000000..e95240c6
--- /dev/null
+++ b/src/ext/UI/ca/precomp.h
@@ -0,0 +1,18 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include <windows.h>
6#include <msiquery.h>
7#include <richedit.h>
8#include <shlwapi.h>
9
10#include "wcautil.h"
11#include "aclutil.h"
12#include "fileutil.h"
13#include "memutil.h"
14#include "strutil.h"
15#include "xmlutil.h"
16
17#include "CustomMsiErrors.h"
18#include "cost.h"
diff --git a/src/ext/UI/ca/uica.def b/src/ext/UI/ca/uica.def
new file mode 100644
index 00000000..62ce8135
--- /dev/null
+++ b/src/ext/UI/ca/uica.def
@@ -0,0 +1,8 @@
1; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3
4LIBRARY "uica"
5
6EXPORTS
7 PrintEula
8 ValidatePath
diff --git a/src/ext/UI/ca/uica.v3.ncrunchproject b/src/ext/UI/ca/uica.v3.ncrunchproject
new file mode 100644
index 00000000..319cd523
--- /dev/null
+++ b/src/ext/UI/ca/uica.v3.ncrunchproject
@@ -0,0 +1,5 @@
1<ProjectConfiguration>
2 <Settings>
3 <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
4 </Settings>
5</ProjectConfiguration> \ No newline at end of file
diff --git a/src/ext/UI/ca/uica.vcxproj b/src/ext/UI/ca/uica.vcxproj
new file mode 100644
index 00000000..5ded1266
--- /dev/null
+++ b/src/ext/UI/ca/uica.vcxproj
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|Win32">
7 <Configuration>Debug</Configuration>
8 <Platform>Win32</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|Win32">
11 <Configuration>Release</Configuration>
12 <Platform>Win32</Platform>
13 </ProjectConfiguration>
14 </ItemGroup>
15
16 <PropertyGroup Label="Globals">
17 <ProjectGuid>{F72D34CA-48DA-4DFD-91A9-A0C78BEF6981}</ProjectGuid>
18 <ConfigurationType>DynamicLibrary</ConfigurationType>
19 <TargetName>uica</TargetName>
20 <PlatformToolset>v142</PlatformToolset>
21 <CharacterSet>MultiByte</CharacterSet>
22 <ProjectModuleDefinitionFile>uica.def</ProjectModuleDefinitionFile>
23 <Description>WiX Toolset UI CustomAction</Description>
24 </PropertyGroup>
25
26 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
27 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
28
29 <PropertyGroup>
30 <ProjectAdditionalLinkLibraries>msi.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
31 </PropertyGroup>
32
33 <ItemGroup>
34 <ClCompile Include="dllmain.cpp">
35 <PrecompiledHeader>Create</PrecompiledHeader>
36 </ClCompile>
37 <ClCompile Include="DriveCheck.cpp" />
38 <ClCompile Include="PrintEula.cpp" />
39 </ItemGroup>
40
41 <ItemGroup>
42 <ClInclude Include="cost.h" />
43 <ClInclude Include="precomp.h" />
44 </ItemGroup>
45
46 <ItemGroup>
47 <None Include="uica.def" />
48 </ItemGroup>
49
50 <ItemGroup>
51 <PackageReference Include="WixToolset.Dutil" Version="4.0.72" />
52 <PackageReference Include="WixToolset.WcaUtil" Version="4.0.19" />
53 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
54 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
55 </ItemGroup>
56
57 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
58</Project>