Windows: fix checked UNICODE build of talocale
[openafs.git] / src / WINNT / afssvrcfg / afscfg.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * INCLUDES _________________________________________________________________
12  *
13  */
14
15 #include <winsock2.h>
16 #include <ws2tcpip.h>
17
18 extern "C" {
19 #include <afsconfig.h>
20 #include <afs/param.h>
21 #include <afs/stds.h>
22 #include <roken.h>
23 }
24
25 #include "afscfg.h"
26 #include "resource.h"
27 #include "graphics.h"
28 #include <string.h>
29 #include "get_cur_config.h"
30 #include "partition_utils.h"
31 extern "C" {
32 #include <afs\dirpath.h>
33 #include <afs\afs_clientAdmin.h>
34 }
35 #include <afs\lanahelper.h>
36
37
38 /*
39  * DEFINITIONS _________________________________________________________________
40  *
41  */
42
43
44 /*
45  * PROTOTYPES _________________________________________________________________
46  *
47  */
48 static void SetConfigDefaults();
49 static void RunCfgTool();
50 static void RunWizard();
51 static void CloseLibHandles(BOOL bExiting = FALSE);
52
53 void RegisterWizardHelp();
54 void RegisterConfigToolHelp();
55
56 // These are the prototypes for the dialog procs of each wizard page.
57 BOOL CALLBACK IntroPageDlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp);
58 BOOL CALLBACK InfoPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
59 BOOL CALLBACK InfoPage2DlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp);
60 BOOL CALLBACK FileServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
61 BOOL CALLBACK DBServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
62 BOOL CALLBACK BackupPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
63 BOOL CALLBACK PartitionPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
64 BOOL CALLBACK RootAfsPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
65 BOOL CALLBACK ReplicationPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
66 BOOL CALLBACK SysControlPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
67 BOOL CALLBACK ConfigServerPageDlgProc(HWND hwndDlg, UINT msg, WPARAM wp, LPARAM lp);
68
69
70 static WIZARD_STATE g_aStates[] = {
71     { sidSTEP_ONE,      IDD_INTRO_PAGE,                 (DLGPROC)IntroPageDlgProc, 0 },
72     { sidSTEP_TWO,      IDD_INFO_PAGE,                  (DLGPROC)InfoPageDlgProc, 0 },
73     { sidSTEP_THREE,    IDD_INFO_PAGE2_FIRST_SERVER,    (DLGPROC)InfoPage2DlgProc, 0 },
74     { sidSTEP_FOUR,     IDD_INFO_PAGE2_NOT_FIRST_SERVER,(DLGPROC)InfoPage2DlgProc, 0 },
75     { sidSTEP_FIVE,     IDD_FILE_SERVER_PAGE,           (DLGPROC)FileServerPageDlgProc, 0 },
76     { sidSTEP_SIX,      IDD_DB_SERVER_PAGE,             (DLGPROC)DBServerPageDlgProc, 0 },
77     { sidSTEP_SEVEN,    IDD_BACKUP_SERVER_PAGE,         (DLGPROC)BackupPageDlgProc, 0 },
78     { sidSTEP_EIGHT,    IDD_PARTITION_PAGE,             (DLGPROC)PartitionPageDlgProc, 0 },
79     { sidSTEP_NINE,     IDD_ROOT_VOLUMES_PAGE,          (DLGPROC)RootAfsPageDlgProc, 0 },
80     { sidSTEP_TEN,      IDD_REPLICATION_PAGE,           (DLGPROC)ReplicationPageDlgProc, 0 },
81     { sidSTEP_ELEVEN,   IDD_SYS_CONTROL_PAGE,           (DLGPROC)SysControlPageDlgProc, 0 },
82     { sidSTEP_TWELVE,   IDD_CONFIG_SERVER_PAGE,         (DLGPROC)ConfigServerPageDlgProc, 0 }
83 };
84
85 size_t g_nNumStates = sizeof(g_aStates) / sizeof(g_aStates[0]);
86
87
88 // Res ID's of text descriptions of each state
89 UINT g_StateDesc[] = {
90         IDS_INTRO_PAGE,
91         IDS_INFO_PAGE,
92         IDS_INFO_PAGE2,
93         IDS_INFO_PAGE2,
94         IDS_FS_PAGE,
95         IDS_DB_PAGE,
96         IDS_BK_PAGE,
97         IDS_PARTITION_PAGE,
98         IDS_ROOT_AFS_PAGE,
99         IDS_REP_PAGE,
100         IDS_SC_PAGE,
101         IDS_CONFIG_PAGE
102 };
103
104
105
106 /*
107  * EXPORTED VARIABLES _________________________________________________________________
108  *
109  */
110 LPWIZARD g_pWiz = NULL;
111 LPPROPSHEET g_pSheet = NULL;
112
113 CFG_DATA g_CfgData;
114
115 void *g_hToken = 0;
116 void *g_hCell = 0;
117 void *g_hClient = 0;
118 void *g_hServer = 0;
119
120 static void *hClientCell = 0;
121
122 LOGFILE g_LogFile;
123
124
125
126 /*
127  * EXPORTED FUNCTIONS _________________________________________________________________
128  *
129  */
130 extern "C" int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR pszCmdLineA, int nCmdShow)
131 {
132     afs_status_t nStatus;
133
134     TaLocale_LoadCorrespondingModule (hInst);
135
136     // Tell the applib our application's name
137     TCHAR szAppName[cchRESOURCE];
138     AfsAppLib_SetAppName(GetResString(GetAppTitleID(), szAppName));
139
140     // Open the admin libraries
141     int nResult = afsclient_Init(&nStatus);
142     if (!nResult) {
143         ShowError(0, nStatus, IDS_CANT_INIT_ADMIN_LIBS);
144         return 0;
145     }
146
147     // Open the log file
148     char szLogPath[MAX_PATH];
149     sprintf(szLogPath, "%s\\%s", AFSDIR_SERVER_LOGS_DIRPATH, LOG_FILE_NAME);
150     if (!g_LogFile.Open(szLogPath))
151         ShowError(0, 0, IDS_CANT_OPEN_LOG_FILE);
152
153     // Register the help file with the applib's help system
154     AfsAppLib_RegisterHelpFile(TEXT("TaAfsCfg.hlp"));
155
156     /* Start up sockets */
157     WSADATA WSAjunk;
158     WSAStartup(0x0101, &WSAjunk);
159
160     memset(&g_CfgData, 0, sizeof(CFG_DATA));
161
162     // Get this machine's local name
163     char szLocalName[sizeof(g_CfgData.szLocalName) / sizeof(TCHAR)];
164     if (gethostname(szLocalName, sizeof(szLocalName)) != 0) {
165         ShowError(GetFocus(), WSAGetLastError(), IDS_ERROR_LOCAL_HOST_NAME);
166         return 0;
167     }
168     CopyAnsiToString(g_CfgData.szLocalName, szLocalName);
169
170     // Turn the local name into a host name
171     struct hostent *pHostEnt = gethostbyname(szLocalName);
172     if (!pHostEnt) {
173         ShowError(GetFocus(), WSAGetLastError(), IDS_ERROR_HOST_NAME);
174         return 0;
175     }
176     CopyAnsiToString(g_CfgData.szHostname, pHostEnt->h_name, sizeof(g_CfgData.szHostname));
177
178     RegisterFastListClass();
179
180     // Get current config status
181     BOOL bCanceled = FALSE;
182     DWORD dwStatus = GetCurrentConfig(NULL, bCanceled);
183     if (dwStatus || bCanceled) {
184         if (!bCanceled)
185             ShowError(GetFocus(), dwStatus, IDS_CONFIG_CHECK_FAILED);
186         return 0;
187     }
188
189     // Run the appropriate interface
190     if ((strstr(_strlwr(pszCmdLineA), "wizard") != 0))
191         RunWizard();
192     else
193         RunCfgTool();
194
195     FreePartitionTable();
196
197     // Disconnect from the config and admin libraries
198     CloseLibHandles(TRUE);
199
200     return 0;
201 }
202
203 /*
204  *      This is a dialog proc that is shared by all of the wizard pages.  It
205  *      handles things that are common to all of them.  Each page also has its
206  *      own dialog proc.
207  */
208 BOOL CALLBACK WizStep_Common_DlgProc(HWND hRHS, UINT msg, WPARAM wp, LPARAM lp)
209 {
210     // Get the dialog's resource ID
211     int nIDD = GetWindowLong(hRHS, GWL_ID);
212         
213     if (AfsAppLib_HandleHelp(nIDD, hRHS, msg, wp, lp))
214         return TRUE;
215         
216     switch (msg) {
217     case WM_INITDIALOG: MakeBold(hRHS, IDC_TITLE);
218         RedrawGraphic();
219         break;
220
221     case WM_COMMAND:
222         switch (LOWORD(wp)) {
223         case IDCANCEL:
224             QueryCancelWiz();
225             return TRUE;
226         }
227         break;
228     }
229
230     return FALSE;
231 }
232
233 BOOL QueryCancelWiz()
234 {
235     int nChoice = Message(MB_YESNO, IDS_WIZARD_APP_TITLE, IDS_CANCEL_DESC);
236     if (nChoice == IDYES) {
237         g_LogFile.Write("User has chosen to cancel the program.\r\n");
238         if (g_pWiz)
239             g_pWiz->Show(FALSE);
240         return TRUE;
241     }
242
243     return FALSE;
244 }
245
246
247 /*
248  *  Accessor functions for the g_CfgData variable.  There are versions
249  *  for both TCHARs and char *'s.
250  */
251 TCHAR GetDeviceName()           { return g_CfgData.chDeviceName; }
252 LPTSTR GetPartitionName()       { return g_CfgData.szPartitionName; }
253 LPTSTR GetHostName()            { return g_CfgData.szHostname; }
254 LPTSTR GetSysControlMachine()   { return g_CfgData.szSysControlMachine; }
255 LPTSTR GetCellName()            { return g_CfgData.szCellName; }
256 LPTSTR GetServerPW()            { return g_CfgData.szServerPW; }
257 LPTSTR GetAdminName()           { return g_CfgData.szAdminName; }
258 LPTSTR GetAdminPW()             { return g_CfgData.szAdminPW; }
259 LPTSTR GetAdminUID()            { return g_CfgData.szAdminUID; }
260 LPTSTR GetLocalName()           { return g_CfgData.szLocalName; }
261 LPTSTR GetHostname()            { return g_CfgData.szHostname; }
262 LPTSTR GetCellServDbHostname()  { return g_CfgData.szCellServDbHostname; }
263 LPTSTR GetClientCellName()      { return g_CfgData.szClientCellName; }
264 LPTSTR GetSalvageLogFileName()  { return g_CfgData.szSalvageLogFileName; }
265
266 LPTSTR GetClientNetbiosName()
267 {
268     static TCHAR szValue[MAX_MACHINE_NAME_LEN + 1] = "";
269
270     if ( szValue[0] == 0 )
271         CopyAnsiToString(GetClientNetbiosNameA(), szValue);
272
273     return szValue;
274 }
275
276 char GetDeviceNameA()
277 {
278     static char szValueA[2];
279
280     TCHAR devName[2] = TEXT("X");
281     devName[0] = g_CfgData.chDeviceName;
282
283     CopyStringToAnsi(szValueA, devName);
284
285     return szValueA[0];
286 }
287
288 char *GetPartitionNameA()
289 {
290     static char szValueA[MAX_PARTITION_NAME_LEN + 1];
291
292     CopyStringToAnsi(szValueA, g_CfgData.szPartitionName);
293
294     return szValueA;
295 }
296
297 char *GetSysControlMachineA()
298 {
299     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
300
301     CopyStringToAnsi(szValueA, g_CfgData.szSysControlMachine);
302
303     return szValueA;
304 }
305
306 char *GetCellNameA()
307 {
308     static char szValueA[MAX_CELL_NAME_LEN + 1];
309
310     CopyStringToAnsi(szValueA, g_CfgData.szCellName);
311
312     return szValueA;
313 }
314
315 char *GetServerPWA()
316 {
317     static char szValueA[MAX_SERVER_PW_LEN + 1];
318
319     CopyStringToAnsi(szValueA, g_CfgData.szServerPW);
320
321     return szValueA;
322 }
323
324 char *GetAdminNameA()
325 {
326     static char szValueA[MAX_ADMIN_NAME_LEN + 1];
327
328     CopyStringToAnsi(szValueA, g_CfgData.szAdminName);
329
330     return szValueA;
331 }
332
333 char *GetAdminPWA()
334 {
335     static char szValueA[MAX_ADMIN_PW_LEN + 1];
336
337     CopyStringToAnsi(szValueA, g_CfgData.szAdminPW);
338
339     return szValueA;
340 }
341
342 char *GetAdminUIDA()
343 {
344     static char szValueA[MAX_UID_LEN + 1];
345
346     CopyStringToAnsi(szValueA, g_CfgData.szAdminUID);
347
348     return szValueA;
349 }
350
351 char *GetLocalNameA()
352 {
353     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
354
355     CopyStringToAnsi(szValueA, g_CfgData.szLocalName);
356
357     return szValueA;
358 }
359
360 char *GetHostnameA()
361 {
362     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
363
364     CopyStringToAnsi(szValueA, g_CfgData.szHostname);
365
366     return szValueA;
367 }
368
369 char *GetCellServDbHostnameA()
370 {
371     static char szValueA[MAX_MACHINE_NAME_LEN + 1];
372
373     CopyStringToAnsi(szValueA, g_CfgData.szCellServDbHostname);
374
375     return szValueA;
376 }
377
378 char *GetClientCellNameA()
379 {
380     static char szValueA[MAX_CELL_NAME_LEN + 1];
381
382     CopyStringToAnsi(szValueA, g_CfgData.szClientCellName);
383
384     return szValueA;
385 }
386
387 char *GetClientNetbiosNameA()
388 {
389     static char szValueA[MAX_MACHINE_NAME_LEN + 1]="";
390         
391     if ( szValueA[0] == 0 )
392         lana_GetNetbiosName(szValueA, LANA_NETBIOS_NAME_FULL);
393
394     return szValueA;
395 }
396
397 char *GetSalvageLogFileNameA()
398 {
399     static char szValueA[_MAX_PATH];
400
401     CopyStringToAnsi(szValueA, g_CfgData.szSalvageLogFileName);
402
403     return szValueA;
404 }
405
406
407 BOOL GetLibHandles(afs_status_t *pStatus)
408 {
409     ASSERT(g_CfgData.szHostname[0]);
410
411     int nResult;
412         
413     // ************************* NOTE ********************************
414     // * You MUST have already determined whether or not the host
415     // * and client config info is valid before calling this function.
416     // ***************************************************************
417
418     // This function can be called at any time to get handles to the cell and
419     // the config library.  It will try to get the most powerful handle to the
420     // cell that it can, and then use that to open the config library.  If the
421     // libraries are already open, it will close them first.  Two handles to
422     // the config library will be opened, one to the server to be configured,
423     // and one to the client machine we are configuring from.  
424
425     // There are two types of cell handles, NULL and Standard.  A null handle
426     // can make calls to any server except DB servers.  We need this primarily
427     // to talk to the bos server to determine the machine's current configuration,
428     // and to configure the client information if it is not already.  A standard 
429     // handle can talk to any server.  Standard handles can be either authenticated 
430     // or unauthenticated.
431
432     // Close all current handles
433     CloseLibHandles();
434
435     g_LogFile.Write("Getting handles to the cell and the config library.\r\n");
436
437     if (!hClientCell) {
438         // Start by getting a null cell handle and using it to open the client
439         // connection to the config library.
440         g_LogFile.Write("Opening a NULL cell handle to use with the client config library handle.\r\n");
441         nResult = afsclient_NullCellOpen(&hClientCell, pStatus);
442         if (!nResult) {
443             g_LogFile.Write("Failed to open a NULL cell handle: %lx.\r\n", (unsigned long)*pStatus);
444                 return FALSE;
445         }
446     
447         // Get the client handle.  We never need a better handle than this
448         // for the client, and so this handle will never be upgraded.
449         g_LogFile.Write("Getting config handle for the client.\r\n");
450         if (!cfg_HostOpen(hClientCell, GetHostnameA(), &g_hClient, pStatus)) {
451             g_LogFile.Write("Failed to get config handle:  %lx.\r\n", (unsigned long)*pStatus);
452                 return FALSE;
453         }
454     }
455
456     // Now we need to get the most powerful cell handle that we can and use it
457     // to open the config library for the server.
458
459     // If the client info is valid and we know what cell the server should be in,
460     // and the client has that cell in its CellServDB, then we can get a Standard cell
461     // handle.  However there is an exception: if this is the first server in the 
462     // cell then there may not yet be an authentication server to talk to.  In that
463     // case we use a null cell handle.
464     if (g_CfgData.bValidClientInfo &&   // Client config is valid
465         g_CfgData.szCellName[0] &&      // We have a cell name
466         (!g_CfgData.bFirstServer || g_CfgData.bAuthServerRunning))  // Auth server is running
467     {
468         g_LogFile.Write("Opening a non-NULL cell handle to use with the server config library handle.\r\n");
469
470         // Do we have the user info necessary to authenticate?
471         BOOL bHaveUserInfo = g_CfgData.szAdminName[0] && g_CfgData.szAdminPW[0];
472
473         // Open a standard cell handle.  szAdminName and szAdminPW will either be NULL, or
474         // if they have been entered by the user, will be the admin name and password strings.
475         if ((!g_CfgData.bFirstServer || g_CfgData.bAdminPrincipalCreated) && bHaveUserInfo) {
476             g_LogFile.Write("Getting tokens in cell %s for user '%s'.\r\n", GetCellNameA(), GetAdminNameA());
477             nResult = afsclient_TokenGetNew(GetCellNameA(), GetAdminNameA(), GetAdminPWA(), &g_hToken, pStatus);
478         } else {
479             g_LogFile.Write("Getting unauthenticated tokens for cell '%s'.\r\n", GetCellNameA());
480             nResult = afsclient_TokenGetNew(GetCellNameA(), "", "", &g_hToken, pStatus);
481         }
482
483         if (!nResult) {
484             g_LogFile.Write("Failed to get tokens for the specified cell:  %lx.\r\n", (unsigned long)*pStatus);
485             return FALSE;
486         }
487
488         // If the admin name and password are NULL, then this will be an unauthenticated
489         // connection to the cell.
490         g_LogFile.Write("Getting cell handle for cell %s.\r\n", GetCellNameA());
491         nResult = afsclient_CellOpen(GetCellNameA(), g_hToken, &g_hCell, pStatus);
492         if (!nResult) {
493             g_LogFile.Write("Failed to open the cell:  %lx.\r\n", (unsigned long)*pStatus);
494             return FALSE;
495         }
496     } else {
497         g_LogFile.Write("Opening a NULL cell handle to use with the server config library handle.\r\n");
498         nResult = afsclient_NullCellOpen(&g_hCell, pStatus);
499         if (!nResult) {
500             g_LogFile.Write("Failed to open a NULL cell handle:  %lx.\r\n", (unsigned long)*pStatus);
501             return FALSE;
502         }       
503     }   
504
505     // Get the server handle
506     g_LogFile.Write("Getting config library handle for the server.\r\n");
507     if (!cfg_HostOpen(g_hCell, GetHostnameA(), &g_hServer, pStatus)) {
508         g_LogFile.Write("Failed to get config library handle for the server:  %lx.\r\n", (unsigned long)*pStatus);
509         return FALSE;
510     }
511
512     return TRUE;
513 }
514
515 BOOL GetHandles(HWND hParentDlg)
516 {
517     afs_status_t nStatus;
518
519     if (!GetLibHandles(&nStatus)) {
520         ShowError(hParentDlg, nStatus, IDS_GET_TOKENS_ERROR);
521         g_CfgData.szAdminPW[0] = 0;
522         return FALSE;
523     }
524
525     return TRUE;
526 }
527
528
529 /*
530  * Static FUNCTIONS _________________________________________________________________
531  *
532  */
533 static void CloseLibHandles(BOOL bExiting)
534 {
535     afs_status_t nStatus;
536
537     // We will close them in the reverse order of their creation.
538     if (g_hServer) {
539         cfg_HostClose(g_hServer, &nStatus);
540         g_hServer = 0;
541     }
542
543     if (g_hCell) {
544         afsclient_CellClose(g_hCell, &nStatus);
545         g_hCell = 0;
546     }
547
548     if (g_hToken) {
549         afsclient_TokenClose(g_hToken, &nStatus);
550         g_hToken = 0;
551     }
552
553     // Only close the client cfg and cell handles if we are exiting.
554     if (bExiting) {
555         if (g_hClient) {
556             cfg_HostClose(g_hClient, &nStatus);
557             g_hClient = 0;
558         }       
559
560         if (hClientCell) {
561             afsclient_CellClose(hClientCell, &nStatus);
562             hClientCell = 0;
563         }
564     }
565 }
566
567 static void SetConfigDefaults()
568 {
569     if (g_CfgData.bWizard) {
570         if (g_CfgData.configFS == CS_NULL)
571             g_CfgData.configFS = CS_CONFIGURE;
572
573         if (g_CfgData.configDB == CS_NULL)
574             g_CfgData.configDB = CS_CONFIGURE;
575
576         if (g_CfgData.configBak == CS_NULL)
577             g_CfgData.configBak = CS_CONFIGURE;
578
579         if (g_CfgData.configPartition == CS_NULL)
580             g_CfgData.configPartition = CS_CONFIGURE;
581
582         if (g_CfgData.configRootVolumes == CS_NULL)
583             g_CfgData.configRootVolumes = CS_CONFIGURE;
584
585         if (g_CfgData.configRep == CS_NULL)
586             g_CfgData.configRep = CS_CONFIGURE;
587
588         if (g_CfgData.configSCS == CS_NULL)
589             g_CfgData.configSCS = CS_DONT_CONFIGURE;
590
591         if (g_CfgData.configSCC == CS_NULL)
592             g_CfgData.configSCC = CS_DONT_CONFIGURE;
593     }   
594
595     lstrcpy(g_CfgData.szAdminName, TEXT("admin"));
596     lstrcpy(g_CfgData.szAdminUID, TEXT("0"));
597
598     g_CfgData.bUseNextUid = TRUE;
599 }       
600
601         
602 // Prototypes for each property page's dialog proc
603 BOOL CALLBACK PartitionsPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
604 BOOL CALLBACK ServicesPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
605
606 static void RunCfgTool()
607 {
608     // If the client info is invalid, then the config tool cannot run.  Inform the user and
609     // ask if they want to run the wizard instead.
610     if (!g_CfgData.bValidClientInfo) {
611         int nChoice = MsgBox(0, IDS_NEED_CLIENT_INFO, GetAppTitleID(), MB_YESNO | MB_ICONSTOP);
612         if (nChoice == IDYES)
613             RunWizard();
614         return;
615     }   
616
617     // If the server info is invalid, then the config tool cannot run.  The Wizard must be run
618     // to initially configure the server.  Inform the user and ask if they want to run the wizard instead.
619     if (!g_CfgData.bValidServerInfo) {
620         int nChoice = MsgBox(0, IDS_NEED_SERVER_INFO, GetAppTitleID(), MB_OKCANCEL | MB_ICONEXCLAMATION);
621         if (nChoice == IDOK)
622             RunWizard();
623         return;
624     }
625
626     g_CfgData.bWizard = FALSE;
627
628     SetConfigDefaults();
629
630     RegisterConfigToolHelp();
631
632     // Create the prop sheet
633     g_pSheet = PropSheet_Create(IDS_CFG_TOOL_APP_TITLE, TRUE);
634
635     // Add the pages
636     PropSheet_AddTab(g_pSheet, IDS_PARTITIONS_PAGE_TITLE, IDD_PARTITIONS_PAGE, (DLGPROC)PartitionsPageDlgProc, 0, TRUE, TRUE);
637     PropSheet_AddTab(g_pSheet, IDS_SERVICES_PAGE_TITLE, IDD_SERVICES_PAGE, (DLGPROC)ServicesPageDlgProc, 0, TRUE);
638
639     // Let the user see it
640     PropSheet_ShowModal(g_pSheet);
641 }       
642
643 static void RunWizard()
644 {
645     g_CfgData.bWizard = TRUE;
646
647     SetConfigDefaults();
648
649     RegisterWizardHelp();
650
651     g_pWiz = new WIZARD;
652     g_pWiz->SetDialogTemplate(IDD_WIZARD, IDC_WIZARD_LEFTPANE, IDC_WIZARD_RIGHTPANE, IDBACK, IDNEXT);
653     g_pWiz->SetGraphic(IDB_GRAPHIC_16, IDB_GRAPHIC_256);
654     g_pWiz->SetStates(g_aStates, g_nNumStates);
655     g_pWiz->SetGraphicCallback(PaintPageGraphic);
656
657     g_pWiz->SetState(sidSTEP_ONE);
658     g_pWiz->Show();
659
660     delete g_pWiz;
661
662     g_pWiz = 0;
663 }       
664