summaryrefslogtreecommitdiff
path: root/src/dtf/SfxCA/SfxUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtf/SfxCA/SfxUtil.cpp')
-rw-r--r--src/dtf/SfxCA/SfxUtil.cpp209
1 files changed, 209 insertions, 0 deletions
diff --git a/src/dtf/SfxCA/SfxUtil.cpp b/src/dtf/SfxCA/SfxUtil.cpp
new file mode 100644
index 00000000..1bf2c5b2
--- /dev/null
+++ b/src/dtf/SfxCA/SfxUtil.cpp
@@ -0,0 +1,209 @@
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#include "SfxUtil.h"
5
6/// <summary>
7/// Writes a formatted message to the MSI log.
8/// Does out-of-proc MSI calls if necessary.
9/// </summary>
10void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...)
11{
12 const int LOG_BUFSIZE = 4096;
13 wchar_t szBuf[LOG_BUFSIZE];
14 va_list args;
15 va_start(args, szMessage);
16 StringCchVPrintf(szBuf, LOG_BUFSIZE, szMessage, args);
17
18 if (!g_fRunningOutOfProc || NULL == g_pRemote)
19 {
20 MSIHANDLE hRec = MsiCreateRecord(1);
21 MsiRecordSetString(hRec, 0, L"SFXCA: [1]");
22 MsiRecordSetString(hRec, 1, szBuf);
23 MsiProcessMessage(hSession, INSTALLMESSAGE_INFO, hRec);
24 MsiCloseHandle(hRec);
25 }
26 else
27 {
28 // Logging is the only remote-MSI operation done from unmanaged code.
29 // It's not very convenient here because part of the infrastructure
30 // for remote MSI APIs is on the managed side.
31
32 RemoteMsiSession::RequestData req;
33 RemoteMsiSession::RequestData* pResp = NULL;
34 SecureZeroMemory(&req, sizeof(RemoteMsiSession::RequestData));
35
36 req.fields[0].vt = VT_UI4;
37 req.fields[0].uiValue = 1;
38 g_pRemote->SendRequest(RemoteMsiSession::MsiCreateRecord, &req, &pResp);
39 MSIHANDLE hRec = (MSIHANDLE) pResp->fields[0].iValue;
40
41 req.fields[0].vt = VT_I4;
42 req.fields[0].iValue = (int) hRec;
43 req.fields[1].vt = VT_UI4;
44 req.fields[1].uiValue = 0;
45 req.fields[2].vt = VT_LPWSTR;
46 req.fields[2].szValue = L"SFXCA: [1]";
47 g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp);
48
49 req.fields[0].vt = VT_I4;
50 req.fields[0].iValue = (int) hRec;
51 req.fields[1].vt = VT_UI4;
52 req.fields[1].uiValue = 1;
53 req.fields[2].vt = VT_LPWSTR;
54 req.fields[2].szValue = szBuf;
55 g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp);
56
57 req.fields[0].vt = VT_I4;
58 req.fields[0].iValue = (int) hSession;
59 req.fields[1].vt = VT_I4;
60 req.fields[1].iValue = (int) INSTALLMESSAGE_INFO;
61 req.fields[2].vt = VT_I4;
62 req.fields[2].iValue = (int) hRec;
63 g_pRemote->SendRequest(RemoteMsiSession::MsiProcessMessage, &req, &pResp);
64
65 req.fields[0].vt = VT_I4;
66 req.fields[0].iValue = (int) hRec;
67 req.fields[1].vt = VT_EMPTY;
68 req.fields[2].vt = VT_EMPTY;
69 g_pRemote->SendRequest(RemoteMsiSession::MsiCloseHandle, &req, &pResp);
70 }
71}
72
73/// <summary>
74/// Deletes a directory, including all files and subdirectories.
75/// </summary>
76/// <param name="szDir">Path to the directory to delete,
77/// not including a trailing backslash.</param>
78/// <returns>True if the directory was successfully deleted, or false
79/// if the deletion failed (most likely because some files were locked).
80/// </returns>
81bool DeleteDirectory(const wchar_t* szDir)
82{
83 size_t cchDir = wcslen(szDir);
84 size_t cchPathBuf = cchDir + 3 + MAX_PATH;
85 wchar_t* szPath = (wchar_t*) _alloca(cchPathBuf * sizeof(wchar_t));
86 if (szPath == NULL) return false;
87 StringCchCopy(szPath, cchPathBuf, szDir);
88 StringCchCat(szPath, cchPathBuf, L"\\*");
89 WIN32_FIND_DATA fd;
90 HANDLE hSearch = FindFirstFile(szPath, &fd);
91 while (hSearch != INVALID_HANDLE_VALUE)
92 {
93 StringCchCopy(szPath + cchDir + 1, cchPathBuf - (cchDir + 1), fd.cFileName);
94 if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
95 {
96 if (wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0)
97 {
98 DeleteDirectory(szPath);
99 }
100 }
101 else
102 {
103 DeleteFile(szPath);
104 }
105 if (!FindNextFile(hSearch, &fd))
106 {
107 FindClose(hSearch);
108 hSearch = INVALID_HANDLE_VALUE;
109 }
110 }
111 return RemoveDirectory(szDir) != 0;
112}
113
114bool DirectoryExists(const wchar_t* szDir)
115{
116 if (szDir != NULL)
117 {
118 DWORD dwAttrs = GetFileAttributes(szDir);
119 if (dwAttrs != -1 && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0)
120 {
121 return true;
122 }
123 }
124 return false;
125}
126
127/// <summary>
128/// Extracts a cabinet that is concatenated to a module
129/// to a new temporary directory.
130/// </summary>
131/// <param name="hSession">Handle to the installer session,
132/// used just for logging.</param>
133/// <param name="hModule">Module that has the concatenated cabinet.</param>
134/// <param name="szTempDir">Buffer for returning the path of the
135/// created temp directory.</param>
136/// <param name="cchTempDirBuf">Size in characters of the buffer.
137/// <returns>True if the files were extracted, or false if the
138/// buffer was too small or the directory could not be created
139/// or the extraction failed for some other reason.</returns>
140__success(return != false)
141bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule,
142 __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf)
143{
144 wchar_t szModule[MAX_PATH];
145 DWORD cchCopied = GetModuleFileName(hModule, szModule, MAX_PATH - 1);
146 if (cchCopied == 0)
147 {
148 Log(hSession, L"Failed to get module path. Error code %d.", GetLastError());
149 return false;
150 }
151 else if (cchCopied == MAX_PATH - 1)
152 {
153 Log(hSession, L"Failed to get module path -- path is too long.");
154 return false;
155 }
156
157 if (szTempDir == NULL || cchTempDirBuf < wcslen(szModule) + 1)
158 {
159 Log(hSession, L"Temp directory buffer is NULL or too small.");
160 return false;
161 }
162 StringCchCopy(szTempDir, cchTempDirBuf, szModule);
163 StringCchCat(szTempDir, cchTempDirBuf, L"-");
164
165 DWORD cchTempDir = (DWORD) wcslen(szTempDir);
166 for (int i = 0; DirectoryExists(szTempDir); i++)
167 {
168 swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i);
169 }
170
171 if (!CreateDirectory(szTempDir, NULL))
172 {
173 cchCopied = GetTempPath(cchTempDirBuf, szTempDir);
174 if (cchCopied == 0 || cchCopied >= cchTempDirBuf)
175 {
176 Log(hSession, L"Failed to get temp directory. Error code %d", GetLastError());
177 return false;
178 }
179
180 wchar_t* szModuleName = wcsrchr(szModule, L'\\');
181 if (szModuleName == NULL) szModuleName = szModule;
182 else szModuleName = szModuleName + 1;
183 StringCchCat(szTempDir, cchTempDirBuf, szModuleName);
184 StringCchCat(szTempDir, cchTempDirBuf, L"-");
185
186 cchTempDir = (DWORD) wcslen(szTempDir);
187 for (int i = 0; DirectoryExists(szTempDir); i++)
188 {
189 swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i);
190 }
191
192 if (!CreateDirectory(szTempDir, NULL))
193 {
194 Log(hSession, L"Failed to create temp directory. Error code %d", GetLastError());
195 return false;
196 }
197 }
198
199 Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir);
200 int err = ExtractCabinet(szModule, szTempDir);
201 if (err != 0)
202 {
203 Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err);
204 DeleteDirectory(szTempDir);
205 return false;
206 }
207 return true;
208}
209