1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
// 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.
#include "precomp.h"
/********************************************************************
IsVerboseLoggingPolicy() - internal helper function to detect if
policy is set for verbose logging. Does
not require database access.
********************************************************************/
static BOOL IsVerboseLoggingPolicy()
{
BOOL fVerbose = FALSE;
HKEY hkey = NULL;
WCHAR rgwc[16] = { 0 };
DWORD cb = sizeof(rgwc);
if (ERROR_SUCCESS == ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Policies\\Microsoft\\Windows\\Installer", 0, KEY_QUERY_VALUE, &hkey))
{
if (ERROR_SUCCESS == ::RegQueryValueExW(hkey, L"Logging", 0, NULL, reinterpret_cast<BYTE*>(rgwc), &cb))
{
for (LPCWSTR pwc = rgwc; (cb / sizeof(WCHAR)) > static_cast<DWORD>(pwc - rgwc) && *pwc; pwc++)
{
if (L'v' == *pwc || L'V' == *pwc)
{
fVerbose = TRUE;
break;
}
}
}
::RegCloseKey(hkey);
}
return fVerbose;
}
/********************************************************************
IsVerboseLogging() - internal helper function to detect if doing
verbose logging. Checks:
1. LOGVERBOSE property.
2. MsiLogging property contains 'v'
3. Policy from registry.
Requires database access.
********************************************************************/
BOOL WIXAPI IsVerboseLogging()
{
static int iVerbose = -1;
LPWSTR pwzMsiLogging = NULL;
if (0 > iVerbose)
{
iVerbose = WcaIsPropertySet("LOGVERBOSE");
if (0 == iVerbose)
{
// if the property wasn't set, check the MsiLogging property (MSI 4.0+)
HRESULT hr = WcaGetProperty(L"MsiLogging", &pwzMsiLogging);
ExitOnFailure(hr, "failed to get MsiLogging property");
if (pwzMsiLogging)
{
for (int i = 0; pwzMsiLogging[i]; i++)
{
if (L'v' == pwzMsiLogging[i] || L'V' == pwzMsiLogging[i])
{
iVerbose = 1;
break;
}
}
}
// last chance: Check the registry to see if the logging policy was turned on
if (0 == iVerbose && IsVerboseLoggingPolicy())
{
iVerbose = 1;
}
}
}
LExit:
ReleaseStr(pwzMsiLogging);
Assert(iVerbose >= 0);
return (BOOL)iVerbose;
}
/********************************************************************
SetVerboseLoggingAtom() - Sets one of two global Atoms to specify
if the install should do verbose logging.
Communicates the verbose setting to
deferred CAs.
Set a negative case atom so that we can
distinguish between an unset atom and the
non-verbose case. This helps prevent the
expensive regkey lookup for non-verbose.
********************************************************************/
HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue)
{
HRESULT hr = S_OK;
ATOM atomVerbose = 0;
atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
if (0 == atomVerbose && bValue)
{
atomVerbose = ::GlobalAddAtomW(L"WcaVerboseLogging");
ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaVerboseLogging global atom.");
}
else if (0 != atomVerbose && !bValue)
{
::SetLastError(ERROR_SUCCESS);
::GlobalDeleteAtom(atomVerbose);
ExitOnLastError(hr, "Failed to delete WcaVerboseLogging global atom.");
}
atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
if (0 == atomVerbose && !bValue)
{
atomVerbose = ::GlobalAddAtomW(L"WcaNotVerboseLogging");
ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaNotVerboseLogging global atom.");
}
else if (0 != atomVerbose && bValue)
{
::SetLastError(ERROR_SUCCESS);
::GlobalDeleteAtom(atomVerbose);
ExitOnLastError(hr, "Failed to delete WcaNotVerboseLogging global atom.");
}
LExit:
return hr;
}
/********************************************************************
IsVerboseLoggingLite() - internal helper function to detect if atom was
previously set to specify verbose logging.
Falls back on policy for an installer that is
unable to set the atom (no immediate CAs).
Does not require database access.
********************************************************************/
static BOOL IsVerboseLoggingLite()
{
ATOM atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
if (0 != atomVerbose)
{
return TRUE;
}
atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
if (0 != atomVerbose)
{
return FALSE;
}
return IsVerboseLoggingPolicy();
}
/********************************************************************
WcaLog() - outputs trace and log info
*******************************************************************/
extern "C" void __cdecl WcaLog(
__in LOGLEVEL llv,
__in_z __format_string PCSTR fmt,
...
)
{
static char szFmt[LOG_BUFFER];
static char szBuf[LOG_BUFFER];
static bool fInLogPrint = false;
// prevent re-entrant logprints. (recursion issues between assert/logging code)
if (fInLogPrint)
return;
fInLogPrint = true;
if (LOGMSG_STANDARD == llv ||
(LOGMSG_VERBOSE == llv && IsVerboseLoggingLite())
#ifdef DEBUG
|| LOGMSG_TRACEONLY == llv
#endif
)
{
va_list args;
va_start(args, fmt);
LPCSTR szLogName = WcaGetLogName();
if (szLogName[0] != 0)
StringCchPrintfA(szFmt, countof(szFmt), "%s: %s", szLogName, fmt);
else
StringCchCopyA(szFmt, countof(szFmt), fmt);
StringCchVPrintfA(szBuf, countof(szBuf), szFmt, args);
va_end(args);
#ifdef DEBUG
// always write to the log in debug
#else
if (llv == LOGMSG_STANDARD || (llv == LOGMSG_VERBOSE && IsVerboseLoggingLite()))
#endif
{
PMSIHANDLE hrec = MsiCreateRecord(1);
::MsiRecordSetStringA(hrec, 0, szBuf);
// TODO: Recursion on failure. May not be safe to assert from here.
WcaProcessMessage(INSTALLMESSAGE_INFO, hrec);
}
#if DEBUG
StringCchCatA(szBuf, countof(szBuf), "\n");
OutputDebugStringA(szBuf);
#endif
}
fInLogPrint = false;
return;
}
/********************************************************************
WcaDisplayAssert() - called before Assert() dialog shows
NOTE: writes the assert string to the MSI log
********************************************************************/
extern "C" BOOL WIXAPI WcaDisplayAssert(
__in LPCSTR sz
)
{
WcaLog(LOGMSG_STANDARD, "Debug Assert Message: %s", sz);
return TRUE;
}
/********************************************************************
WcaLogError() - called before ExitOnXXX() macro exits the function
NOTE: writes the hresult and error string to the MSI log
********************************************************************/
extern "C" void WcaLogError(
__in HRESULT hr,
__in LPCSTR szMessage,
...
)
{
va_list dots;
va_start(dots, szMessage);
WcaLogErrorArgs(hr, szMessage, dots);
va_end(dots);
}
/********************************************************************
WcaLogErrorArgs() - called before ExitOnXXX() macro exits the function
NOTE: writes the hresult and error string to the MSI log
********************************************************************/
extern "C" void WcaLogErrorArgs(
__in HRESULT hr,
__in LPCSTR szMessage,
__in va_list args
)
{
char szBuffer[LOG_BUFFER];
StringCchVPrintfA(szBuffer, countof(szBuffer), szMessage, args);
// log the message if using Wca common layer
if (WcaIsInitialized())
{
WcaLog(LOGMSG_STANDARD, "Error 0x%x: %s", hr, szBuffer);
}
}
|