]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/win32/libwin32/main.cpp
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / win32 / libwin32 / main.cpp
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /* 
20  * 
21  *  Kern Sibbald, August 2007
22  *
23  * Note, some of the original Bacula Windows startup and service handling code
24  *  was derived from VNC code that was used in apcupsd then ported to 
25  *  Bacula.  However, since then the code has been significantly enhanced 
26  *  and largely rewritten.  
27  *
28  * Evidently due to the nature of Windows startup code and service
29  *  handling code, certain similarities remain. Thanks to the original
30  *  VNC authors.
31  *
32  * This is a generic main routine, which is used by all three
33  *  of the daemons. Each one compiles it with slightly different
34  *  #defines.
35  *
36  */
37
38 #define LOCKMGR_COMPLIANT
39 #include "bacula.h"
40 #include "win32.h"
41 #include <signal.h>
42 #include <pthread.h>
43
44 #include <commctrl.h>
45
46
47 /* Globals */
48 HINSTANCE appInstance;
49 DWORD mainthreadId;
50 bool opt_debug = false;
51 bool have_service_api;
52 DWORD service_thread_id = 0;
53 char win_os[300];
54
55 bool GetWindowsVersionString(LPTSTR osbuf, int maxsiz);
56
57
58 #define MAX_COMMAND_ARGS 100
59 static char *command_args[MAX_COMMAND_ARGS] = {(char *)LC_APP_NAME, NULL};
60 static int num_command_args = 1;
61 static pid_t main_pid;
62 static pthread_t main_tid;
63
64 const char usage[] = APP_NAME "[/debug] [/service] [/run] [/kill] [/install] [/remove] [/help]\n";
65
66 /*
67  *
68  * Main Windows entry point.
69  *
70  * We parse the command line and either calls the main App
71  *   or starts up the service.
72  */
73 int WINAPI WinMain(HINSTANCE Instance, HINSTANCE /*PrevInstance*/, PSTR CmdLine, 
74                    int /*show*/)
75 {
76    char *cmdLine = CmdLine;
77    char *wordPtr, *tempPtr;
78    int i, quote;
79    OSVERSIONINFO osversioninfo;
80    osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);
81
82    /* Save the application instance and main thread id */
83    appInstance = Instance;
84    mainthreadId = GetCurrentThreadId();
85
86    if (GetVersionEx(&osversioninfo) && 
87        osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
88       have_service_api = true;
89    }
90
91    GetWindowsVersionString(win_os, sizeof(win_os));
92
93    main_pid = getpid();
94    main_tid = pthread_self();
95
96    INITCOMMONCONTROLSEX initCC = {
97       sizeof(INITCOMMONCONTROLSEX), 
98       ICC_STANDARD_CLASSES
99    };
100
101    InitCommonControlsEx(&initCC);
102
103    /*
104     * Funny things happen with the command line if the
105     * execution comes from c:/Program Files/bacula/bacula.exe
106     * We get a command line like: Files/bacula/bacula.exe" options
107     * I.e. someone stops scanning command line on a space, not
108     * realizing that the filename is quoted!!!!!!!!!!
109     * So if first character is not a double quote and
110     * the last character before first space is a double
111     * quote, we throw away the junk.
112     */
113
114    wordPtr = cmdLine;
115    while (*wordPtr && *wordPtr != ' ')
116       wordPtr++;
117    if (wordPtr > cmdLine)      /* backup to char before space */
118       wordPtr--;
119    /* if first character is not a quote and last is, junk it */
120    if (*cmdLine != '"' && *wordPtr == '"') {
121       cmdLine = wordPtr + 1;
122    }
123
124    /*
125     * Build Unix style argc *argv[] for the main "Unix" code
126     *  stripping out any Windows options 
127     */
128
129    /* Don't NULL command_args[0] !!! */
130    for (i=1;i<MAX_COMMAND_ARGS;i++) {
131       command_args[i] = NULL;
132    }
133
134    char *pszArgs = bstrdup(cmdLine);
135    wordPtr = pszArgs;
136    quote = 0;
137    while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
138       wordPtr++;
139    if (*wordPtr == '\"') {
140       quote = 1;
141       wordPtr++;
142    } else if (*wordPtr == '/') {
143       /* Skip Windows options */
144       while (*wordPtr && (*wordPtr != ' ' && *wordPtr != '\t'))
145          wordPtr++;
146       while  (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
147          wordPtr++;
148    }
149    if (*wordPtr) {
150       while (*wordPtr && num_command_args < MAX_COMMAND_ARGS) {
151          tempPtr = wordPtr;
152          if (quote) {
153             while (*tempPtr && *tempPtr != '\"')
154                tempPtr++;
155             quote = 0;
156          } else {
157             while (*tempPtr && *tempPtr != ' ')
158             tempPtr++;
159          }
160          if (*tempPtr)
161             *(tempPtr++) = '\0';
162          command_args[num_command_args++] = wordPtr;
163          wordPtr = tempPtr;
164          while (*wordPtr && (*wordPtr == ' ' || *wordPtr == '\t'))
165             wordPtr++;
166          if (*wordPtr == '\"') {
167             quote = 1;
168             wordPtr++;
169          }
170       }
171    }
172
173    /*
174     * Now process Windows command line options. Most of these options
175     *  are single shot -- i.e. we accept one option, do something and
176     *  terminate.
177     */
178    for (i = 0; i < (int)strlen(cmdLine); i++) {
179       char *p = &cmdLine[i];
180
181       if (*p <= ' ') {
182          continue;                    /* toss junk */
183       }
184
185       if (*p != '/') {
186          break;                       /* syntax error */
187       }
188
189       /* Start as a service? */
190       if (strncasecmp(p, "/service", 8) == 0) {
191          return baculaServiceMain();      /* yes, run as a service */
192       }
193
194       /* Stop any running copy? */
195       if (strncasecmp(p, "/kill", 5) == 0) {
196          return stopRunningBacula();
197       }
198
199       /* Run app as user program? */
200       if (strncasecmp(p, "/run", 4) == 0) {
201          return BaculaAppMain();         /* yes, run as a user program */
202       }
203
204       /* Install Bacula in registry? */
205       if (strncasecmp(p, "/install", 8) == 0) {
206          return installService(p+8);    /* Pass command options */
207       }
208
209       /* Remove Bacula registry entry? */
210       if (strncasecmp(p, "/remove", 7) == 0) {
211          return removeService();
212       }
213
214       /* Set debug mode? -- causes more dialogs to be displayed */
215       if (strncasecmp(p, "/debug", 6) == 0) {
216          opt_debug = true;
217          i += 6;                /* skip /debug */
218          continue;
219       }
220
221       /* Display help? -- displays usage */
222       if (strncasecmp(p, "/help", 5) == 0) {
223          MessageBox(NULL, usage, APP_DESC, MB_OK|MB_ICONINFORMATION);
224          return 0;
225       }
226       
227       MessageBox(NULL, cmdLine, _("Bad Command Line Option"), MB_OK);
228
229       /* Show the usage dialog */
230       MessageBox(NULL, usage, APP_DESC, MB_OK | MB_ICONINFORMATION);
231
232       return 1;
233    }
234    return BaculaAppMain();
235 }
236
237 #ifndef HAVE_TRAY_MONITOR
238 /* Minimalist winproc when don't have tray monitor */
239 LRESULT CALLBACK bacWinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
240 {
241    switch (iMsg) {
242    case WM_DESTROY:
243       PostQuitMessage(0);
244       return 0;
245    }
246    return DefWindowProc(hwnd, iMsg, wParam, lParam);
247 }
248 #endif
249
250
251 /*
252  * Called as a thread from BaculaAppMain()
253  * Here we handle the Windows messages
254  */
255 void *Main_Msg_Loop(LPVOID lpwThreadParam) 
256 {
257    MSG msg;
258
259    pthread_detach(pthread_self());
260
261    /*
262     * Since we are the only thread with a message loop
263     * mark ourselves as the service thread so that
264     * we can receive all the window events.
265     */
266    service_thread_id = GetCurrentThreadId();
267
268 #ifdef HAVE_TRAY_MONITOR
269    /* Create tray icon & menu if we're running as an app */
270    trayMonitor *monitor = new trayMonitor();
271    if (monitor == NULL) {
272       PostQuitMessage(0);
273    }
274
275 #else
276    /* Create a window to handle Windows messages */
277    WNDCLASSEX baclass;
278
279    baclass.cbSize         = sizeof(baclass);
280    baclass.style          = 0;
281    baclass.lpfnWndProc    = bacWinProc;
282    baclass.cbClsExtra     = 0;
283    baclass.cbWndExtra     = 0;
284    baclass.hInstance      = appInstance;
285    baclass.hIcon          = NULL;
286    baclass.hCursor        = NULL;
287    baclass.hbrBackground  = NULL;
288    baclass.lpszMenuName   = NULL;
289    baclass.lpszClassName  = APP_NAME;
290    baclass.hIconSm        = NULL;
291
292    RegisterClassEx(&baclass);
293
294    if (CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
295                 CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
296                 NULL, NULL, appInstance, NULL) == NULL) {
297       PostQuitMessage(0);
298    }
299 #endif
300
301    /* Now enter the Windows message handling loop until told to quit! */
302    while (GetMessage(&msg, NULL, 0,0) ) {
303       TranslateMessage(&msg);
304       DispatchMessage(&msg);
305    }
306
307    /* If we get here, we are shutting down */
308
309 #ifdef HAVE_TRAY_MONITOR
310    if (monitor != NULL) {
311       delete monitor;
312    }
313 #endif
314
315    if (have_service_api) {
316       /* Mark that we're no longer running */
317       service_thread_id = 0;
318       /* Tell the service manager that we've stopped. */
319       ReportStatus(SERVICE_STOPPED, service_error, 0);
320    }  
321    /* Tell main "Unix" program to go away */
322    terminate_app(0);
323
324    /* Should not get here */
325    pthread_kill(main_tid, SIGTERM);   /* ask main thread to terminate */
326    sleep(1);
327    kill(main_pid, SIGTERM);           /* kill main thread */
328    _exit(0);
329 }
330  
331
332 /*
333  * This is the main routine for Bacula when running as an application,
334  *  or after the service has started up.
335  */
336 int BaculaAppMain()
337 {
338    pthread_t tid;
339    DWORD dwCharsWritten;
340
341    OSDependentInit();
342
343    /* If no arguments were given then just run */
344    if (p_AttachConsole == NULL || !p_AttachConsole(ATTACH_PARENT_PROCESS)) {
345       if (opt_debug) {
346          AllocConsole();
347       }
348    }
349    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), "\r\n", 2, &dwCharsWritten, NULL);
350
351    /* Startup networking */
352    WSA_Init();
353
354    /* Set this process to be the last application to be shut down. */
355    if (p_SetProcessShutdownParameters) {
356       p_SetProcessShutdownParameters(0x100, 0);
357    }
358
359    /* Create a thread to handle the Windows messages */
360    pthread_create(&tid, NULL,  Main_Msg_Loop, (void *)0);
361
362    /* Call the Unix Bacula daemon */
363    BaculaMain(num_command_args, command_args);
364    PostQuitMessage(0);                /* terminate our main message loop */
365
366    WSACleanup();
367    _exit(0);
368 }
369
370
371 void pause_msg(const char *file, const char *func, int line, const char *msg)
372 {
373    char buf[1000];
374    if (msg) {
375       bsnprintf(buf, sizeof(buf), "%s:%s:%d %s", file, func, line, msg);
376    } else {
377       bsnprintf(buf, sizeof(buf), "%s:%s:%d", file, func, line);
378    }
379    MessageBox(NULL, buf, "Pause", MB_OK);
380 }
381
382 #include <tchar.h>
383 #include <stdio.h>
384
385 #ifndef PRODUCT_UNLICENSED
386 #define PRODUCT_UNLICENSED 0xABCDABCD
387 #define PRODUCT_BUSINESS 0x00000006
388 #define PRODUCT_BUSINESS_N 0x00000010
389 #define PRODUCT_CLUSTER_SERVER 0x00000012
390 #define PRODUCT_DATACENTER_SERVER 0x00000008
391 #define PRODUCT_DATACENTER_SERVER_CORE 0x0000000C
392 #define PRODUCT_DATACENTER_SERVER_CORE_V 0x00000027
393 #define PRODUCT_DATACENTER_SERVER_V 0x00000025
394 #define PRODUCT_ENTERPRISE 0x00000004
395 #define PRODUCT_ENTERPRISE_E 0x00000046
396 #define PRODUCT_ENTERPRISE_N 0x0000001B
397 #define PRODUCT_ENTERPRISE_SERVER 0x0000000A
398 #define PRODUCT_ENTERPRISE_SERVER_CORE 0x0000000E
399 #define PRODUCT_ENTERPRISE_SERVER_CORE_V 0x00000029
400 #define PRODUCT_ENTERPRISE_SERVER_IA64 0x0000000F
401 #define PRODUCT_ENTERPRISE_SERVER_V 0x00000026
402 #define PRODUCT_HOME_BASIC 0x00000002
403 #define PRODUCT_HOME_BASIC_E 0x00000043
404 #define PRODUCT_HOME_BASIC_N 0x00000005
405 #define PRODUCT_HOME_PREMIUM 0x00000003
406 #define PRODUCT_HOME_PREMIUM_E 0x00000044
407 #define PRODUCT_HOME_PREMIUM_N 0x0000001A
408 #define PRODUCT_HYPERV 0x0000002A
409 #define PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT 0x0000001E
410 #define PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING 0x00000020
411 #define PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY 0x0000001F
412 #define PRODUCT_PROFESSIONAL 0x00000030
413 #define PRODUCT_PROFESSIONAL_E 0x00000045
414 #define PRODUCT_PROFESSIONAL_N 0x00000031
415 #define PRODUCT_SERVER_FOR_SMALLBUSINESS 0x00000018
416 #define PRODUCT_SERVER_FOR_SMALLBUSINESS_V 0x00000023
417 #define PRODUCT_SERVER_FOUNDATION 0x00000021
418 #define PRODUCT_SMALLBUSINESS_SERVER 0x00000009
419 #define PRODUCT_SOLUTION_EMBEDDEDSERVER 0x00000038
420 #define PRODUCT_STANDARD_SERVER 0x00000007
421 #define PRODUCT_STANDARD_SERVER_CORE 0x0000000D
422 #define PRODUCT_STANDARD_SERVER_CORE_V 0x00000028
423 #define PRODUCT_STANDARD_SERVER_V 0x00000024
424 #define PRODUCT_STARTER 0x0000000B
425 #define PRODUCT_STARTER_E 0x00000042
426 #define PRODUCT_STARTER_N 0x0000002F
427 #define PRODUCT_STORAGE_ENTERPRISE_SERVER 0x00000017
428 #define PRODUCT_STORAGE_EXPRESS_SERVER 0x00000014
429 #define PRODUCT_STORAGE_STANDARD_SERVER 0x00000015
430 #define PRODUCT_STORAGE_WORKGROUP_SERVER  0x00000016
431 #define PRODUCT_UNDEFINED 0x00000000
432 #define PRODUCT_ULTIMATE 0x00000001
433 #define PRODUCT_ULTIMATE_E 0x00000047
434 #define PRODUCT_ULTIMATE_N 0x0000001C
435 #define PRODUCT_WEB_SERVER 0x00000011
436 #define PRODUCT_WEB_SERVER_CORE 0x0000001D
437
438 #define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x19
439 #define SM_SERVERR2 89
440 #define VER_SERVER_NT 0x80000000
441
442 #endif
443
444 #ifndef PRODUCT_PROFESSIONAL
445 #define PRODUCT_PROFESSIONAL 0x00000030
446 #endif
447 #ifndef VER_SUITE_STORAGE_SERVER
448 #define VER_SUITE_STORAGE_SERVER 0x00002000
449 #endif
450 #ifndef VER_SUITE_COMPUTE_SERVER
451 #define VER_SUITE_COMPUTE_SERVER 0x00004000
452 #endif
453
454 /* Unknown value */
455 #ifndef VER_SUITE_WH_SERVER
456 #define VER_SUITE_WH_SERVER -1
457 #endif
458
459 typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
460 typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
461
462 /*
463  * Get Windows version display string
464  */
465 bool GetWindowsVersionString(LPTSTR osbuf, int maxsiz)
466 {
467    OSVERSIONINFOEX osvi;
468    SYSTEM_INFO si;
469    PGNSI pGNSI;
470    PGPI pGPI;
471    BOOL bOsVersionInfoEx;
472    DWORD dwType;
473
474    memset(&si, 0, sizeof(SYSTEM_INFO));
475    memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
476
477    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
478
479    if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
480       return 1;
481
482    // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
483
484    pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), 
485            "GetNativeSystemInfo");
486    if (pGNSI) {
487       pGNSI(&si);
488    } else {
489       GetSystemInfo(&si);
490    }
491
492    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion > 4) { 
493       bstrncpy(osbuf, TEXT("Microsoft "), maxsiz);
494
495       // Test for the specific product.
496
497       if (osvi.dwMajorVersion == 6) {
498          if (osvi.dwMinorVersion == 0) {
499             if (osvi.wProductType == VER_NT_WORKSTATION)
500                 bstrncat(osbuf, TEXT("Windows Vista "), maxsiz);
501             else 
502                 bstrncat(osbuf, TEXT("Windows Server 2008 " ), maxsiz);
503          }
504
505          if (osvi.dwMinorVersion == 1) {
506             if (osvi.wProductType == VER_NT_WORKSTATION )
507                 bstrncat(osbuf, TEXT("Windows 7 "), maxsiz);
508             else 
509                 bstrncat(osbuf, TEXT("Windows Server 2008 R2 " ), maxsiz);
510          }
511          
512          pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), 
513             "GetProductInfo");
514
515          if (pGPI) {
516             pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
517          } else {
518             dwType = PRODUCT_HOME_BASIC;
519          }
520
521          switch (dwType) {
522             case PRODUCT_ULTIMATE:
523                bstrncat(osbuf, TEXT("Ultimate Edition" ), maxsiz);
524                break;
525             case PRODUCT_PROFESSIONAL:
526                bstrncat(osbuf, TEXT("Professional" ), maxsiz);
527                break;
528             case PRODUCT_HOME_PREMIUM:
529                bstrncat(osbuf, TEXT("Home Premium Edition" ), maxsiz);
530                break;
531             case PRODUCT_HOME_BASIC:
532                bstrncat(osbuf, TEXT("Home Basic Edition" ), maxsiz);
533                break;
534             case PRODUCT_ENTERPRISE:
535                bstrncat(osbuf, TEXT("Enterprise Edition" ), maxsiz);
536                break;
537             case PRODUCT_BUSINESS:
538                bstrncat(osbuf, TEXT("Business Edition" ), maxsiz);
539                break;
540             case PRODUCT_STARTER:
541                bstrncat(osbuf, TEXT("Starter Edition" ), maxsiz);
542                break;
543             case PRODUCT_CLUSTER_SERVER:
544                bstrncat(osbuf, TEXT("Cluster Server Edition" ), maxsiz);
545                break;
546             case PRODUCT_DATACENTER_SERVER:
547                bstrncat(osbuf, TEXT("Datacenter Edition" ), maxsiz);
548                break;
549             case PRODUCT_DATACENTER_SERVER_CORE:
550                bstrncat(osbuf, TEXT("Datacenter Edition (core installation)" ), maxsiz);
551                break;
552             case PRODUCT_ENTERPRISE_SERVER:
553                bstrncat(osbuf, TEXT("Enterprise Edition" ), maxsiz);
554                break;
555             case PRODUCT_ENTERPRISE_SERVER_CORE:
556                bstrncat(osbuf, TEXT("Enterprise Edition (core installation)" ), maxsiz);
557                break;
558             case PRODUCT_ENTERPRISE_SERVER_IA64:
559                bstrncat(osbuf, TEXT("Enterprise Edition for Itanium-based Systems" ), maxsiz);
560                break;
561             case PRODUCT_SMALLBUSINESS_SERVER:
562                bstrncat(osbuf, TEXT("Small Business Server" ), maxsiz);
563                break;
564             case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
565                bstrncat(osbuf, TEXT("Small Business Server Premium Edition" ), maxsiz);
566                break;
567             case PRODUCT_STANDARD_SERVER:
568                bstrncat(osbuf, TEXT("Standard Edition" ), maxsiz);
569                break;
570             case PRODUCT_STANDARD_SERVER_CORE:
571                bstrncat(osbuf, TEXT("Standard Edition (core installation)" ), maxsiz);
572                break;
573             case PRODUCT_WEB_SERVER:
574                bstrncat(osbuf, TEXT("Web Server Edition" ), maxsiz);
575                break;
576          }
577       }
578
579       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
580          if( GetSystemMetrics(SM_SERVERR2) )
581             bstrncat(osbuf, TEXT( "Windows Server 2003 R2 "), maxsiz);
582          else if (osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER)
583             bstrncat(osbuf, TEXT( "Windows Storage Server 2003"), maxsiz);
584          else if (osvi.wSuiteMask & VER_SUITE_WH_SERVER )
585             bstrncat(osbuf, TEXT( "Windows Home Server"), maxsiz);
586          else if (osvi.wProductType == VER_NT_WORKSTATION &&
587                   si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
588             bstrncat(osbuf, TEXT( "Windows XP Professional x64 Edition"), maxsiz);
589          else 
590             bstrncat(osbuf, TEXT("Windows Server 2003 "), maxsiz);
591
592          // Test for the server type.
593          if (osvi.wProductType != VER_NT_WORKSTATION) {
594             if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64) {
595                 if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
596                    bstrncat(osbuf, TEXT( "Datacenter Edition for Itanium-based Systems" ), maxsiz);
597                 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
598                    bstrncat(osbuf, TEXT( "Enterprise Edition for Itanium-based Systems" ), maxsiz);
599             }
600
601             else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) {
602                 if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
603                    bstrncat(osbuf, TEXT( "Datacenter x64 Edition" ), maxsiz);
604                 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
605                    bstrncat(osbuf, TEXT( "Enterprise x64 Edition" ), maxsiz);
606                 else bstrncat(osbuf, TEXT( "Standard x64 Edition" ), maxsiz);
607             } else {
608                 if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
609                    bstrncat(osbuf, TEXT( "Compute Cluster Edition" ), maxsiz);
610                 else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
611                    bstrncat(osbuf, TEXT( "Datacenter Edition" ), maxsiz);
612                 else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
613                    bstrncat(osbuf, TEXT( "Enterprise Edition" ), maxsiz);
614                 else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
615                    bstrncat(osbuf, TEXT( "Web Edition" ), maxsiz);
616                 else bstrncat(osbuf, TEXT( "Standard Edition" ), maxsiz);
617             }
618          }
619       }
620
621       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
622          bstrncat(osbuf, TEXT("Windows XP "), maxsiz);
623          if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
624             bstrncat(osbuf, TEXT( "Home Edition" ), maxsiz);
625          else 
626             bstrncat(osbuf, TEXT( "Professional" ), maxsiz);
627       }
628
629       if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
630          bstrncat(osbuf, TEXT("Windows 2000 "), maxsiz);
631          if ( osvi.wProductType == VER_NT_WORKSTATION ) {
632             bstrncat(osbuf, TEXT( "Professional" ), maxsiz);
633          } else {
634             if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
635                bstrncat(osbuf, TEXT( "Datacenter Server" ), maxsiz);
636             else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
637                bstrncat(osbuf, TEXT( "Advanced Server" ), maxsiz);
638             else bstrncat(osbuf, TEXT( "Server" ), maxsiz);
639          }
640       }
641
642        // Include service pack (if any) and build number.
643
644       if (_tcslen(osvi.szCSDVersion) > 0) {
645           bstrncat(osbuf, TEXT(" ") , maxsiz);
646           bstrncat(osbuf, osvi.szCSDVersion, maxsiz);
647       }
648
649       char buf[80];
650
651       snprintf(buf, 80, " (build %d)", (int)osvi.dwBuildNumber);
652       bstrncat(osbuf, buf, maxsiz);
653
654       if (osvi.dwMajorVersion >= 6) {
655          if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
656             bstrncat(osbuf, TEXT( ", 64-bit" ), maxsiz);
657          else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
658             bstrncat(osbuf, TEXT(", 32-bit"), maxsiz);
659       }
660       
661       return true; 
662    } else {
663       bstrncpy(osbuf, "Unknown Windows version.", maxsiz);
664       return true;
665    }
666 }