VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxGINA/Dialog.cpp@ 95890

最後變更 在這個檔案從95890是 95874,由 vboxsync 提交於 3 年 前

Add/Nt/VBoxGINA: Made it build in VBOX_WITH_NOCRT_STATIC mode. bugref:10261

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 21.6 KB
 
1/* $Id: Dialog.cpp 95874 2022-07-27 08:01:09Z vboxsync $ */
2/** @file
3 * VBoxGINA - Windows Logon DLL for VirtualBox, Dialog Code.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#include <iprt/win/windows.h>
22
23#include <VBox/VBoxGuestLib.h>
24#include <iprt/errcore.h>
25#include <iprt/utf16.h>
26
27#include "Dialog.h"
28#include "WinWlx.h"
29#include "Helper.h"
30#include "VBoxGINA.h"
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36/*
37 * Dialog IDs for legacy Windows OSes (e.g. NT 4.0).
38 */
39#define IDD_WLXDIAPLAYSASNOTICE_DIALOG 1400
40#define IDD_WLXLOGGEDOUTSAS_DIALOG 1450
41/** Change password dialog: To change the current
42 * account password. */
43#define IDD_CHANGE_PASSWORD_DIALOG 1550
44#define IDD_WLXLOGGEDONSAS_DIALOG 1650
45/** Security dialog: To lock the workstation, log off
46 * change password, ... */
47#define IDD_SECURITY_DIALOG 1800
48/** Locked dialog: To unlock the currently lockted
49 * workstation. */
50#define IDD_WLXWKSTALOCKEDSAS_DIALOG 1850
51/** Shutdown dialog: To either restart, logoff current
52 * user or shutdown the workstation. */
53#define IDD_SHUTDOWN_DIALOG 2200
54/** Logoff dialog: "Do you really want to logoff?". */
55#define IDD_LOGOFF_DIALOG 2250
56
57
58/*
59 * Dialog IDs for Windows 2000 and up.
60 */
61#define IDD_WLXLOGGEDOUTSAS_DIALOG2 1500
62/** Change password dialog: To change the current
63 * account password. */
64#define IDD_CHANGE_PASSWORD_DIALOG2 1700
65/** Locked dialog: To unlock the currently lockted
66 * workstation. */
67#define IDD_WLXWKSTALOCKEDSAS_DIALOG2 1950
68
69
70/*
71 * Control IDs.
72 */
73#define IDC_WLXLOGGEDOUTSAS_USERNAME 1453
74#define IDC_WLXLOGGEDOUTSAS_USERNAME2 1502
75#define IDC_WLXLOGGEDOUTSAS_PASSWORD 1454
76#define IDC_WLXLOGGEDOUTSAS_PASSWORD2 1503
77#define IDC_WLXLOGGEDOUTSAS_DOMAIN 1455
78#define IDC_WLXLOGGEDOUTSAS_DOMAIN2 1504
79
80#define IDC_WKSTALOCKED_USERNAME 1953
81#define IDC_WKSTALOCKED_PASSWORD 1954
82#define IDC_WKSTALOCKEd_DOMAIN 1856
83#define IDC_WKSTALOCKED_DOMAIN2 1956
84
85
86/*
87 * Own IDs.
88 */
89#define IDT_BASE WM_USER + 1100 /* Timer ID base. */
90#define IDT_LOGGEDONDLG_POLL IDT_BASE + 1
91#define IDT_LOCKEDDLG_POLL IDT_BASE + 2
92
93
94/*********************************************************************************************************************************
95* Global Variables *
96*********************************************************************************************************************************/
97static DLGPROC g_pfnWlxLoggedOutSASDlgProc = NULL;
98static DLGPROC g_pfnWlxLockedSASDlgProc = NULL;
99
100static PWLX_DIALOG_BOX_PARAM g_pfnWlxDialogBoxParam = NULL;
101
102
103/*********************************************************************************************************************************
104* Internal Functions *
105*********************************************************************************************************************************/
106int WINAPI MyWlxDialogBoxParam (HANDLE, HANDLE, LPWSTR, HWND, DLGPROC, LPARAM);
107
108
109void hookDialogBoxes(PVOID pWinlogonFunctions, DWORD dwWlxVersion)
110{
111 if (!pWinlogonFunctions) /* Needed for testcase. */
112 return;
113
114 VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes\n");
115
116 /* this is version dependent */
117 switch (dwWlxVersion)
118 {
119 case WLX_VERSION_1_0:
120 {
121 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam;
122 ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
123 break;
124 }
125
126 case WLX_VERSION_1_1:
127 {
128 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam;
129 ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
130 break;
131 }
132
133 case WLX_VERSION_1_2:
134 {
135 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam;
136 ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
137 break;
138 }
139
140 case WLX_VERSION_1_3:
141 {
142 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam;
143 ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
144 break;
145 }
146
147 case WLX_VERSION_1_4:
148 {
149 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam;
150 ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
151 break;
152 }
153
154 default:
155 {
156 VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes: unrecognized version '%d', nothing hooked!\n", dwWlxVersion);
157 /* not good, don't do anything */
158 break;
159 }
160 }
161}
162
163/**
164 * Enters credentials into the given text fields.
165 *
166 * @return IPRT status code.
167 * @param hwndDlg Handle of dialog to enter credentials into.
168 * @param hwndUserId Handle of username text field. Optional.
169 * @param hwndPassword Handle of password text field. Optional.
170 * @param hwndDomain Handle of domain text field. Optional.
171 * @param pwszUser Username to enter into username text field.
172 * @param pwszPassword Password to enter into password text field.
173 * @param pwszDomain Domain to enter into domain text field.
174 */
175int credentialsToUI(HWND hwndDlg,
176 HWND hwndUserId, HWND hwndPassword, HWND hwndDomain,
177 PCRTUTF16 pwszUser, PCRTUTF16 pwszPassword, PCRTUTF16 pwszDomain)
178{
179 RT_NOREF(hwndDlg);
180 BOOL bIsFQDN = FALSE;
181 wchar_t szUserFQDN[512]; /* VMMDEV_CREDENTIALS_STRLEN + 255 bytes max. for FQDN */
182 if (hwndDomain)
183 {
184 /* search the domain combo box for our required domain and select it */
185 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Trying to find domain entry in combo box ...\n");
186 DWORD dwIndex = (DWORD) SendMessage(hwndDomain, CB_FINDSTRING,
187 0, (LPARAM)pwszDomain);
188 if (dwIndex != CB_ERR)
189 {
190 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Found domain at combo box pos %ld\n", dwIndex);
191 SendMessage(hwndDomain, CB_SETCURSEL, (WPARAM) dwIndex, 0);
192 EnableWindow(hwndDomain, FALSE);
193 }
194 else
195 {
196 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain not found in combo box ...\n");
197
198 /* If the domain value has a dot (.) in it, it is a FQDN (Fully Qualified Domain Name)
199 * which will not work with the combo box selection because Windows only keeps the
200 * NETBIOS names to the left most part of the domain name there. Of course a FQDN
201 * then will not be found by the search in the block above.
202 *
203 * To solve this problem the FQDN domain value will be appended at the user name value
204 * (Kerberos style) using an "@", e.g. "<user-name>@full.qualified.domain".
205 *
206 */
207 size_t l = RTUtf16Len(pwszDomain);
208 if (l > 255)
209 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Warning! FQDN (domain) is too long (max 255 bytes), will be truncated!\n");
210
211 if (*pwszUser) /* We need a user name that we can use in caes of a FQDN */
212 {
213 if (l > 16) /* Domain name is longer than 16 chars, cannot be a NetBIOS name anymore */
214 {
215 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (length)!\n");
216 bIsFQDN = TRUE;
217 }
218 else if ( l > 0
219 && RTUtf16Chr(pwszDomain, L'.') != NULL) /* if we found a dot (.) in the domain name, this has to be a FQDN */
220 {
221 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (dot)!\n");
222 bIsFQDN = TRUE;
223 }
224
225 if (bIsFQDN)
226 {
227 RTUtf16Printf(szUserFQDN, sizeof(szUserFQDN) / sizeof(wchar_t), "%ls@%ls", pwszUser, pwszDomain);
228 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: FQDN user name is now: %s!\n", szUserFQDN);
229 }
230 }
231 }
232 }
233 if (hwndUserId)
234 {
235 if (!bIsFQDN)
236 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)pwszUser);
237 else
238 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)szUserFQDN);
239 }
240 if (hwndPassword)
241 SendMessage(hwndPassword, WM_SETTEXT, 0, (LPARAM)pwszPassword);
242
243 return VINF_SUCCESS; /** @todo */
244}
245
246/**
247 * Tries to retrieve credentials and enters them into the specified windows,
248 * optionally followed by a button press to confirm/abort the dialog.
249 *
250 * @return IPRT status code.
251 * @param hwndDlg Handle of dialog to enter credentials into.
252 * @param hwndUserId Handle of username text field. Optional.
253 * @param hwndPassword Handle of password text field. Optional.
254 * @param hwndDomain Handle of domain text field. Optional.
255 * @param wButtonToPress Button ID of dialog to press after successful
256 * retrieval + storage. If set to 0 no button will
257 * be pressed.
258 */
259int credentialsHandle(HWND hwndDlg,
260 HWND hwndUserId, HWND hwndPassword, HWND hwndDomain,
261 WORD wButtonToPress)
262{
263 int rc = VINF_SUCCESS;
264
265 if (!VBoxGINAHandleCurrentSession())
266 rc = VERR_NOT_FOUND;
267
268 if (RT_SUCCESS(rc))
269 {
270 rc = VbglR3CredentialsQueryAvailability();
271 if (RT_FAILURE(rc))
272 {
273 if (rc != VERR_NOT_FOUND)
274 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: error querying for credentials, rc=%Rrc\n", rc);
275 }
276 }
277
278 if (RT_SUCCESS(rc))
279 {
280 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: credentials available\n");
281
282 /*
283 * Set status to "terminating" to let the host know this module now
284 * tries to receive and use passed credentials so that credentials from
285 * the host won't be sent twice.
286 */
287 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminating);
288
289 PRTUTF16 pwszUser, pwszPassword, pwszDomain;
290 rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain);
291 if (RT_SUCCESS(rc))
292 {
293#ifdef DEBUG
294 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=%ls, domain=%ls\n",
295 pwszUser, pwszPassword, pwszDomain);
296#else
297 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=XXX, domain=%ls\n",
298 pwszUser, pwszDomain);
299#endif
300 /* Fill in credentials to appropriate UI elements. */
301 rc = credentialsToUI(hwndDlg,
302 hwndUserId, hwndPassword, hwndDomain,
303 pwszUser, pwszPassword, pwszDomain);
304 if (RT_SUCCESS(rc))
305 {
306 /* Confirm/cancel the dialog by pressing the appropriate button. */
307 if (wButtonToPress)
308 {
309 WPARAM wParam = MAKEWPARAM(wButtonToPress, BN_CLICKED);
310 PostMessage(hwndDlg, WM_COMMAND, wParam, 0);
311 }
312 }
313
314 VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain,
315 3 /* Passes */);
316 }
317 }
318
319#ifdef DEBUG
320 VBoxGINAVerbose(3, "VBoxGINA::credentialsHandle: returned with rc=%Rrc\n", rc);
321#endif
322 return rc;
323}
324
325INT_PTR CALLBACK MyWlxLoggedOutSASDlgProc(HWND hwndDlg, // handle to dialog box
326 UINT uMsg, // message
327 WPARAM wParam, // first message parameter
328 LPARAM lParam) // second message parameter
329{
330 BOOL bResult;
331 static HWND s_hwndUserId, s_hwndPassword, s_hwndDomain = 0;
332
333 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc\n");*/
334
335 //
336 // Pass on to MSGINA first.
337 //
338 bResult = g_pfnWlxLoggedOutSASDlgProc(hwndDlg, uMsg, wParam, lParam);
339
340 //
341 // We are only interested in the WM_INITDIALOG message.
342 //
343 switch (uMsg)
344 {
345 case WM_INITDIALOG:
346 {
347 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: got WM_INITDIALOG\n");
348
349 /* get the entry fields */
350 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME);
351 if (!s_hwndUserId)
352 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME2);
353 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD);
354 if (!s_hwndPassword)
355 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD2);
356 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN);
357 if (!s_hwndDomain)
358 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN2);
359
360 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: hwndUserId: %x, hwndPassword: %d, hwndDomain: %d\n",
361 s_hwndUserId, s_hwndPassword, s_hwndDomain);
362
363 /* terminate the credentials poller thread, it's done is job */
364 VBoxGINACredentialsPollerTerminate();
365
366 int rc = credentialsHandle(hwndDlg,
367 s_hwndUserId, s_hwndPassword, s_hwndDomain,
368 IDOK /* Button */);
369 if (RT_FAILURE(rc))
370 {
371 /*
372 * The dialog is there but we don't have any credentials.
373 * Create a timer and poll for them.
374 */
375 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOGGEDONDLG_POLL, 200, NULL);
376 if (!uTimer)
377 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: failed creating timer! Last error: %ld\n",
378 GetLastError());
379 }
380 break;
381 }
382
383 case WM_TIMER:
384 {
385 /* is it our credentials poller timer? */
386 if (wParam == IDT_LOGGEDONDLG_POLL)
387 {
388 int rc = credentialsHandle(hwndDlg,
389 s_hwndUserId, s_hwndPassword, s_hwndDomain,
390 IDOK /* Button */);
391 if (RT_SUCCESS(rc))
392 {
393 /* we don't need the timer any longer */
394 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
395 }
396 }
397 break;
398 }
399
400 case WM_DESTROY:
401 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
402 break;
403 }
404 return bResult;
405}
406
407
408INT_PTR CALLBACK MyWlxLockedSASDlgProc(HWND hwndDlg, // handle to dialog box
409 UINT uMsg, // message
410 WPARAM wParam, // first message parameter
411 LPARAM lParam) // second message parameter
412{
413 BOOL bResult;
414 static HWND s_hwndPassword = 0;
415
416 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc\n");*/
417
418 //
419 // Pass on to MSGINA first.
420 //
421 bResult = g_pfnWlxLockedSASDlgProc(hwndDlg, uMsg, wParam, lParam);
422
423 //
424 // We are only interested in the WM_INITDIALOG message.
425 //
426 switch (uMsg)
427 {
428 case WM_INITDIALOG:
429 {
430 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_INITDIALOG\n");
431
432 /* get the entry fields */
433 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WKSTALOCKED_PASSWORD);
434 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: hwndPassword: %d\n", s_hwndPassword);
435
436 /* terminate the credentials poller thread, it's done is job */
437 VBoxGINACredentialsPollerTerminate();
438
439 int rc = credentialsHandle(hwndDlg,
440 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
441 IDOK /* Button */);
442 if (RT_FAILURE(rc))
443 {
444 /*
445 * The dialog is there but we don't have any credentials.
446 * Create a timer and poll for them.
447 */
448 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOCKEDDLG_POLL, 200, NULL);
449 if (!uTimer)
450 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: failed creating timer! Last error: %ld\n",
451 GetLastError());
452 }
453 break;
454 }
455
456 case WM_TIMER:
457 {
458 /* is it our credentials poller timer? */
459 if (wParam == IDT_LOCKEDDLG_POLL)
460 {
461 int rc = credentialsHandle(hwndDlg,
462 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
463 IDOK /* Button */);
464 if (RT_SUCCESS(rc))
465 {
466 /* we don't need the timer any longer */
467 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
468 }
469 }
470 break;
471 }
472
473 case WM_DESTROY:
474 {
475 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_DESTROY\n");
476
477 /* Because this is the only point where we know within our module that the locked
478 * dialog has been closed by a valid unlock password we have to set the appropriate
479 * facility status here. */
480 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminated);
481
482 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
483 break;
484 }
485 }
486 return bResult;
487}
488
489
490int WINAPI MyWlxDialogBoxParam(HANDLE hWlx,
491 HANDLE hInst,
492 LPWSTR pszTemplate,
493 HWND hwndOwner,
494 DLGPROC dlgprc,
495 LPARAM dwInitParam)
496{
497 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: pszTemplate=%ls\n", pszTemplate);
498
499 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Active);
500
501 //
502 // We only know MSGINA dialogs by identifiers.
503 //
504 if (((uintptr_t)pszTemplate >> 16) == 0)
505 {
506 //
507 // Hook appropriate dialog boxes as necessary.
508 //
509 switch ((DWORD)(uintptr_t)pszTemplate)
510 {
511 case IDD_WLXDIAPLAYSASNOTICE_DIALOG:
512 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: SAS notice dialog displayed; not handled\n");
513 break;
514
515 case IDD_WLXLOGGEDOUTSAS_DIALOG: /* Windows NT 4.0. */
516 case IDD_WLXLOGGEDOUTSAS_DIALOG2: /* Windows 2000 and up. */
517 {
518 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS logged out dialog\n");
519 g_pfnWlxLoggedOutSASDlgProc = dlgprc;
520 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner,
521 MyWlxLoggedOutSASDlgProc, dwInitParam);
522 }
523
524 case IDD_SECURITY_DIALOG:
525 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: Security dialog displayed; not handled\n");
526 break;
527
528 case IDD_WLXWKSTALOCKEDSAS_DIALOG: /* Windows NT 4.0. */
529 case IDD_WLXWKSTALOCKEDSAS_DIALOG2: /* Windows 2000 and up. */
530 {
531 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS locked dialog\n");
532 g_pfnWlxLockedSASDlgProc = dlgprc;
533 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner,
534 MyWlxLockedSASDlgProc, dwInitParam);
535 }
536
537 /** @todo Add other hooking stuff here. */
538
539 default:
540 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: dialog %p (%u) not handled\n",
541 pszTemplate, (DWORD)(uintptr_t)pszTemplate);
542 break;
543 }
544 }
545
546 /* The rest will be redirected. */
547 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner, dlgprc, dwInitParam);
548}
549
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette