VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Installer/InstallHelper/VBoxGuestInstallHelper.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.7 KB
Line 
1/* $Id: VBoxGuestInstallHelper.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxGuestInstallHelper - Various helper routines for Windows guest installer.
4 * Works with NSIS 3.x.
5 */
6
7/*
8 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#ifndef UNICODE
34# define UNICODE
35#endif
36#include <iprt/win/windows.h>
37#include <iprt/win/commctrl.h>
38#include "exdll.h"
39
40#include <iprt/alloca.h>
41#include <iprt/errcore.h>
42#include <iprt/initterm.h>
43#include <iprt/ldr.h>
44#include <iprt/localipc.h>
45#include <iprt/mem.h>
46#include <iprt/process.h>
47#include <iprt/string.h>
48#include <iprt/utf16.h>
49
50/* Required structures/defines of VBoxTray. */
51#include "../../VBoxTray/VBoxTrayMsg.h"
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57#define VBOXINSTALLHELPER_EXPORT extern "C" DECLEXPORT(void)
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63typedef DWORD (WINAPI *PFNSFCFILEEXCEPTION)(DWORD param1, PWCHAR param2, DWORD param3);
64
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69static HINSTANCE g_hInstance;
70static HWND g_hwndParent;
71
72
73/**
74 * Frees a popped stack entry after use.
75 */
76DECLINLINE(void) vboxFreeStackEntry(stack_t *pEntry)
77{
78 if (pEntry)
79 GlobalFree((HGLOBAL)pEntry);
80}
81
82
83/**
84 * Allocates a new stack entry for containing a string of the given length
85 * (excluding terminator)
86 */
87DECLINLINE(stack_t *) vboxAllocStackEntry(size_t cwcString)
88{
89 return (stack_t *)GlobalAlloc(GPTR, RT_UOFFSETOF_DYN(stack_t, text[cwcString + 1]));
90}
91
92
93/**
94 * Pops an entry off the stack, return NULL if empty.
95 *
96 * Call vboxFreeStackEntry when done.
97 *
98 */
99DECLINLINE(stack_t *) vboxPopStack(stack_t **ppTopOfStack)
100{
101 stack_t *pEntry = ppTopOfStack ? *ppTopOfStack : NULL;
102 if (pEntry)
103 *ppTopOfStack = pEntry->next;
104 return pEntry;
105}
106
107
108/**
109 * Pushes an entry onto the stack.
110 */
111DECLINLINE(void) vboxPushStack(stack_t **ppTopOfStack, stack_t *pEntry)
112{
113 pEntry->next = *ppTopOfStack;
114 *ppTopOfStack = pEntry;
115}
116
117
118static void vboxPushUtf16N(stack_t **ppTopOfStack, wchar_t const *pwszString, size_t cwcString)
119{
120 stack_t *pEntry = vboxAllocStackEntry(cwcString);
121
122 memcpy(pEntry->text, pwszString, cwcString * sizeof(pEntry->text[0]));
123 pEntry->text[cwcString] = '\0';
124
125 vboxPushStack(ppTopOfStack, pEntry);
126}
127
128
129static void vboxPushUtf16(stack_t **ppTopOfStack, wchar_t const *pwszString)
130{
131 return vboxPushUtf16N(ppTopOfStack, pwszString, RTUtf16Len(pwszString));
132}
133
134
135#define VBOX_PUSH_STRING_LITERAL(a_ppTopOfStack, a_szLiteral) \
136 vboxPushUtf16N(a_ppTopOfStack, RT_CONCAT(L, a_szLiteral), sizeof(RT_CONCAT(L, a_szLiteral)) / sizeof(wchar_t) - 1)
137
138
139static void vboxPushUtf8(stack_t **ppTopOfStack, char const *pszString)
140{
141 size_t cwcUtf16 = RTStrCalcUtf16Len(pszString);
142 stack_t *pEntry = vboxAllocStackEntry(cwcUtf16);
143
144 PRTUTF16 pwszUtf16 = pEntry->text;
145 int rc = RTStrToUtf16Ex(pszString, RTSTR_MAX, &pwszUtf16, cwcUtf16 + 1, NULL);
146 AssertRC(rc);
147
148 vboxPushStack(ppTopOfStack, pEntry);
149}
150
151/**
152 * Pushes a string containing an error message and a VBox status code.
153 */
154static void vboxPushVBoxError(stack_t **ppTopOfStack, char const *pszString, int vrc)
155{
156 RTUTF16 wszTmp[128];
157 RTUtf16Printf(wszTmp, RT_ELEMENTS(wszTmp), "Error: %s! %Rrc", pszString, vrc);
158 vboxPushUtf16(ppTopOfStack, wszTmp);
159}
160
161
162static void vboxPushLastError(stack_t **ppTopOfStack, char const *pszString)
163{
164 DWORD const dwErr = GetLastError();
165 RTUTF16 wszTmp[128];
166 RTUtf16Printf(wszTmp, RT_ELEMENTS(wszTmp), "Error: %s! lasterr=%u (%#x)", pszString, dwErr, dwErr);
167 vboxPushUtf16(ppTopOfStack, wszTmp);
168}
169
170
171static void vboxPushLastErrorF(stack_t **ppTopOfStack, const char *pszFormat, ...)
172{
173 DWORD const dwErr = GetLastError();
174 RTUTF16 wszTmp[128];
175 va_list va;
176 va_start(va, pszFormat);
177 RTUtf16Printf(wszTmp, RT_ELEMENTS(wszTmp), "Error: %N! lasterr=%u (%#x)", pszFormat, &va, dwErr, dwErr);
178 va_end(va);
179 vboxPushUtf16(ppTopOfStack, wszTmp);
180}
181
182
183/**
184 * Convers a parameter to uint32_t.
185 *
186 * @returns IPRT status code.
187 * @param pwsz Will be trimmed.
188 * @param puValue Where to return the value.
189 */
190static int vboxUtf16ToUInt32(PRTUTF16 pwsz, uint32_t *puValue)
191{
192 *puValue = 0;
193
194 /* Trim the input: */
195 RTUTF16 wc;
196 while ((wc = *pwsz) == ' ' || wc == '\t')
197 pwsz++;
198 size_t cwc = RTUtf16Len(pwsz);
199 while (cwc > 0 && ((wc = pwsz[cwc - 1]) == ' ' || wc == '\t'))
200 pwsz[--cwc] = '\0';
201
202 /* Convert the remains into an UTF-8 string. */
203 char szValue[128];
204 char *pszValue = &szValue[0];
205 int rc = RTUtf16ToUtf8Ex(pwsz, cwc, &pszValue, sizeof(szValue), NULL);
206 if (RT_SUCCESS(rc))
207 rc = RTStrToUInt32Full(pszValue, 0, puValue);
208 return rc;
209}
210
211
212/**
213 * Connects to VBoxTray IPC under the behalf of the user running in the current
214 * thread context.
215 *
216 * @return IPRT status code.
217 * @param phSession Where to store the IPC session.
218 */
219static int vboxConnectToVBoxTray(RTLOCALIPCSESSION *phSession)
220{
221 char szPipeName[512 + sizeof(VBOXTRAY_IPC_PIPE_PREFIX)];
222 memcpy(szPipeName, VBOXTRAY_IPC_PIPE_PREFIX, sizeof(VBOXTRAY_IPC_PIPE_PREFIX));
223 int rc = RTProcQueryUsername(NIL_RTPROCESS,
224 &szPipeName[sizeof(VBOXTRAY_IPC_PIPE_PREFIX) - 1],
225 sizeof(szPipeName) - sizeof(VBOXTRAY_IPC_PIPE_PREFIX) + 1,
226 NULL /*pcbUser*/);
227 if (RT_SUCCESS(rc))
228 rc = RTLocalIpcSessionConnect(phSession, szPipeName, RTLOCALIPC_FLAGS_NATIVE_NAME);
229 return rc;
230}
231
232
233/**
234 * Retrieves a file's architecture (x86 or amd64).
235 *
236 * Outputs "x86", "amd64" or an error message (if not found/invalid) on stack.
237 *
238 * @param hwndParent Window handle of parent.
239 * @param string_size Size of variable string.
240 * @param variables The actual variable string.
241 * @param stacktop Pointer to a pointer to the current stack.
242 * @param extra Extra parameters.
243 */
244VBOXINSTALLHELPER_EXPORT FileGetArchitecture(HWND hwndParent, int string_size, WCHAR *variables, stack_t **stacktop,
245 extra_parameters *extra)
246{
247 RT_NOREF(hwndParent, string_size, variables, extra);
248
249 stack_t *pEntry = vboxPopStack(stacktop);
250 if (pEntry)
251 {
252 char *pszFileUtf8;
253 int rc = RTUtf16ToUtf8(pEntry->text, &pszFileUtf8);
254 if (RT_SUCCESS(rc))
255 {
256 RTLDRMOD hLdrMod;
257 rc = RTLdrOpen(pszFileUtf8, RTLDR_O_FOR_VALIDATION, RTLDRARCH_WHATEVER, &hLdrMod);
258 if (RT_SUCCESS(rc))
259 {
260 if (RTLdrGetFormat(hLdrMod) == RTLDRFMT_PE)
261 {
262 RTLDRARCH enmLdrArch = RTLdrGetArch(hLdrMod);
263 switch (enmLdrArch)
264 {
265 case RTLDRARCH_X86_32:
266 VBOX_PUSH_STRING_LITERAL(stacktop, "x86");
267 break;
268
269 case RTLDRARCH_AMD64:
270 VBOX_PUSH_STRING_LITERAL(stacktop, "amd64");
271 break;
272
273 default:
274 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Unknown / invalid architecture");
275 break;
276 }
277 }
278 else
279 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Unknown / invalid PE signature");
280
281 RTLdrClose(hLdrMod);
282 }
283 else
284 vboxPushVBoxError(stacktop, "RTLdrOpen failed", rc);
285 RTStrFree(pszFileUtf8);
286 }
287 else
288 vboxPushVBoxError(stacktop, "RTUtf16ToUtf8 failed", rc);
289 }
290 else
291 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Could not retrieve file name");
292 vboxFreeStackEntry(pEntry);
293}
294
295/**
296 * Retrieves a file's vendor.
297 *
298 * Outputs the vendor's name or an error message (if not found/invalid) on stack.
299 *
300 * @param hwndParent Window handle of parent.
301 * @param string_size Size of variable string.
302 * @param variables The actual variable string.
303 * @param stacktop Pointer to a pointer to the current stack.
304 * @param extra Extra parameters.
305 */
306VBOXINSTALLHELPER_EXPORT FileGetVendor(HWND hwndParent, int string_size, WCHAR *variables, stack_t **stacktop,
307 extra_parameters *extra)
308{
309 RT_NOREF(hwndParent, string_size, variables, extra);
310
311 stack_t *pEntry = vboxPopStack(stacktop);
312 if (pEntry)
313 {
314 DWORD dwInfoSize = GetFileVersionInfoSizeW(pEntry->text, NULL /* lpdwHandle */);
315 if (dwInfoSize)
316 {
317 void *pvFileInfo = GlobalAlloc(GMEM_FIXED, dwInfoSize);
318 if (pvFileInfo)
319 {
320 if (GetFileVersionInfoW(pEntry->text, 0, dwInfoSize, pvFileInfo))
321 {
322 LPVOID pvInfo;
323 UINT cbInfo;
324 if (VerQueryValueW(pvFileInfo, L"\\VarFileInfo\\Translation", &pvInfo, &cbInfo))
325 {
326 WORD wCodePage = LOWORD(*(DWORD const *)pvInfo);
327 WORD wLanguageID = HIWORD(*(DWORD const *)pvInfo);
328
329 WCHAR wszQuery[80];
330 RTUtf16Printf(wszQuery, RT_ELEMENTS(wszQuery), "StringFileInfo\\%04X%04X\\CompanyName",
331 wCodePage, wLanguageID);
332
333 LPCWSTR pwszData;
334 if (VerQueryValueW(pvFileInfo, wszQuery, (void **)&pwszData, &cbInfo))
335 vboxPushUtf16(stacktop, pwszData);
336 else
337 vboxPushLastErrorF(stacktop, "VerQueryValueW '%ls' failed", wszQuery);
338 }
339 else
340 vboxPushLastError(stacktop, "VerQueryValueW '\\VarFileInfo\\Translation' failed");
341 }
342 else
343 vboxPushLastError(stacktop, "GetFileVersionInfo failed");
344 GlobalFree(pvFileInfo);
345 }
346 else
347 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: GlobalAlloc failed");
348 }
349 else
350 vboxPushLastError(stacktop, "GetFileVersionInfoSizeW failed");
351 }
352 else
353 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Could not retrieve file name");
354 vboxFreeStackEntry(pEntry);
355}
356
357/**
358 * Shows a balloon message using VBoxTray's notification area in the
359 * Windows task bar.
360 *
361 * @param hwndParent Window handle of parent.
362 * @param string_size Size of variable string.
363 * @param variables The actual variable string.
364 * @param stacktop Pointer to a pointer to the current stack.
365 * @param extra Extra parameters.
366 */
367VBOXINSTALLHELPER_EXPORT VBoxTrayShowBallonMsg(HWND hwndParent, int string_size, WCHAR *variables, stack_t **stacktop,
368 extra_parameters *extra)
369{
370 RT_NOREF(hwndParent, string_size, variables, extra);
371
372 /*
373 * Get parameters from the stack.
374 */
375 stack_t * const pMsgEntry = vboxPopStack(stacktop);
376 stack_t * const pTitleEntry = vboxPopStack(stacktop);
377 stack_t * const pTypeEntry = vboxPopStack(stacktop);
378 stack_t * const pTimeoutEntry = vboxPopStack(stacktop);
379 if (pTimeoutEntry)
380 {
381 /*
382 * Allocate an IPC message payload structure of the right size.
383 */
384 size_t const cchMsg = RTUtf16CalcUtf8Len(pMsgEntry->text);
385 size_t const cchTitle = RTUtf16CalcUtf8Len(pTitleEntry->text);
386 size_t const cbPayload = RT_UOFFSETOF_DYN(VBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T, szzStrings[cchMsg + 1 + cchTitle + 1]);
387 PVBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T pPayload = (PVBOXTRAYIPCMSG_SHOW_BALLOON_MSG_T)RTMemAllocZVar(cbPayload);
388 if (pPayload)
389 {
390 VBOXTRAYIPCHEADER const MsgHdr =
391 {
392 VBOXTRAY_IPC_HDR_MAGIC,
393 VBOXTRAY_IPC_HDR_VERSION,
394 VBOXTRAYIPCMSGTYPE_SHOW_BALLOON_MSG,
395 cbPayload
396 };
397
398 /*
399 * Convert the parametes and put them into the payload structure.
400 */
401 pPayload->cchMsg = cchMsg;
402 pPayload->cchTitle = cchTitle;
403 char *psz = &pPayload->szzStrings[0];
404 int rc = RTUtf16ToUtf8Ex(pMsgEntry->text, RTSTR_MAX, &psz, cchMsg + 1, NULL);
405 if (RT_SUCCESS(rc))
406 {
407 psz = &pPayload->szzStrings[cchMsg + 1];
408 rc = RTUtf16ToUtf8Ex(pTitleEntry->text, RTSTR_MAX, &psz, cchTitle + 1, NULL);
409 if (RT_SUCCESS(rc))
410 {
411 rc = vboxUtf16ToUInt32(pTypeEntry->text, &pPayload->uType);
412 if (RT_SUCCESS(rc))
413 {
414 rc = vboxUtf16ToUInt32(pTypeEntry->text, &pPayload->cMsTimeout);
415 if (RT_SUCCESS(rc))
416 {
417 /*
418 * Connect to VBoxTray and send the message.
419 */
420 RTLOCALIPCSESSION hSession = 0;
421 rc = vboxConnectToVBoxTray(&hSession);
422 if (RT_SUCCESS(rc))
423 {
424 rc = RTLocalIpcSessionWrite(hSession, &MsgHdr, sizeof(MsgHdr));
425 if (RT_SUCCESS(rc))
426 {
427 rc = RTLocalIpcSessionWrite(hSession, pPayload, cbPayload);
428 if (RT_FAILURE(rc))
429 vboxPushVBoxError(stacktop, "Failed to write message payload", rc);
430 }
431 else
432 vboxPushVBoxError(stacktop, "Failed to write message header", rc);
433
434 int rc2 = RTLocalIpcSessionClose(hSession);
435 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
436 {
437 vboxPushVBoxError(stacktop, "RTLocalIpcSessionClose failed", rc);
438 rc = rc2;
439 }
440 }
441 else
442 vboxPushVBoxError(stacktop, "vboxConnectToVBoxTray failed", rc);
443 }
444 else
445 vboxPushVBoxError(stacktop, "MyUtf16ToUInt32 failed on the timeout value", rc);
446 }
447 else
448 vboxPushVBoxError(stacktop, "MyUtf16ToUInt32 failed on the type value", rc);
449 }
450 else
451 vboxPushVBoxError(stacktop, "RTUtf16ToUtf8Ex failed on the title text", rc);
452 }
453 else
454 vboxPushVBoxError(stacktop, "RTUtf16ToUtf8Ex failed on the message text", rc);
455 }
456 else
457 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Out of memory!");
458 }
459 else
460 VBOX_PUSH_STRING_LITERAL(stacktop, "Error: Too few parameters on the stack!");
461 vboxFreeStackEntry(pTimeoutEntry);
462 vboxFreeStackEntry(pTypeEntry);
463 vboxFreeStackEntry(pTitleEntry);
464 vboxFreeStackEntry(pMsgEntry);
465}
466
467/**
468 * Dumps the UI log to a file in UTF-8 format.
469 *
470 * Does not return any values on the stack.
471 *
472 * @param hWndParent Window handle of parent.
473 * @param string_size Size of variable string.
474 * @param variables The actual variable string.
475 * @param stacktop Pointer to a pointer to the current stack.
476 * @param extra Extra parameters.
477 */
478VBOXINSTALLHELPER_EXPORT DumpLog(HWND hWndParent, int string_size, WCHAR *variables, stack_t **stacktop,
479 extra_parameters *extra)
480{
481 RT_NOREF(string_size, variables, extra);
482
483 /*
484 * Get parameters from the stack.
485 */
486 stack_t * const pFilename = vboxPopStack(stacktop);
487 if (pFilename)
488 {
489 /*
490 * Open the output file.
491 */
492 HANDLE hFile = CreateFileW(pFilename->text, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttr*/, CREATE_ALWAYS,
493 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
494 if (hFile != NULL)
495 {
496 DWORD dwIgn;
497
498 /*
499 * Locate the list view widget.
500 */
501 HWND hWndDialog = FindWindowExW(hWndParent, NULL /*hWndChildAfter*/, L"#32770" /*pwszClass*/, NULL /*pwszTitle*/);
502 if (hWndDialog)
503 {
504 HWND hWndList = FindWindowExW(hWndDialog, NULL /*hWndChildAfter*/, L"SysListView32", NULL /*pwszTitle*/);
505 if (hWndList != NULL)
506 {
507 uint32_t const cLines = (uint32_t)SendMessageW(hWndList, LVM_GETITEMCOUNT, 0, 0);
508 if (cLines > 0)
509 {
510 /* Allocate a buffer for retriving the text. */
511 uint32_t cwcBuf = RT_MAX(string_size + 16, _8K);
512 wchar_t *pwszBuf = (wchar_t *)RTMemTmpAlloc(cwcBuf * sizeof(wchar_t));
513 wchar_t * const pwszBufFree = pwszBuf;
514 if (!pwszBuf)
515 {
516 cwcBuf = _4K;
517 pwszBuf = (wchar_t *)alloca(cwcBuf * sizeof(wchar_t));
518 }
519
520 /*
521 * Retreive the lines and write them to the output file.
522 */
523 for (uint32_t iLine = 0; iLine < cLines; iLine++)
524 {
525 LVITEMW Item =
526 {
527 /* .mask = */ 0,
528 /* .iItem = */ (int)iLine,
529 /* .iSubItem = */ 0,
530 /* .state = */ 0,
531 /* .stateMask = */ 0,
532 /* .pszText = */ pwszBuf,
533 /* .cchTextMax = */ (int)cwcBuf,
534 };
535 uint32_t const cwcRet = (uint32_t)SendMessageW(hWndList, LVM_GETITEMTEXT, iLine, (LPARAM)&Item);
536 if (cwcRet < cwcBuf)
537 {
538 pwszBuf[cwcRet] = '\0';
539 bool fNeedsNewline = cwcRet + 2 >= cwcBuf;
540 if (!fNeedsNewline)
541 {
542 pwszBuf[cwcRet + 0] = '\r';
543 pwszBuf[cwcRet + 1] = '\n';
544 pwszBuf[cwcRet + 2] = '\0';
545 }
546
547 char *pszUtf8;
548 int rc = RTUtf16ToUtf8(pwszBuf, &pszUtf8);
549 if (RT_SUCCESS(rc))
550 {
551 WriteFile(hFile, pszUtf8, strlen(pszUtf8), &dwIgn, NULL);
552 if (fNeedsNewline)
553 WriteFile(hFile, RT_STR_TUPLE("\r\n"), &dwIgn, NULL);
554 RTStrFree(pszUtf8);
555 }
556 else
557 WriteFile(hFile, RT_STR_TUPLE("!RTUtf16ToUtf8 failed!\r\n"), &dwIgn, NULL);
558 }
559 else
560 WriteFile(hFile, RT_STR_TUPLE("!LVM_GETITEMTEXT overflow!\r\n"), &dwIgn, NULL);
561 } /* for loop*/
562
563 RTMemTmpFree(pwszBufFree);
564 }
565 else
566 WriteFile(hFile, RT_STR_TUPLE("Log is empty.\r\n"), &dwIgn, NULL);
567 }
568 else
569 WriteFile(hFile, RT_STR_TUPLE("FindWindowEx failed to locate the log control!\r\n"), &dwIgn, NULL);
570 }
571 else
572 WriteFile(hFile, RT_STR_TUPLE("FindWindowEx failed to locate dialog windows!\r\n"), &dwIgn, NULL);
573 CloseHandle(hFile);
574 }
575 }
576 vboxFreeStackEntry(pFilename);
577}
578
579BOOL WINAPI DllMain(HANDLE hInst, ULONG uReason, LPVOID pReserved)
580{
581 RT_NOREF(pReserved);
582
583 g_hInstance = (HINSTANCE)hInst;
584
585 switch (uReason)
586 {
587 case DLL_PROCESS_ATTACH:
588 RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE);
589 break;
590
591 case DLL_PROCESS_DETACH:
592 break;
593
594 case DLL_THREAD_ATTACH:
595 break;
596
597 case DLL_THREAD_DETACH:
598 break;
599
600 default:
601 break;
602 }
603
604 return TRUE;
605}
606
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use