- Added "Service Dependencies" page
[public/netxms.git] / src / server / tools / nxconfig / WizardWorker.cpp
CommitLineData
7e24b306
VK
1/*
2** NetXMS - Network Management System
3** Server Configurator for Windows
4** Copyright (C) 2005 Victor Kirhenshtein
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** $module: WizardWorker.cpp
21** Worker thread for configuration wizard
22**
23**/
24
25#include "stdafx.h"
26#include "nxconfig.h"
27
28
a689c135
VK
29//
30// Global data
31//
32
33TCHAR g_szWizardErrorText[MAX_ERROR_TEXT] = _T("Completed successfully");
34
35
7e24b306
VK
36//
37// Static data
38//
39
40static HWND m_hStatusWnd = NULL;
41
42
242a1cdc
VK
43//
44// Install event source
45//
46
47static BOOL InstallEventSource(TCHAR *pszPath)
48{
49 HKEY hKey;
50 TCHAR szBuffer[256];
51 DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
52
53 if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,
54 "System\\CurrentControlSet\\Services\\EventLog\\System\\" CORE_EVENT_SOURCE,
55 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL))
56 {
57 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
58 _T("Unable to create registry key: %s"),
59 GetSystemErrorText(GetLastError(), szBuffer, 256));
60 return FALSE;
61 }
62
63 RegSetValueEx(hKey, _T("TypesSupported"), 0, REG_DWORD,(BYTE *)&dwTypes, sizeof(DWORD));
64 RegSetValueEx(hKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ,
56b32598 65 (BYTE *)pszPath, ((DWORD)_tcslen(pszPath) + 1) * sizeof(TCHAR));
242a1cdc
VK
66
67 RegCloseKey(hKey);
68 return TRUE;
69}
70
71
72//
73// Install Windows service
74//
75
76static BOOL InstallService(WIZARD_CFG_INFO *pc)
77{
78 SC_HANDLE hMgr, hService;
79 TCHAR szCmdLine[MAX_PATH * 2], *pszLogin, *pszPassword, szBuffer[256];
80 BOOL bResult = FALSE;
81
82 hMgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
83 if (hMgr == NULL)
84 {
85 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
86 _T("Cannot connect to Service Manager: %s"),
87 GetSystemErrorText(GetLastError(), szBuffer, 256));
88 return FALSE;
89 }
90
91 if (pc->m_szServiceLogin[0] == 0)
92 {
93 pszLogin = NULL;
94 pszPassword = NULL;
95 }
96 else
97 {
98 pszLogin = pc->m_szServiceLogin;
99 pszPassword = pc->m_szServicePassword;
100 }
101 _sntprintf(szCmdLine, MAX_PATH * 2, _T("\"%s\\bin\\netxmsd.exe\" --config \"%s\""),
102 pc->m_szInstallDir, pc->m_szConfigFile);
103 hService = CreateService(hMgr, CORE_SERVICE_NAME, _T("NetXMS Core"),
104 GENERIC_READ, SERVICE_WIN32_OWN_PROCESS,
105 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
106 szCmdLine, NULL, NULL, NULL, pszLogin, pszPassword);
107 if (hService == NULL)
108 {
109 DWORD dwCode = GetLastError();
110
111 if (dwCode == ERROR_SERVICE_EXISTS)
112 {
113 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
114 _T("Service named '") CORE_SERVICE_NAME _T("' already exist"));
115 }
116 else
117 {
118 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
119 _T("Cannot create service: %s"),
120 GetSystemErrorText(dwCode, szBuffer, 256));
121 }
122 }
123 else
124 {
125 CloseServiceHandle(hService);
126 bResult = TRUE;
a5ff0f94 127 _sntprintf(szCmdLine, MAX_PATH, _T("%s\\bin\\libnxsrv.dll"), pc->m_szInstallDir);
242a1cdc
VK
128 }
129
130 CloseServiceHandle(hMgr);
131
132 return bResult ? InstallEventSource(szCmdLine) : FALSE;
133}
134
135
a689c135
VK
136//
137// Execute query and set error text
138//
139
140static BOOL DBQueryEx(DB_HANDLE hConn, TCHAR *pszQuery)
141{
142 BOOL bResult;
143
144 bResult = DBQuery(hConn, pszQuery);
145 if (!bResult)
146 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
147 _T("SQL query failed:\n%s"), pszQuery);
148 return bResult;
149}
150
151
579f05b6
VK
152//
153// Write string value to configuration table
154//
155
156static BOOL WriteConfigStr(DB_HANDLE hConn, TCHAR *pszVar, TCHAR *pszValue,
157 BOOL bIsVisible, BOOL bNeedRestart)
158{
159 DB_RESULT hResult;
56cd40b6 160 TCHAR *pszEscValue, szQuery[1024];
579f05b6
VK
161 BOOL bVarExist = FALSE;
162
163 if (_tcslen(pszVar) > 127)
164 return FALSE;
165
166 // Check for variable existence
167 _sntprintf(szQuery, 1024, _T("SELECT var_value FROM config WHERE var_name='%s'"), pszVar);
168 hResult = DBSelect(hConn, szQuery);
169 if (hResult != 0)
170 {
171 if (DBGetNumRows(hResult) > 0)
172 bVarExist = TRUE;
173 DBFreeResult(hResult);
174 }
175
176 // Create or update variable value
56cd40b6 177 pszEscValue = EncodeSQLString(pszValue);
579f05b6
VK
178 if (bVarExist)
179 _sntprintf(szQuery, 1024,
180 _T("UPDATE config SET var_value='%s' WHERE var_name='%s'"),
56cd40b6 181 pszEscValue, pszVar);
579f05b6
VK
182 else
183 _sntprintf(szQuery, 1024,
184 _T("INSERT INTO config (var_name,var_value,is_visible,need_server_restart) VALUES ('%s','%s',%d,%d)"),
56cd40b6
VK
185 pszVar, pszEscValue, bIsVisible, bNeedRestart);
186 free(pszEscValue);
579f05b6
VK
187 return DBQueryEx(hConn, szQuery);
188}
189
190
191//
192// Write integer value to configuration table
193//
194
195static BOOL WriteConfigInt(DB_HANDLE hConn, TCHAR *pszVar, int iValue,
196 BOOL bIsVisible, BOOL bNeedRestart)
197{
198 TCHAR szBuffer[64];
199
0212dc5a 200 _stprintf(szBuffer, _T("%d"), iValue);
579f05b6
VK
201 return WriteConfigStr(hConn, pszVar, szBuffer, bIsVisible, bNeedRestart);
202}
203
204
205//
206// Write unsigned long value to configuration table
207//
208
209static BOOL WriteConfigULong(DB_HANDLE hConn, TCHAR *pszVar, DWORD dwValue,
210 BOOL bIsVisible, BOOL bNeedRestart)
211{
212 TCHAR szBuffer[64];
213
0212dc5a 214 _stprintf(szBuffer, _T("%u"), dwValue);
579f05b6
VK
215 return WriteConfigStr(hConn, pszVar, szBuffer, bIsVisible, bNeedRestart);
216}
217
218
7e24b306
VK
219//
220// Create configuration file
221//
222
223static BOOL CreateConfigFile(WIZARD_CFG_INFO *pc)
224{
225 BOOL bResult = FALSE;
a689c135
VK
226 FILE *pf;
227 time_t t;
7e24b306
VK
228
229 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating configuration file"));
230
a689c135
VK
231 pf = _tfopen(pc->m_szConfigFile, _T("w"));
232 if (pf != NULL)
233 {
234 t= time(NULL);
235 fprintf(pf, _T("#\n# NetXMS Server configuration file\n")
236 _T("# Created by NetXMS Server configuration wizard at %s#\n\n"),
237 _tctime(&t));
238 fprintf(pf, _T("LogFile = %s\n"), pc->m_bLogToSyslog ? _T("{syslog}") : pc->m_szLogFile);
239 fprintf(pf, _T("DBDriver = %s\n"), pc->m_szDBDriver);
7007d4a9 240 if (pc->m_szDBDrvParams[0] != 0)
a689c135 241 fprintf(pf, _T("DBDrvParams = %s\n"), pc->m_szDBDrvParams);
7dbb6862
VK
242 if (!_tcsicmp(pc->m_szDBDriver, _T("sqlite.ddr")))
243 {
244 fprintf(pf, _T("DBName = %s\\database\\%s\n"), pc->m_szInstallDir, pc->m_szDBName);
245 }
246 else
247 {
248 if (pc->m_szDBServer[0] != 0)
249 fprintf(pf, _T("DBServer = %s\n"), pc->m_szDBServer);
250 if ((pc->m_szDBName[0] != 0) && (_tcsicmp(pc->m_szDBDriver, _T("odbc.ddr"))))
251 fprintf(pf, _T("DBName = %s\n"), pc->m_szDBName);
252 if (pc->m_szDBLogin[0] != 0)
253 fprintf(pf, _T("DBLogin = %s\n"), pc->m_szDBLogin);
254 if (pc->m_szDBPassword[0] != 0)
255 fprintf(pf, _T("DBPassword = %s\n"), pc->m_szDBPassword);
256 }
a689c135
VK
257 fprintf(pf, _T("LogFailedSQLQueries = %s\n"), pc->m_bLogFailedSQLQueries ? _T("yes") : _T("no"));
258 fclose(pf);
259 bResult = TRUE;
260 }
261 else
262 {
263 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT, _T("Error creating file %s"), pc->m_szConfigFile);
264 }
265
266 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
267 return bResult;
268}
269
270
271//
272// Create database in MySQL
273//
274
275static BOOL CreateDBMySQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
276{
277 TCHAR szQuery[256];
278 BOOL bResult;
279
280 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
281 bResult = DBQueryEx(hConn, szQuery);
282
283 if (bResult)
284 {
285 _stprintf(szQuery, _T("GRANT ALL ON %s.* TO %s IDENTIFIED BY '%s'"),
286 pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
287 bResult = DBQueryEx(hConn, szQuery);
288
289 _stprintf(szQuery, _T("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY '%s'"),
290 pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
291 bResult = DBQueryEx(hConn, szQuery);
292 }
293
294 if (bResult)
295 bResult = DBQueryEx(hConn, _T("FLUSH PRIVILEGES"));
296 return bResult;
297}
298
299
d589a067
VK
300//
301// Create database in PostgreSQL
302//
303
304static BOOL CreateDBPostgreSQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
305{
306 TCHAR szQuery[256];
307 BOOL bResult;
308
309 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
310 bResult = DBQueryEx(hConn, szQuery);
311
312 if (bResult)
313 {
314 _stprintf(szQuery, _T("CREATE USER %s WITH PASSWORD '%s'"),
315 pc->m_szDBLogin, pc->m_szDBPassword);
316 bResult = DBQueryEx(hConn, szQuery);
317 }
318
319 if (bResult)
320 {
321 _stprintf(szQuery, _T("GRANT ALL PRIVILEGES ON DATABASE %s TO %s"),
322 pc->m_szDBName, pc->m_szDBLogin);
323 bResult = DBQueryEx(hConn, szQuery);
324 }
325
326 return bResult;
327}
328
329
a689c135
VK
330//
331// Create database in Microsoft SQL
332//
333
334static BOOL CreateDBMSSQL(WIZARD_CFG_INFO *pc, DB_HANDLE hConn)
335{
242a1cdc 336 TCHAR szQuery[512], *pszLogin;
a689c135
VK
337 BOOL bResult;
338
339 bResult = DBQueryEx(hConn, _T("USE master"));
340 if (bResult)
341 {
342 _stprintf(szQuery, _T("CREATE DATABASE %s"), pc->m_szDBName);
343 bResult = DBQueryEx(hConn, szQuery);
344 }
345
346 if (bResult)
347 {
348 _stprintf(szQuery, _T("USE %s"), pc->m_szDBName);
349 bResult = DBQueryEx(hConn, szQuery);
350 }
351
352 if (bResult)
353 {
242a1cdc
VK
354 if (!strcmp(pc->m_szDBLogin, _T("*")))
355 {
356 // Use Windows authentication
357 pszLogin = pc->m_szServiceLogin;
358 }
359 else
360 {
361 _sntprintf(szQuery, 512, _T("sp_addlogin @loginame = '%s', @passwd = '%s', @defdb = '%s'"),
362 pc->m_szDBLogin, pc->m_szDBPassword, pc->m_szDBName);
363 bResult = DBQueryEx(hConn, szQuery);
364 pszLogin = pc->m_szDBLogin;
365 }
a689c135
VK
366 }
367
368 if (bResult)
369 {
242a1cdc 370 _sntprintf(szQuery, 512, _T("sp_grantdbaccess @loginame = '%s'"), pszLogin);
a689c135
VK
371 bResult = DBQueryEx(hConn, szQuery);
372 }
373
374 if (bResult)
375 {
242a1cdc 376 _stprintf(szQuery, _T("GRANT ALL TO %s"), pszLogin);
a689c135
VK
377 bResult = DBQueryEx(hConn, szQuery);
378 }
379
380 return bResult;
381}
382
383
384//
7dbb6862 385// Create SQLite embedded database
a689c135
VK
386//
387
7dbb6862 388static BOOL CreateSQLiteDB(WIZARD_CFG_INFO *pc)
a689c135 389{
7dbb6862 390 TCHAR szBaseDir[MAX_PATH];
a689c135
VK
391 DB_HANDLE hConn;
392 BOOL bResult = FALSE;
393
7dbb6862
VK
394 _stprintf(szBaseDir, _T("%s\\database"), pc->m_szInstallDir);
395 SetCurrentDirectory(szBaseDir);
396 DeleteFile(pc->m_szDBName);
397 hConn = DBConnectEx(NULL, pc->m_szDBName, NULL, NULL);
a689c135
VK
398 if (hConn != NULL)
399 {
7dbb6862
VK
400 DBDisconnect(hConn);
401 bResult = TRUE;
402 }
403 return bResult;
404}
a689c135 405
a689c135 406
7dbb6862
VK
407//
408// Create database
409//
410
411static BOOL CreateDatabase(WIZARD_CFG_INFO *pc)
412{
413 DB_HANDLE hConn;
414 BOOL bResult = FALSE;
415
416 if (pc->m_iDBEngine == DB_ENGINE_SQLITE)
417 {
418 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating database"));
419 bResult = CreateSQLiteDB(pc);
a689c135 420 }
fc5296c5
VK
421 else
422 {
7dbb6862 423 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Connecting to database server as DBA"));
b3dec4cd
VK
424 hConn = DBConnectEx(pc->m_szDBServer,
425 (pc->m_iDBEngine == DB_ENGINE_PGSQL) ? _T("template1") : NULL,
426 pc->m_szDBALogin, pc->m_szDBAPassword);
7dbb6862
VK
427 if (hConn != NULL)
428 {
429 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, TRUE, 0);
430 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Creating database"));
431
432 switch(pc->m_iDBEngine)
433 {
434 case DB_ENGINE_MYSQL:
435 bResult = CreateDBMySQL(pc, hConn);
436 break;
437 case DB_ENGINE_MSSQL:
438 bResult = CreateDBMSSQL(pc, hConn);
439 break;
440 case DB_ENGINE_PGSQL:
441 bResult = CreateDBPostgreSQL(pc, hConn);
442 break;
443 default:
444 bResult = FALSE;
445 _sntprintf(g_szWizardErrorText, MAX_ERROR_TEXT,
446 _T("Unsupported database engine code %d"), pc->m_iDBEngine);
447 break;
448 }
449
450 DBDisconnect(hConn);
451 }
452 else
453 {
454 _tcscpy(g_szWizardErrorText, _T("Unable to connect to database"));
455 }
fc5296c5 456 }
7e24b306
VK
457
458 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
459 return bResult;
460}
461
462
463//
464// Worker thread
465//
466
467static DWORD __stdcall WorkerThread(void *pArg)
468{
469 WIZARD_CFG_INFO *pc = (WIZARD_CFG_INFO *)pArg;
a689c135 470 DB_HANDLE hConn = NULL;
383b42a1
VK
471 TCHAR szDataDir[MAX_PATH], szQuery[256], szGUID[64];
472 uuid_t guid;
7e24b306
VK
473 BOOL bResult;
474
475 bResult = CreateConfigFile(pc);
476
477 // Load database driver
478 if (bResult)
479 {
480 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Loading database driver"));
c81979bc
VK
481 nx_strncpy(g_szDbDriver, pc->m_szDBDriver, MAX_PATH);
482 nx_strncpy(g_szDbServer, pc->m_szDBServer, MAX_PATH);
7e24b306 483 bResult = DBInit(FALSE, FALSE, FALSE);
a689c135
VK
484 if (!bResult)
485 _tcscpy(g_szWizardErrorText, _T("Error loading database driver"));
7e24b306
VK
486 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
487 }
488
489 // Create database if requested
a689c135
VK
490 if (pc->m_bCreateDB && bResult)
491 bResult = CreateDatabase(pc);
492
493 // Connect to database as user
494 if (bResult)
495 {
496 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Connecting to database"));
497 hConn = DBConnectEx(pc->m_szDBServer, pc->m_szDBName, pc->m_szDBLogin, pc->m_szDBPassword);
498 bResult = (hConn != NULL);
499 if (!bResult)
500 _tcscpy(g_szWizardErrorText, _T("Unable to connect to database"));
501 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
502 }
503
504 // Initialize database if requested
505 if ((pc->m_bCreateDB || pc->m_bInitDB) && bResult)
506 {
507 TCHAR szInitFile[MAX_PATH];
7dbb6862 508 static TCHAR *szEngineName[] = { _T("mysql"), _T("pgsql"), _T("mssql"), _T("oracle"), _T("sqlite") };
a689c135
VK
509
510 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Initializing database"));
511
512 _sntprintf(szInitFile, MAX_PATH, _T("%s\\lib\\sql\\dbinit_%s.sql"),
513 pc->m_szInstallDir, szEngineName[pc->m_iDBEngine]);
514 bResult = ExecSQLBatch(hConn, szInitFile);
515
383b42a1
VK
516 // Generate GUID for user "admin"
517 if (bResult)
518 {
519 uuid_generate(guid);
520 _sntprintf(szQuery, 256, _T("UPDATE users SET guid='%s' WHERE id=0"),
521 uuid_to_string(guid, szGUID));
522 bResult = DBQueryEx(hConn, szQuery);
523 }
524
525 // Generate GUID for "everyone" group
526 if (bResult)
527 {
528 uuid_generate(guid);
529 _sntprintf(szQuery, 256, _T("UPDATE user_groups SET guid='%s' WHERE id=%d"),
530 uuid_to_string(guid, szGUID), GROUP_EVERYONE);
531 bResult = DBQueryEx(hConn, szQuery);
532 }
533
a689c135
VK
534 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
535 }
536
537 // Set server parameters
579f05b6
VK
538 if (bResult)
539 {
540 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Configuring server"));
541
542#define __CHK(x) if (bResult) bResult = x;
543
544 __CHK(WriteConfigInt(hConn, _T("RunNetworkDiscovery"), pc->m_bRunAutoDiscovery, TRUE, TRUE));
545 __CHK(WriteConfigULong(hConn, _T("DiscoveryPollingInterval"), pc->m_dwDiscoveryPI, TRUE, TRUE));
546 __CHK(WriteConfigULong(hConn, _T("NumberOfStatusPollers"), pc->m_dwNumStatusPollers, TRUE, TRUE));
547 __CHK(WriteConfigULong(hConn, _T("StatusPollingInterval"), pc->m_dwStatusPI, TRUE, TRUE));
548 __CHK(WriteConfigULong(hConn, _T("NumberOfConfigurationPollers"), pc->m_dwNumConfigPollers, TRUE, TRUE));
549 __CHK(WriteConfigULong(hConn, _T("ConfigurationPollingInterval"), pc->m_dwConfigurationPI, TRUE, TRUE));
550
551 __CHK(WriteConfigStr(hConn, _T("SMTPServer"), pc->m_szSMTPServer, TRUE, FALSE));
552 __CHK(WriteConfigStr(hConn, _T("SMTPFromAddr"), pc->m_szSMTPMailFrom, TRUE, FALSE));
553
554 __CHK(WriteConfigInt(hConn, _T("EnableAdminInterface"), pc->m_bEnableAdminInterface, TRUE, TRUE));
555
721d9c44
VK
556 _sntprintf(szDataDir, MAX_PATH, _T("%s\\var"), pc->m_szInstallDir);
557 __CHK(WriteConfigStr(hConn, _T("DataDirectory"), szDataDir, TRUE, TRUE));
558
91d8e711 559 __CHK(WriteConfigStr(hConn, _T("SMSDriver"), pc->m_szSMSDriver, TRUE, TRUE));
543171c6 560 __CHK(WriteConfigStr(hConn, _T("SMSDrvConfig"), pc->m_szSMSDrvParam, TRUE, TRUE));
91d8e711 561
579f05b6
VK
562#undef __CHK
563
564 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
565 }
a689c135
VK
566
567 // Cleanup
568 if (hConn != NULL)
569 DBDisconnect(hConn);
7e24b306 570
242a1cdc
VK
571 // Install service
572 if (bResult)
573 {
574 PostMessage(m_hStatusWnd, WM_START_STAGE, 0, (LPARAM)_T("Installing service"));
575 bResult = InstallService(pc);
576 PostMessage(m_hStatusWnd, WM_STAGE_COMPLETED, bResult, 0);
577 }
578
7e24b306
VK
579 // Notify UI that job is finished
580 PostMessage(m_hStatusWnd, WM_JOB_FINISHED, bResult, 0);
581 return 0;
582}
583
584
585//
586// Worker thread starter
587//
588
589void StartWizardWorkerThread(HWND hWnd, WIZARD_CFG_INFO *pc)
590{
591 HANDLE hThread;
592 DWORD dwThreadId;
593
594 m_hStatusWnd = hWnd;
595 hThread = CreateThread(NULL, 0, WorkerThread, pc, 0, &dwThreadId);
596 if (hThread != NULL)
597 CloseHandle(hThread);
598}