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