Silence Borland compiler warnings (except for warnings from zlib) here:
[p5sagit/p5-mst-13.2.git] / ext / Win32 / Win32.xs
1 #include <wctype.h>
2 #include <windows.h>
3 #include <shlobj.h>
4
5 #define PERL_NO_GET_CONTEXT
6 #include "EXTERN.h"
7 #include "perl.h"
8 #include "XSUB.h"
9
10 #ifndef countof
11 #  define countof(array) (sizeof (array) / sizeof (*(array)))
12 #endif
13
14 #define SE_SHUTDOWN_NAMEA   "SeShutdownPrivilege"
15
16 #ifndef WC_NO_BEST_FIT_CHARS
17 #  define WC_NO_BEST_FIT_CHARS 0x00000400
18 #endif
19
20 #define GETPROC(fn) pfn##fn = (PFN##fn)GetProcAddress(module, #fn)
21
22 typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathA)(HWND, char*, int, BOOL);
23 typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathW)(HWND, WCHAR*, int, BOOL);
24 typedef HRESULT (WINAPI *PFNSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPTSTR);
25 typedef HRESULT (WINAPI *PFNSHGetFolderPathW)(HWND, int, HANDLE, DWORD, LPWSTR);
26 typedef BOOL (WINAPI *PFNCreateEnvironmentBlock)(void**, HANDLE, BOOL);
27 typedef BOOL (WINAPI *PFNDestroyEnvironmentBlock)(void*);
28 typedef int (__stdcall *PFNDllRegisterServer)(void);
29 typedef int (__stdcall *PFNDllUnregisterServer)(void);
30 typedef DWORD (__stdcall *PFNNetApiBufferFree)(void*);
31 typedef DWORD (__stdcall *PFNNetWkstaGetInfo)(LPWSTR, DWORD, void*);
32
33 typedef BOOL (__stdcall *PFNOpenProcessToken)(HANDLE, DWORD, HANDLE*);
34 typedef BOOL (__stdcall *PFNOpenThreadToken)(HANDLE, DWORD, BOOL, HANDLE*);
35 typedef BOOL (__stdcall *PFNGetTokenInformation)(HANDLE, TOKEN_INFORMATION_CLASS, void*, DWORD, DWORD*);
36 typedef BOOL (__stdcall *PFNAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD,
37                                                       DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
38 typedef BOOL (__stdcall *PFNEqualSid)(PSID, PSID);
39 typedef void* (__stdcall *PFNFreeSid)(PSID);
40 typedef BOOL (__stdcall *PFNIsUserAnAdmin)(void);
41
42 #ifndef CSIDL_MYMUSIC
43 #   define CSIDL_MYMUSIC              0x000D
44 #endif
45 #ifndef CSIDL_MYVIDEO
46 #   define CSIDL_MYVIDEO              0x000E
47 #endif
48 #ifndef CSIDL_LOCAL_APPDATA
49 #   define CSIDL_LOCAL_APPDATA        0x001C
50 #endif
51 #ifndef CSIDL_COMMON_FAVORITES
52 #   define CSIDL_COMMON_FAVORITES     0x001F
53 #endif
54 #ifndef CSIDL_INTERNET_CACHE
55 #   define CSIDL_INTERNET_CACHE       0x0020
56 #endif
57 #ifndef CSIDL_COOKIES
58 #   define CSIDL_COOKIES              0x0021
59 #endif
60 #ifndef CSIDL_HISTORY
61 #   define CSIDL_HISTORY              0x0022
62 #endif
63 #ifndef CSIDL_COMMON_APPDATA
64 #   define CSIDL_COMMON_APPDATA       0x0023
65 #endif
66 #ifndef CSIDL_WINDOWS
67 #   define CSIDL_WINDOWS              0x0024
68 #endif
69 #ifndef CSIDL_PROGRAM_FILES
70 #   define CSIDL_PROGRAM_FILES        0x0026
71 #endif
72 #ifndef CSIDL_MYPICTURES
73 #   define CSIDL_MYPICTURES           0x0027
74 #endif
75 #ifndef CSIDL_PROFILE
76 #   define CSIDL_PROFILE              0x0028
77 #endif
78 #ifndef CSIDL_PROGRAM_FILES_COMMON
79 #   define CSIDL_PROGRAM_FILES_COMMON 0x002B
80 #endif
81 #ifndef CSIDL_COMMON_TEMPLATES
82 #   define CSIDL_COMMON_TEMPLATES     0x002D
83 #endif
84 #ifndef CSIDL_COMMON_DOCUMENTS
85 #   define CSIDL_COMMON_DOCUMENTS     0x002E
86 #endif
87 #ifndef CSIDL_COMMON_ADMINTOOLS
88 #   define CSIDL_COMMON_ADMINTOOLS    0x002F
89 #endif
90 #ifndef CSIDL_ADMINTOOLS
91 #   define CSIDL_ADMINTOOLS           0x0030
92 #endif
93 #ifndef CSIDL_COMMON_MUSIC
94 #   define CSIDL_COMMON_MUSIC         0x0035
95 #endif
96 #ifndef CSIDL_COMMON_PICTURES
97 #   define CSIDL_COMMON_PICTURES      0x0036
98 #endif
99 #ifndef CSIDL_COMMON_VIDEO
100 #   define CSIDL_COMMON_VIDEO         0x0037
101 #endif
102 #ifndef CSIDL_CDBURN_AREA
103 #   define CSIDL_CDBURN_AREA          0x003B
104 #endif
105 #ifndef CSIDL_FLAG_CREATE
106 #   define CSIDL_FLAG_CREATE          0x8000
107 #endif
108
109 /* Use explicit struct definition because wSuiteMask and
110  * wProductType are not defined in the VC++ 6.0 headers.
111  * WORD type has been replaced by unsigned short because
112  * WORD is already used by Perl itself.
113  */
114 struct {
115     DWORD dwOSVersionInfoSize;
116     DWORD dwMajorVersion;
117     DWORD dwMinorVersion;
118     DWORD dwBuildNumber;
119     DWORD dwPlatformId;
120     CHAR  szCSDVersion[128];
121     unsigned short wServicePackMajor;
122     unsigned short wServicePackMinor;
123     unsigned short wSuiteMask;
124     BYTE  wProductType;
125     BYTE  wReserved;
126 }   g_osver = {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0};
127 BOOL g_osver_ex = TRUE;
128
129 #define ONE_K_BUFSIZE   1024
130
131 int
132 IsWin95(void)
133 {
134     return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
135 }
136
137 int
138 IsWinNT(void)
139 {
140     return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT);
141 }
142
143 int
144 IsWin2000(void)
145 {
146     return (g_osver.dwMajorVersion > 4);
147 }
148
149 /* Convert SV to wide character string.  The return value must be
150  * freed using Safefree().
151  */
152 WCHAR*
153 sv_to_wstr(pTHX_ SV *sv)
154 {
155     DWORD wlen;
156     WCHAR *wstr;
157     STRLEN len;
158     char *str = SvPV(sv, len);
159     UINT cp = SvUTF8(sv) ? CP_UTF8 : CP_ACP;
160
161     wlen = MultiByteToWideChar(cp, 0, str, len+1, NULL, 0);
162     New(0, wstr, wlen, WCHAR);
163     MultiByteToWideChar(cp, 0, str, len+1, wstr, wlen);
164
165     return wstr;
166 }
167
168 /* Convert wide character string to mortal SV.  Use UTF8 encoding
169  * if the string cannot be represented in the system codepage.
170  */
171 SV *
172 wstr_to_sv(pTHX_ WCHAR *wstr)
173 {
174     size_t wlen = wcslen(wstr)+1;
175     BOOL use_default = FALSE;
176     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, NULL, 0, NULL, NULL);
177     SV *sv = sv_2mortal(newSV(len));
178
179     len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, SvPVX(sv), len, NULL, &use_default);
180     if (use_default) {
181         len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, NULL, 0, NULL, NULL);
182         sv_grow(sv, len);
183         len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, SvPVX(sv), len, NULL, NULL);
184         SvUTF8_on(sv);
185     }
186     /* Shouldn't really ever fail since we ask for the required length first, but who knows... */
187     if (len) {
188         SvPOK_on(sv);
189         SvCUR_set(sv, len-1);
190     }
191     return sv;
192 }
193
194 /* Retrieve a variable from the Unicode environment in a mortal SV.
195  *
196  * Recreates the Unicode environment because a bug in earlier Perl versions
197  * overwrites it with the ANSI version, which contains replacement
198  * characters for the characters not in the ANSI codepage.
199  */
200 SV*
201 get_unicode_env(pTHX_ WCHAR *name)
202 {
203     SV *sv = NULL;
204     void *env;
205     HANDLE token;
206     HMODULE module;
207     PFNOpenProcessToken pfnOpenProcessToken;
208
209     /* Get security token for the current process owner */
210     module = LoadLibrary("advapi32.dll");
211     if (!module)
212         return NULL;
213
214     GETPROC(OpenProcessToken);
215
216     if (pfnOpenProcessToken == NULL ||
217         !pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &token))
218     {
219         FreeLibrary(module);
220         return NULL;
221     }
222     FreeLibrary(module);
223
224     /* Create a Unicode environment block for this process */
225     module = LoadLibrary("userenv.dll");
226     if (module) {
227         PFNCreateEnvironmentBlock pfnCreateEnvironmentBlock;
228         PFNDestroyEnvironmentBlock pfnDestroyEnvironmentBlock;
229
230         GETPROC(CreateEnvironmentBlock);
231         GETPROC(DestroyEnvironmentBlock);
232
233         if (pfnCreateEnvironmentBlock && pfnDestroyEnvironmentBlock &&
234             pfnCreateEnvironmentBlock(&env, token, FALSE))
235         {
236             size_t name_len = wcslen(name);
237             WCHAR *entry = env;
238             while (*entry) {
239                 size_t i;
240                 size_t entry_len = wcslen(entry);
241                 BOOL equal = (entry_len > name_len) && (entry[name_len] == '=');
242
243                 for (i=0; equal && i < name_len; ++i)
244                     equal = (towupper(entry[i]) == towupper(name[i]));
245
246                 if (equal) {
247                     sv = wstr_to_sv(aTHX_ entry+name_len+1);
248                     break;
249                 }
250                 entry += entry_len+1;
251             }
252             pfnDestroyEnvironmentBlock(env);
253         }
254         FreeLibrary(module);
255     }
256     CloseHandle(token);
257     return sv;
258 }
259
260 /* Define both an ANSI and a Wide version of win32_longpath */
261
262 #define CHAR_T            char
263 #define WIN32_FIND_DATA_T WIN32_FIND_DATAA
264 #define FN_FINDFIRSTFILE  FindFirstFileA
265 #define FN_STRLEN         strlen
266 #define FN_STRCPY         strcpy
267 #define LONGPATH          my_longpathA
268 #include "longpath.inc"
269
270 #define CHAR_T            WCHAR
271 #define WIN32_FIND_DATA_T WIN32_FIND_DATAW
272 #define FN_FINDFIRSTFILE  FindFirstFileW
273 #define FN_STRLEN         wcslen
274 #define FN_STRCPY         wcscpy
275 #define LONGPATH          my_longpathW
276 #include "longpath.inc"
277
278 /* The my_ansipath() function takes a Unicode filename and converts it
279  * into the current Windows codepage. If some characters cannot be mapped,
280  * then it will convert the short name instead.
281  *
282  * The buffer to the ansi pathname must be freed with Safefree() when it
283  * it no longer needed.
284  *
285  * The argument to my_ansipath() must exist before this function is
286  * called; otherwise there is no way to determine the short path name.
287  *
288  * Ideas for future refinement:
289  * - Only convert those segments of the path that are not in the current
290  *   codepage, but leave the other segments in their long form.
291  * - If the resulting name is longer than MAX_PATH, start converting
292  *   additional path segments into short names until the full name
293  *   is shorter than MAX_PATH.  Shorten the filename part last!
294  */
295
296 /* This is a modified version of core Perl win32/win32.c(win32_ansipath).
297  * It uses New() etc. instead of win32_malloc().
298  */
299
300 char *
301 my_ansipath(const WCHAR *widename)
302 {
303     char *name;
304     BOOL use_default = FALSE;
305     size_t widelen = wcslen(widename)+1;
306     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
307                                   NULL, 0, NULL, NULL);
308     New(0, name, len, char);
309     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
310                         name, len, NULL, &use_default);
311     if (use_default) {
312         DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
313         if (shortlen) {
314             WCHAR *shortname;
315             New(0, shortname, shortlen, WCHAR);
316             shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
317
318             len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
319                                       NULL, 0, NULL, NULL);
320             Renew(name, len, char);
321             WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
322                                 name, len, NULL, NULL);
323             Safefree(shortname);
324         }
325     }
326     return name;
327 }
328
329 /* Convert wide character path to ANSI path and return as mortal SV. */
330 SV*
331 wstr_to_ansipath(pTHX_ WCHAR *wstr)
332 {
333     char *ansi = my_ansipath(wstr);
334     SV *sv = sv_2mortal(newSVpvn(ansi, strlen(ansi)));
335     Safefree(ansi);
336     return sv;
337 }
338
339 #ifdef __CYGWIN__
340
341 char*
342 get_childdir(void)
343 {
344     dTHX;
345     char* ptr;
346
347     if (IsWin2000()) {
348         WCHAR filename[MAX_PATH+1];
349         GetCurrentDirectoryW(MAX_PATH+1, filename);
350         ptr = my_ansipath(filename);
351     }
352     else {
353         char filename[MAX_PATH+1];
354         GetCurrentDirectoryA(MAX_PATH+1, filename);
355         New(0, ptr, strlen(filename)+1, char);
356         strcpy(ptr, filename);
357     }
358     return ptr;
359 }
360
361 void
362 free_childdir(char *d)
363 {
364     dTHX;
365     Safefree(d);
366 }
367
368 void*
369 get_childenv(void)
370 {
371     return NULL;
372 }
373
374 void
375 free_childenv(void *d)
376 {
377 }
378
379 #  define PerlDir_mapA(dir) (dir)
380
381 #endif
382
383 XS(w32_ExpandEnvironmentStrings)
384 {
385     dXSARGS;
386
387     if (items != 1)
388         croak("usage: Win32::ExpandEnvironmentStrings($String);\n");
389
390     if (IsWin2000()) {
391         WCHAR value[31*1024];
392         WCHAR *source = sv_to_wstr(aTHX_ ST(0));
393         ExpandEnvironmentStringsW(source, value, countof(value)-1);
394         ST(0) = wstr_to_sv(aTHX_ value);
395         Safefree(source);
396         XSRETURN(1);
397     }
398     else {
399         char value[31*1024];
400         ExpandEnvironmentStringsA(SvPV_nolen(ST(0)), value, countof(value)-2);
401         XSRETURN_PV(value);
402     }
403 }
404
405 XS(w32_IsAdminUser)
406 {
407     dXSARGS;
408     HMODULE                     module;
409     PFNIsUserAnAdmin            pfnIsUserAnAdmin;
410     PFNOpenThreadToken          pfnOpenThreadToken;
411     PFNOpenProcessToken         pfnOpenProcessToken;
412     PFNGetTokenInformation      pfnGetTokenInformation;
413     PFNAllocateAndInitializeSid pfnAllocateAndInitializeSid;
414     PFNEqualSid                 pfnEqualSid;
415     PFNFreeSid                  pfnFreeSid;
416     HANDLE                      hTok;
417     DWORD                       dwTokInfoLen;
418     TOKEN_GROUPS                *lpTokInfo;
419     SID_IDENTIFIER_AUTHORITY    NtAuth = SECURITY_NT_AUTHORITY;
420     PSID                        pAdminSid;
421     int                         iRetVal;
422     unsigned int                i;
423
424     if (items)
425         croak("usage: Win32::IsAdminUser()");
426
427     /* There is no concept of "Administrator" user accounts on Win9x systems,
428        so just return true. */
429     if (IsWin95())
430         XSRETURN_YES;
431
432     /* Use IsUserAnAdmin() when available.  On Vista this will only return TRUE
433      * if the process is running with elevated privileges and not just when the
434      * process owner is a member of the "Administrators" group.
435      */
436     module = LoadLibrary("shell32.dll");
437     if (module) {
438         GETPROC(IsUserAnAdmin);
439         if (pfnIsUserAnAdmin) {
440             EXTEND(SP, 1);
441             ST(0) = sv_2mortal(newSViv(pfnIsUserAnAdmin() ? 1 : 0));
442             FreeLibrary(module);
443             XSRETURN(1);
444         }
445         FreeLibrary(module);
446     }
447
448     module = LoadLibrary("advapi32.dll");
449     if (!module) {
450         warn("Cannot load advapi32.dll library");
451         XSRETURN_UNDEF;
452     }
453
454     GETPROC(OpenThreadToken);
455     GETPROC(OpenProcessToken);
456     GETPROC(GetTokenInformation);
457     GETPROC(AllocateAndInitializeSid);
458     GETPROC(EqualSid);
459     GETPROC(FreeSid);
460
461     if (!(pfnOpenThreadToken && pfnOpenProcessToken &&
462           pfnGetTokenInformation && pfnAllocateAndInitializeSid &&
463           pfnEqualSid && pfnFreeSid))
464     {
465         warn("Cannot load functions from advapi32.dll library");
466         FreeLibrary(module);
467         XSRETURN_UNDEF;
468     }
469
470     if (!pfnOpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hTok)) {
471         if (!pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTok)) {
472             warn("Cannot open thread token or process token");
473             FreeLibrary(module);
474             XSRETURN_UNDEF;
475         }
476     }
477
478     pfnGetTokenInformation(hTok, TokenGroups, NULL, 0, &dwTokInfoLen);
479     if (!New(1, lpTokInfo, dwTokInfoLen, TOKEN_GROUPS)) {
480         warn("Cannot allocate token information structure");
481         CloseHandle(hTok);
482         FreeLibrary(module);
483         XSRETURN_UNDEF;
484     }
485
486     if (!pfnGetTokenInformation(hTok, TokenGroups, lpTokInfo, dwTokInfoLen,
487             &dwTokInfoLen))
488     {
489         warn("Cannot get token information");
490         Safefree(lpTokInfo);
491         CloseHandle(hTok);
492         FreeLibrary(module);
493         XSRETURN_UNDEF;
494     }
495
496     if (!pfnAllocateAndInitializeSid(&NtAuth, 2, SECURITY_BUILTIN_DOMAIN_RID,
497             DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid))
498     {
499         warn("Cannot allocate administrators' SID");
500         Safefree(lpTokInfo);
501         CloseHandle(hTok);
502         FreeLibrary(module);
503         XSRETURN_UNDEF;
504     }
505
506     iRetVal = 0;
507     for (i = 0; i < lpTokInfo->GroupCount; ++i) {
508         if (pfnEqualSid(lpTokInfo->Groups[i].Sid, pAdminSid)) {
509             iRetVal = 1;
510             break;
511         }
512     }
513
514     pfnFreeSid(pAdminSid);
515     Safefree(lpTokInfo);
516     CloseHandle(hTok);
517     FreeLibrary(module);
518
519     EXTEND(SP, 1);
520     ST(0) = sv_2mortal(newSViv(iRetVal));
521     XSRETURN(1);
522 }
523
524 XS(w32_LookupAccountName)
525 {
526     dXSARGS;
527     char SID[400];
528     DWORD SIDLen;
529     SID_NAME_USE snu;
530     char Domain[256];
531     DWORD DomLen;
532     BOOL bResult;
533
534     if (items != 5)
535         croak("usage: Win32::LookupAccountName($system, $account, $domain, "
536               "$sid, $sidtype);\n");
537
538     SIDLen = sizeof(SID);
539     DomLen = sizeof(Domain);
540
541     bResult = LookupAccountNameA(SvPV_nolen(ST(0)),     /* System */
542                                  SvPV_nolen(ST(1)),     /* Account name */
543                                  &SID,                  /* SID structure */
544                                  &SIDLen,               /* Size of SID buffer */
545                                  Domain,                /* Domain buffer */
546                                  &DomLen,               /* Domain buffer size */
547                                  &snu);                 /* SID name type */
548     if (bResult) {
549         sv_setpv(ST(2), Domain);
550         sv_setpvn(ST(3), SID, SIDLen);
551         sv_setiv(ST(4), snu);
552         XSRETURN_YES;
553     }
554     XSRETURN_NO;
555 }
556
557
558 XS(w32_LookupAccountSID)
559 {
560     dXSARGS;
561     PSID sid;
562     char Account[256];
563     DWORD AcctLen = sizeof(Account);
564     char Domain[256];
565     DWORD DomLen = sizeof(Domain);
566     SID_NAME_USE snu;
567     BOOL bResult;
568
569     if (items != 5)
570         croak("usage: Win32::LookupAccountSID($system, $sid, $account, $domain, $sidtype);\n");
571
572     sid = SvPV_nolen(ST(1));
573     if (IsValidSid(sid)) {
574         bResult = LookupAccountSidA(SvPV_nolen(ST(0)),  /* System */
575                                     sid,                /* SID structure */
576                                     Account,            /* Account name buffer */
577                                     &AcctLen,           /* name buffer length */
578                                     Domain,             /* Domain buffer */
579                                     &DomLen,            /* Domain buffer length */
580                                     &snu);              /* SID name type */
581         if (bResult) {
582             sv_setpv(ST(2), Account);
583             sv_setpv(ST(3), Domain);
584             sv_setiv(ST(4), (IV)snu);
585             XSRETURN_YES;
586         }
587     }
588     XSRETURN_NO;
589 }
590
591 XS(w32_InitiateSystemShutdown)
592 {
593     dXSARGS;
594     HANDLE hToken;              /* handle to process token   */
595     TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */
596     BOOL bRet;
597     char *machineName, *message;
598
599     if (items != 5)
600         croak("usage: Win32::InitiateSystemShutdown($machineName, $message, "
601               "$timeOut, $forceClose, $reboot);\n");
602
603     machineName = SvPV_nolen(ST(0));
604
605     if (OpenProcessToken(GetCurrentProcess(),
606                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
607                          &hToken))
608     {
609         LookupPrivilegeValueA(machineName,
610                               SE_SHUTDOWN_NAMEA,
611                               &tkp.Privileges[0].Luid);
612
613         tkp.PrivilegeCount = 1; /* only setting one */
614         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
615
616         /* Get shutdown privilege for this process. */
617         AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
618                               (PTOKEN_PRIVILEGES)NULL, 0);
619     }
620
621     message = SvPV_nolen(ST(1));
622     bRet = InitiateSystemShutdownA(machineName, message,
623                                    SvIV(ST(2)), SvIV(ST(3)), SvIV(ST(4)));
624
625     /* Disable shutdown privilege. */
626     tkp.Privileges[0].Attributes = 0; 
627     AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
628                           (PTOKEN_PRIVILEGES)NULL, 0); 
629     CloseHandle(hToken);
630     XSRETURN_IV(bRet);
631 }
632
633 XS(w32_AbortSystemShutdown)
634 {
635     dXSARGS;
636     HANDLE hToken;              /* handle to process token   */
637     TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */
638     BOOL bRet;
639     char *machineName;
640
641     if (items != 1)
642         croak("usage: Win32::AbortSystemShutdown($machineName);\n");
643
644     machineName = SvPV_nolen(ST(0));
645
646     if (OpenProcessToken(GetCurrentProcess(),
647                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
648                          &hToken))
649     {
650         LookupPrivilegeValueA(machineName,
651                               SE_SHUTDOWN_NAMEA,
652                               &tkp.Privileges[0].Luid);
653
654         tkp.PrivilegeCount = 1; /* only setting one */
655         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
656
657         /* Get shutdown privilege for this process. */
658         AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
659                               (PTOKEN_PRIVILEGES)NULL, 0);
660     }
661
662     bRet = AbortSystemShutdownA(machineName);
663
664     /* Disable shutdown privilege. */
665     tkp.Privileges[0].Attributes = 0;
666     AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
667                           (PTOKEN_PRIVILEGES)NULL, 0);
668     CloseHandle(hToken);
669     XSRETURN_IV(bRet);
670 }
671
672
673 XS(w32_MsgBox)
674 {
675     dXSARGS;
676     DWORD flags = MB_ICONEXCLAMATION;
677     I32 result;
678
679     if (items < 1 || items > 3)
680         croak("usage: Win32::MsgBox($message [, $flags [, $title]]);\n");
681
682     if (items > 1)
683         flags = SvIV(ST(1));
684
685     if (IsWin2000()) {
686         WCHAR *title = NULL;
687         WCHAR *msg = sv_to_wstr(aTHX_ ST(0));
688         if (items > 2)
689             title = sv_to_wstr(aTHX_ ST(2));
690         result = MessageBoxW(GetActiveWindow(), msg, title ? title : L"Perl", flags);
691         Safefree(msg);
692         if (title)
693             Safefree(title);
694     }
695     else {
696         char *title = "Perl";
697         char *msg = SvPV_nolen(ST(0));
698         if (items > 2)
699             title = SvPV_nolen(ST(2));
700         result = MessageBoxA(GetActiveWindow(), msg, title, flags);
701     }
702     XSRETURN_IV(result);
703 }
704
705 XS(w32_LoadLibrary)
706 {
707     dXSARGS;
708     HANDLE hHandle;
709
710     if (items != 1)
711         croak("usage: Win32::LoadLibrary($libname)\n");
712     hHandle = LoadLibraryA(SvPV_nolen(ST(0)));
713     XSRETURN_IV((long)hHandle);
714 }
715
716 XS(w32_FreeLibrary)
717 {
718     dXSARGS;
719
720     if (items != 1)
721         croak("usage: Win32::FreeLibrary($handle)\n");
722     if (FreeLibrary(INT2PTR(HINSTANCE, SvIV(ST(0))))) {
723         XSRETURN_YES;
724     }
725     XSRETURN_NO;
726 }
727
728 XS(w32_GetProcAddress)
729 {
730     dXSARGS;
731
732     if (items != 2)
733         croak("usage: Win32::GetProcAddress($hinstance, $procname)\n");
734     XSRETURN_IV(PTR2IV(GetProcAddress(INT2PTR(HINSTANCE, SvIV(ST(0))), SvPV_nolen(ST(1)))));
735 }
736
737 XS(w32_RegisterServer)
738 {
739     dXSARGS;
740     BOOL result = FALSE;
741     HMODULE module;
742
743     if (items != 1)
744         croak("usage: Win32::RegisterServer($libname)\n");
745
746     module = LoadLibraryA(SvPV_nolen(ST(0)));
747     if (module) {
748         PFNDllRegisterServer pfnDllRegisterServer;
749         GETPROC(DllRegisterServer);
750         if (pfnDllRegisterServer && pfnDllRegisterServer() == 0)
751             result = TRUE;
752         FreeLibrary(module);
753     }
754     ST(0) = boolSV(result);
755     XSRETURN(1);
756 }
757
758 XS(w32_UnregisterServer)
759 {
760     dXSARGS;
761     BOOL result = FALSE;
762     HINSTANCE module;
763
764     if (items != 1)
765         croak("usage: Win32::UnregisterServer($libname)\n");
766
767     module = LoadLibraryA(SvPV_nolen(ST(0)));
768     if (module) {
769         PFNDllUnregisterServer pfnDllUnregisterServer;
770         GETPROC(DllUnregisterServer);
771         if (pfnDllUnregisterServer && pfnDllUnregisterServer() == 0)
772             result = TRUE;
773         FreeLibrary(module);
774     }
775     ST(0) = boolSV(result);
776     XSRETURN(1);
777 }
778
779 /* XXX rather bogus */
780 XS(w32_GetArchName)
781 {
782     dXSARGS;
783     XSRETURN_PV(getenv("PROCESSOR_ARCHITECTURE"));
784 }
785
786 XS(w32_GetChipName)
787 {
788     dXSARGS;
789     SYSTEM_INFO sysinfo;
790
791     Zero(&sysinfo,1,SYSTEM_INFO);
792     GetSystemInfo(&sysinfo);
793     /* XXX docs say dwProcessorType is deprecated on NT */
794     XSRETURN_IV(sysinfo.dwProcessorType);
795 }
796
797 XS(w32_GuidGen)
798 {
799     dXSARGS;
800     GUID guid;
801     char szGUID[50] = {'\0'};
802     HRESULT  hr     = CoCreateGuid(&guid);
803
804     if (SUCCEEDED(hr)) {
805         LPOLESTR pStr = NULL;
806         if (SUCCEEDED(StringFromCLSID(&guid, &pStr))) {
807             WideCharToMultiByte(CP_ACP, 0, pStr, wcslen(pStr), szGUID,
808                                 sizeof(szGUID), NULL, NULL);
809             CoTaskMemFree(pStr);
810             XSRETURN_PV(szGUID);
811         }
812     }
813     XSRETURN_UNDEF;
814 }
815
816 XS(w32_GetFolderPath)
817 {
818     dXSARGS;
819     char path[MAX_PATH+1];
820     WCHAR wpath[MAX_PATH+1];
821     int folder;
822     int create = 0;
823     HMODULE module;
824
825     if (items != 1 && items != 2)
826         croak("usage: Win32::GetFolderPath($csidl [, $create])\n");
827
828     folder = SvIV(ST(0));
829     if (items == 2)
830         create = SvTRUE(ST(1)) ? CSIDL_FLAG_CREATE : 0;
831
832     module = LoadLibrary("shfolder.dll");
833     if (module) {
834         PFNSHGetFolderPathA pfna;
835         if (IsWin2000()) {
836             PFNSHGetFolderPathW pfnw;
837             pfnw = (PFNSHGetFolderPathW)GetProcAddress(module, "SHGetFolderPathW");
838             if (pfnw && SUCCEEDED(pfnw(NULL, folder|create, NULL, 0, wpath))) {
839                 FreeLibrary(module);
840                 ST(0) = wstr_to_ansipath(aTHX_ wpath);
841                 XSRETURN(1);
842             }
843         }
844         pfna = (PFNSHGetFolderPathA)GetProcAddress(module, "SHGetFolderPathA");
845         if (pfna && SUCCEEDED(pfna(NULL, folder|create, NULL, 0, path))) {
846             FreeLibrary(module);
847             XSRETURN_PV(path);
848         }
849         FreeLibrary(module);
850     }
851
852     module = LoadLibrary("shell32.dll");
853     if (module) {
854         PFNSHGetSpecialFolderPathA pfna;
855         if (IsWin2000()) {
856             PFNSHGetSpecialFolderPathW pfnw;
857             pfnw = (PFNSHGetSpecialFolderPathW)GetProcAddress(module, "SHGetSpecialFolderPathW");
858             if (pfnw && pfnw(NULL, wpath, folder, !!create)) {
859                 FreeLibrary(module);
860                 ST(0) = wstr_to_ansipath(aTHX_ wpath);
861                 XSRETURN(1);
862             }
863         }
864         pfna = (PFNSHGetSpecialFolderPathA)GetProcAddress(module, "SHGetSpecialFolderPathA");
865         if (pfna && pfna(NULL, path, folder, !!create)) {
866             FreeLibrary(module);
867             XSRETURN_PV(path);
868         }
869         FreeLibrary(module);
870     }
871
872     /* SHGetFolderPathW() and SHGetSpecialFolderPathW() may fail on older
873      * Perl versions that have replaced the Unicode environment with an
874      * ANSI version.  Let's go spelunking in the registry now...
875      */
876     if (IsWin2000()) {
877         SV *sv;
878         HKEY hkey;
879         HKEY root = HKEY_CURRENT_USER;
880         WCHAR *name = NULL;
881
882         switch (folder) {
883         case CSIDL_ADMINTOOLS:                  name = L"Administrative Tools";        break;
884         case CSIDL_APPDATA:                     name = L"AppData";                     break;
885         case CSIDL_CDBURN_AREA:                 name = L"CD Burning";                  break;
886         case CSIDL_COOKIES:                     name = L"Cookies";                     break;
887         case CSIDL_DESKTOP:
888         case CSIDL_DESKTOPDIRECTORY:            name = L"Desktop";                     break;
889         case CSIDL_FAVORITES:                   name = L"Favorites";                   break;
890         case CSIDL_FONTS:                       name = L"Fonts";                       break;
891         case CSIDL_HISTORY:                     name = L"History";                     break;
892         case CSIDL_INTERNET_CACHE:              name = L"Cache";                       break;
893         case CSIDL_LOCAL_APPDATA:               name = L"Local AppData";               break;
894         case CSIDL_MYMUSIC:                     name = L"My Music";                    break;
895         case CSIDL_MYPICTURES:                  name = L"My Pictures";                 break;
896         case CSIDL_MYVIDEO:                     name = L"My Video";                    break;
897         case CSIDL_NETHOOD:                     name = L"NetHood";                     break;
898         case CSIDL_PERSONAL:                    name = L"Personal";                    break;
899         case CSIDL_PRINTHOOD:                   name = L"PrintHood";                   break;
900         case CSIDL_PROGRAMS:                    name = L"Programs";                    break;
901         case CSIDL_RECENT:                      name = L"Recent";                      break;
902         case CSIDL_SENDTO:                      name = L"SendTo";                      break;
903         case CSIDL_STARTMENU:                   name = L"Start Menu";                  break;
904         case CSIDL_STARTUP:                     name = L"Startup";                     break;
905         case CSIDL_TEMPLATES:                   name = L"Templates";                   break;
906             /* XXX L"Local Settings" */
907         }
908
909         if (!name) {
910             root = HKEY_LOCAL_MACHINE;
911             switch (folder) {
912             case CSIDL_COMMON_ADMINTOOLS:       name = L"Common Administrative Tools"; break;
913             case CSIDL_COMMON_APPDATA:          name = L"Common AppData";              break;
914             case CSIDL_COMMON_DESKTOPDIRECTORY: name = L"Common Desktop";              break;
915             case CSIDL_COMMON_DOCUMENTS:        name = L"Common Documents";            break;
916             case CSIDL_COMMON_FAVORITES:        name = L"Common Favorites";            break;
917             case CSIDL_COMMON_PROGRAMS:         name = L"Common Programs";             break;
918             case CSIDL_COMMON_STARTMENU:        name = L"Common Start Menu";           break;
919             case CSIDL_COMMON_STARTUP:          name = L"Common Startup";              break;
920             case CSIDL_COMMON_TEMPLATES:        name = L"Common Templates";            break;
921             case CSIDL_COMMON_MUSIC:            name = L"CommonMusic";                 break;
922             case CSIDL_COMMON_PICTURES:         name = L"CommonPictures";              break;
923             case CSIDL_COMMON_VIDEO:            name = L"CommonVideo";                 break;
924             }
925         }
926         /* XXX todo
927          * case CSIDL_SYSTEM               # GetSystemDirectory()
928          * case CSIDL_RESOURCES            # %windir%\Resources\, For theme and other windows resources.
929          * case CSIDL_RESOURCES_LOCALIZED  # %windir%\Resources\<LangID>, for theme and other windows specific resources.
930          */
931
932 #define SHELL_FOLDERS "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
933
934         if (name && RegOpenKeyEx(root, SHELL_FOLDERS, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
935             WCHAR data[MAX_PATH+1];
936             DWORD cb = sizeof(data)-sizeof(WCHAR);
937             DWORD type = REG_NONE;
938             long rc = RegQueryValueExW(hkey, name, NULL, &type, (BYTE*)&data, &cb);
939             RegCloseKey(hkey);
940             if (rc == ERROR_SUCCESS && type == REG_SZ && cb > sizeof(WCHAR) && data[0]) {
941                 /* Make sure the string is properly terminated */
942                 data[cb/sizeof(WCHAR)] = '\0';
943                 ST(0) = wstr_to_ansipath(aTHX_ data);
944                 XSRETURN(1);
945             }
946         }
947
948 #undef SHELL_FOLDERS
949
950         /* Unders some circumstances the registry entries seem to have a null string
951          * as their value even when the directory already exists.  The environment
952          * variables do get set though, so try re-create a Unicode environment and
953          * check if they are there.
954          */
955         sv = NULL;
956         switch (folder) {
957         case CSIDL_APPDATA:              sv = get_unicode_env(aTHX_ L"APPDATA");            break;
958         case CSIDL_PROFILE:              sv = get_unicode_env(aTHX_ L"USERPROFILE");        break;
959         case CSIDL_PROGRAM_FILES:        sv = get_unicode_env(aTHX_ L"ProgramFiles");       break;
960         case CSIDL_PROGRAM_FILES_COMMON: sv = get_unicode_env(aTHX_ L"CommonProgramFiles"); break;
961         case CSIDL_WINDOWS:              sv = get_unicode_env(aTHX_ L"SystemRoot");         break;
962         }
963         if (sv) {
964             ST(0) = sv;
965             XSRETURN(1);
966         }
967     }
968
969     XSRETURN_UNDEF;
970 }
971
972 XS(w32_GetFileVersion)
973 {
974     dXSARGS;
975     DWORD size;
976     DWORD handle;
977     char *filename;
978     char *data;
979
980     if (items != 1)
981         croak("usage: Win32::GetFileVersion($filename)\n");
982
983     filename = SvPV_nolen(ST(0));
984     size = GetFileVersionInfoSize(filename, &handle);
985     if (!size)
986         XSRETURN_UNDEF;
987
988     New(0, data, size, char);
989     if (!data)
990         XSRETURN_UNDEF;
991
992     if (GetFileVersionInfo(filename, handle, size, data)) {
993         VS_FIXEDFILEINFO *info;
994         UINT len;
995         if (VerQueryValue(data, "\\", (void**)&info, &len)) {
996             int dwValueMS1 = (info->dwFileVersionMS>>16);
997             int dwValueMS2 = (info->dwFileVersionMS&0xffff);
998             int dwValueLS1 = (info->dwFileVersionLS>>16);
999             int dwValueLS2 = (info->dwFileVersionLS&0xffff);
1000
1001             if (GIMME_V == G_ARRAY) {
1002                 EXTEND(SP, 4);
1003                 XST_mIV(0, dwValueMS1);
1004                 XST_mIV(1, dwValueMS2);
1005                 XST_mIV(2, dwValueLS1);
1006                 XST_mIV(3, dwValueLS2);
1007                 items = 4;
1008             }
1009             else {
1010                 char version[50];
1011                 sprintf(version, "%d.%d.%d.%d", dwValueMS1, dwValueMS2, dwValueLS1, dwValueLS2);
1012                 XST_mPV(0, version);
1013             }
1014         }
1015     }
1016     else
1017         items = 0;
1018
1019     Safefree(data);
1020     XSRETURN(items);
1021 }
1022
1023 #ifdef __CYGWIN__
1024 XS(w32_SetChildShowWindow)
1025 {
1026     /* This function doesn't do anything useful for cygwin.  In the
1027      * MSWin32 case it modifies w32_showwindow, which is used by
1028      * win32_spawnvp().  Since w32_showwindow is an internal variable
1029      * inside the thread_intern structure, the MSWin32 implementation
1030      * lives in win32/win32.c in the core Perl distribution.
1031      */
1032     dXSARGS;
1033     XSRETURN_UNDEF;
1034 }
1035 #endif
1036
1037 XS(w32_GetCwd)
1038 {
1039     dXSARGS;
1040     /* Make the host for current directory */
1041     char* ptr = PerlEnv_get_childdir();
1042     /*
1043      * If ptr != Nullch
1044      *   then it worked, set PV valid,
1045      *   else return 'undef'
1046      */
1047     if (ptr) {
1048         SV *sv = sv_newmortal();
1049         sv_setpv(sv, ptr);
1050         PerlEnv_free_childdir(ptr);
1051
1052 #ifndef INCOMPLETE_TAINTS
1053         SvTAINTED_on(sv);
1054 #endif
1055
1056         EXTEND(SP,1);
1057         ST(0) = sv;
1058         XSRETURN(1);
1059     }
1060     XSRETURN_UNDEF;
1061 }
1062
1063 XS(w32_SetCwd)
1064 {
1065     dXSARGS;
1066     if (items != 1)
1067         Perl_croak(aTHX_ "usage: Win32::SetCwd($cwd)");
1068
1069     if (IsWin2000() && SvUTF8(ST(0))) {
1070         WCHAR *wide = sv_to_wstr(aTHX_ ST(0));
1071         char *ansi = my_ansipath(wide);
1072         int rc = PerlDir_chdir(ansi);
1073         Safefree(wide);
1074         Safefree(ansi);
1075         if (!rc)
1076             XSRETURN_YES;
1077     }
1078     else {
1079         if (!PerlDir_chdir(SvPV_nolen(ST(0))))
1080             XSRETURN_YES;
1081     }
1082
1083     XSRETURN_NO;
1084 }
1085
1086 XS(w32_GetNextAvailDrive)
1087 {
1088     dXSARGS;
1089     char ix = 'C';
1090     char root[] = "_:\\";
1091
1092     EXTEND(SP,1);
1093     while (ix <= 'Z') {
1094         root[0] = ix++;
1095         if (GetDriveType(root) == 1) {
1096             root[2] = '\0';
1097             XSRETURN_PV(root);
1098         }
1099     }
1100     XSRETURN_UNDEF;
1101 }
1102
1103 XS(w32_GetLastError)
1104 {
1105     dXSARGS;
1106     EXTEND(SP,1);
1107     XSRETURN_IV(GetLastError());
1108 }
1109
1110 XS(w32_SetLastError)
1111 {
1112     dXSARGS;
1113     if (items != 1)
1114         Perl_croak(aTHX_ "usage: Win32::SetLastError($error)");
1115     SetLastError(SvIV(ST(0)));
1116     XSRETURN_EMPTY;
1117 }
1118
1119 XS(w32_LoginName)
1120 {
1121     dXSARGS;
1122     EXTEND(SP,1);
1123     if (IsWin2000()) {
1124         WCHAR name[128];
1125         DWORD size = countof(name);
1126         if (GetUserNameW(name, &size)) {
1127             ST(0) = wstr_to_sv(aTHX_ name);
1128             XSRETURN(1);
1129         }
1130     }
1131     else {
1132         char name[128];
1133         DWORD size = countof(name);
1134         if (GetUserNameA(name, &size)) {
1135             /* size includes NULL */
1136             ST(0) = sv_2mortal(newSVpvn(name, size-1));
1137             XSRETURN(1);
1138         }
1139     }
1140     XSRETURN_UNDEF;
1141 }
1142
1143 XS(w32_NodeName)
1144 {
1145     dXSARGS;
1146     char name[MAX_COMPUTERNAME_LENGTH+1];
1147     DWORD size = sizeof(name);
1148     EXTEND(SP,1);
1149     if (GetComputerName(name,&size)) {
1150         /* size does NOT include NULL :-( */
1151         ST(0) = sv_2mortal(newSVpvn(name,size));
1152         XSRETURN(1);
1153     }
1154     XSRETURN_UNDEF;
1155 }
1156
1157
1158 XS(w32_DomainName)
1159 {
1160     dXSARGS;
1161     HMODULE module = LoadLibrary("netapi32.dll");
1162     PFNNetApiBufferFree pfnNetApiBufferFree;
1163     PFNNetWkstaGetInfo pfnNetWkstaGetInfo;
1164
1165     if (module) {
1166         GETPROC(NetApiBufferFree);
1167         GETPROC(NetWkstaGetInfo);
1168     }
1169     EXTEND(SP,1);
1170     if (module && pfnNetWkstaGetInfo && pfnNetApiBufferFree) {
1171         /* this way is more reliable, in case user has a local account. */
1172         char dname[256];
1173         DWORD dnamelen = sizeof(dname);
1174         struct {
1175             DWORD   wki100_platform_id;
1176             LPWSTR  wki100_computername;
1177             LPWSTR  wki100_langroup;
1178             DWORD   wki100_ver_major;
1179             DWORD   wki100_ver_minor;
1180         } *pwi;
1181         DWORD retval;
1182         retval = pfnNetWkstaGetInfo(NULL, 100, &pwi);
1183         /* NERR_Success *is* 0*/
1184         if (retval == 0) {
1185             if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
1186                 WideCharToMultiByte(CP_ACP, 0, pwi->wki100_langroup,
1187                                     -1, (LPSTR)dname, dnamelen, NULL, NULL);
1188             }
1189             else {
1190                 WideCharToMultiByte(CP_ACP, 0, pwi->wki100_computername,
1191                                     -1, (LPSTR)dname, dnamelen, NULL, NULL);
1192             }
1193             pfnNetApiBufferFree(pwi);
1194             FreeLibrary(module);
1195             XSRETURN_PV(dname);
1196         }
1197         FreeLibrary(module);
1198         SetLastError(retval);
1199     }
1200     else {
1201         /* Win95 doesn't have NetWksta*(), so do it the old way */
1202         char name[256];
1203         DWORD size = sizeof(name);
1204         if (module)
1205             FreeLibrary(module);
1206         if (GetUserName(name,&size)) {
1207             char sid[ONE_K_BUFSIZE];
1208             DWORD sidlen = sizeof(sid);
1209             char dname[256];
1210             DWORD dnamelen = sizeof(dname);
1211             SID_NAME_USE snu;
1212             if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
1213                                   dname, &dnamelen, &snu)) {
1214                 XSRETURN_PV(dname);             /* all that for this */
1215             }
1216         }
1217     }
1218     XSRETURN_UNDEF;
1219 }
1220
1221 XS(w32_FsType)
1222 {
1223     dXSARGS;
1224     char fsname[256];
1225     DWORD flags, filecomplen;
1226     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
1227                          &flags, fsname, sizeof(fsname))) {
1228         if (GIMME_V == G_ARRAY) {
1229             XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
1230             XPUSHs(sv_2mortal(newSViv(flags)));
1231             XPUSHs(sv_2mortal(newSViv(filecomplen)));
1232             PUTBACK;
1233             return;
1234         }
1235         EXTEND(SP,1);
1236         XSRETURN_PV(fsname);
1237     }
1238     XSRETURN_EMPTY;
1239 }
1240
1241 XS(w32_GetOSVersion)
1242 {
1243     dXSARGS;
1244
1245     if (GIMME_V == G_SCALAR) {
1246         XSRETURN_IV(g_osver.dwPlatformId);
1247     }
1248     mXPUSHp(g_osver.szCSDVersion, strlen(g_osver.szCSDVersion));
1249
1250     mXPUSHi(g_osver.dwMajorVersion);
1251     mXPUSHi(g_osver.dwMinorVersion);
1252     mXPUSHi(g_osver.dwBuildNumber);
1253     mXPUSHi(g_osver.dwPlatformId);
1254     if (g_osver_ex) {
1255         mXPUSHi(g_osver.wServicePackMajor);
1256         mXPUSHi(g_osver.wServicePackMinor);
1257         mXPUSHi(g_osver.wSuiteMask);
1258         mXPUSHi(g_osver.wProductType);
1259     }
1260     PUTBACK;
1261 }
1262
1263 XS(w32_IsWinNT)
1264 {
1265     dXSARGS;
1266     EXTEND(SP,1);
1267     XSRETURN_IV(IsWinNT());
1268 }
1269
1270 XS(w32_IsWin95)
1271 {
1272     dXSARGS;
1273     EXTEND(SP,1);
1274     XSRETURN_IV(IsWin95());
1275 }
1276
1277 XS(w32_FormatMessage)
1278 {
1279     dXSARGS;
1280     DWORD source = 0;
1281     char msgbuf[ONE_K_BUFSIZE];
1282
1283     if (items != 1)
1284         Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");
1285
1286     if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
1287                        &source, SvIV(ST(0)), 0,
1288                        msgbuf, sizeof(msgbuf)-1, NULL))
1289     {
1290         XSRETURN_PV(msgbuf);
1291     }
1292
1293     XSRETURN_UNDEF;
1294 }
1295
1296 XS(w32_Spawn)
1297 {
1298     dXSARGS;
1299     char *cmd, *args;
1300     void *env;
1301     char *dir;
1302     PROCESS_INFORMATION stProcInfo;
1303     STARTUPINFO stStartInfo;
1304     BOOL bSuccess = FALSE;
1305
1306     if (items != 3)
1307         Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)");
1308
1309     cmd = SvPV_nolen(ST(0));
1310     args = SvPV_nolen(ST(1));
1311
1312     env = PerlEnv_get_childenv();
1313     dir = PerlEnv_get_childdir();
1314
1315     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
1316     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
1317     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
1318     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
1319
1320     if (CreateProcess(
1321                 cmd,                    /* Image path */
1322                 args,                   /* Arguments for command line */
1323                 NULL,                   /* Default process security */
1324                 NULL,                   /* Default thread security */
1325                 FALSE,                  /* Must be TRUE to use std handles */
1326                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
1327                 env,                    /* Inherit our environment block */
1328                 dir,                    /* Inherit our currrent directory */
1329                 &stStartInfo,           /* -> Startup info */
1330                 &stProcInfo))           /* <- Process info (if OK) */
1331     {
1332         int pid = (int)stProcInfo.dwProcessId;
1333         if (IsWin95() && pid < 0)
1334             pid = -pid;
1335         sv_setiv(ST(2), pid);
1336         CloseHandle(stProcInfo.hThread);/* library source code does this. */
1337         bSuccess = TRUE;
1338     }
1339     PerlEnv_free_childenv(env);
1340     PerlEnv_free_childdir(dir);
1341     XSRETURN_IV(bSuccess);
1342 }
1343
1344 XS(w32_GetTickCount)
1345 {
1346     dXSARGS;
1347     DWORD msec = GetTickCount();
1348     EXTEND(SP,1);
1349     if ((IV)msec > 0)
1350         XSRETURN_IV(msec);
1351     XSRETURN_NV(msec);
1352 }
1353
1354 XS(w32_GetShortPathName)
1355 {
1356     dXSARGS;
1357     SV *shortpath;
1358     DWORD len;
1359
1360     if (items != 1)
1361         Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)");
1362
1363     if (IsWin2000()) {
1364         WCHAR wshort[MAX_PATH+1];
1365         WCHAR *wlong = sv_to_wstr(aTHX_ ST(0));
1366         len = GetShortPathNameW(wlong, wshort, countof(wshort));
1367         Safefree(wlong);
1368         if (len && len < sizeof(wshort)) {
1369             ST(0) = wstr_to_sv(aTHX_ wshort);
1370             XSRETURN(1);
1371         }
1372         XSRETURN_UNDEF;
1373     }
1374
1375     shortpath = sv_mortalcopy(ST(0));
1376     SvUPGRADE(shortpath, SVt_PV);
1377     if (!SvPVX(shortpath) || !SvLEN(shortpath))
1378         XSRETURN_UNDEF;
1379
1380     /* src == target is allowed */
1381     do {
1382         len = GetShortPathName(SvPVX(shortpath),
1383                                SvPVX(shortpath),
1384                                SvLEN(shortpath));
1385     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
1386     if (len) {
1387         SvCUR_set(shortpath,len);
1388         *SvEND(shortpath) = '\0';
1389         ST(0) = shortpath;
1390         XSRETURN(1);
1391     }
1392     XSRETURN_UNDEF;
1393 }
1394
1395 XS(w32_GetFullPathName)
1396 {
1397     dXSARGS;
1398     char *fullname;
1399     char *ansi = NULL;
1400
1401 /* The code below relies on the fact that PerlDir_mapX() returns an
1402  * absolute path, which is only true under PERL_IMPLICIT_SYS when
1403  * we use the virtualization code from win32/vdir.h.
1404  * Without it PerlDir_mapX() is a no-op and we need to use the same
1405  * code as we use for Cygwin.
1406  */
1407 #if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
1408     char buffer[2*MAX_PATH];
1409 #endif
1410
1411     if (items != 1)
1412         Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)");
1413
1414 #if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
1415     if (IsWin2000()) {
1416         WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
1417         WCHAR full[2*MAX_PATH];
1418         DWORD len = GetFullPathNameW(filename, countof(full), full, NULL);
1419         Safefree(filename);
1420         if (len == 0 || len >= countof(full))
1421             XSRETURN_EMPTY;
1422         ansi = fullname = my_ansipath(full);
1423     }
1424     else {
1425         DWORD len = GetFullPathNameA(SvPV_nolen(ST(0)), countof(buffer), buffer, NULL);
1426         if (len == 0 || len >= countof(buffer))
1427             XSRETURN_EMPTY;
1428         fullname = buffer;
1429     }
1430 #else
1431     /* Don't use my_ansipath() unless the $filename argument is in Unicode.
1432      * If the relative path doesn't exist, GetShortPathName() will fail and
1433      * my_ansipath() will use the long name with replacement characters.
1434      * In that case we will be better off using PerlDir_mapA(), which
1435      * already uses the ANSI name of the current directory.
1436      *
1437      * XXX The one missing case is where we could downgrade $filename
1438      * XXX from UTF8 into the current codepage.
1439      */
1440     if (IsWin2000() && SvUTF8(ST(0))) {
1441         WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
1442         WCHAR *mappedname = PerlDir_mapW(filename);
1443         Safefree(filename);
1444         ansi = fullname = my_ansipath(mappedname);
1445     }
1446     else {
1447         fullname = PerlDir_mapA(SvPV_nolen(ST(0)));
1448     }
1449 #  if PERL_VERSION < 8
1450     {
1451         /* PerlDir_mapX() in Perl 5.6 used to return forward slashes */
1452         char *str = fullname;
1453         while (*str) {
1454             if (*str == '/')
1455                 *str = '\\';
1456             ++str;
1457         }
1458     }
1459 #  endif
1460 #endif
1461
1462     /* GetFullPathName() on Windows NT drops trailing backslash */
1463     if (g_osver.dwMajorVersion == 4 && *fullname) {
1464         STRLEN len;
1465         char *pv = SvPV(ST(0), len);
1466         char *lastchar = fullname + strlen(fullname) - 1;
1467         /* If ST(0) ends with a slash, but fullname doesn't ... */
1468         if (len && (pv[len-1] == '/' || pv[len-1] == '\\') && *lastchar != '\\') {
1469             /* fullname is the MAX_PATH+1 sized buffer returned from PerlDir_mapA()
1470              * or the 2*MAX_PATH sized local buffer in the __CYGWIN__ case.
1471              */
1472             strcpy(lastchar+1, "\\");
1473         }
1474     }
1475
1476     if (GIMME_V == G_ARRAY) {
1477         char *filepart = strrchr(fullname, '\\');
1478
1479         EXTEND(SP,1);
1480         if (filepart) {
1481             XST_mPV(1, ++filepart);
1482             *filepart = '\0';
1483         }
1484         else {
1485             XST_mPVN(1, "", 0);
1486         }
1487         items = 2;
1488     }
1489     XST_mPV(0, fullname);
1490
1491     if (ansi)
1492         Safefree(ansi);
1493     XSRETURN(items);
1494 }
1495
1496 XS(w32_GetLongPathName)
1497 {
1498     dXSARGS;
1499
1500     if (items != 1)
1501         Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)");
1502
1503     if (IsWin2000()) {
1504         WCHAR *wstr = sv_to_wstr(aTHX_ ST(0));
1505         WCHAR wide_path[MAX_PATH+1];
1506         WCHAR *long_path;
1507
1508         wcscpy(wide_path, wstr);
1509         Safefree(wstr);
1510         long_path = my_longpathW(wide_path);
1511         if (long_path) {
1512             ST(0) = wstr_to_sv(aTHX_ long_path);
1513             XSRETURN(1);
1514         }
1515     }
1516     else {
1517         SV *path;
1518         char tmpbuf[MAX_PATH+1];
1519         char *pathstr;
1520         STRLEN len;
1521
1522         path = ST(0);
1523         pathstr = SvPV(path,len);
1524         strcpy(tmpbuf, pathstr);
1525         pathstr = my_longpathA(tmpbuf);
1526         if (pathstr) {
1527             ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
1528             XSRETURN(1);
1529         }
1530     }
1531     XSRETURN_EMPTY;
1532 }
1533
1534 XS(w32_GetANSIPathName)
1535 {
1536     dXSARGS;
1537     WCHAR *wide_path;
1538
1539     if (items != 1)
1540         Perl_croak(aTHX_ "usage: Win32::GetANSIPathName($pathname)");
1541
1542     wide_path = sv_to_wstr(aTHX_ ST(0));
1543     ST(0) = wstr_to_ansipath(aTHX_ wide_path);
1544     Safefree(wide_path);
1545     XSRETURN(1);
1546 }
1547
1548 XS(w32_Sleep)
1549 {
1550     dXSARGS;
1551     if (items != 1)
1552         Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
1553     Sleep(SvIV(ST(0)));
1554     XSRETURN_YES;
1555 }
1556
1557 XS(w32_CopyFile)
1558 {
1559     dXSARGS;
1560     BOOL bResult;
1561     char szSourceFile[MAX_PATH+1];
1562
1563     if (items != 3)
1564         Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
1565     strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
1566     bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));
1567     if (bResult)
1568         XSRETURN_YES;
1569     XSRETURN_NO;
1570 }
1571
1572 XS(w32_OutputDebugString)
1573 {
1574     dXSARGS;
1575     if (items != 1)
1576         Perl_croak(aTHX_ "usage: Win32::OutputDebugString($string)");
1577
1578     if (SvUTF8(ST(0))) {
1579         WCHAR *str = sv_to_wstr(aTHX_ ST(0));
1580         OutputDebugStringW(str);
1581         Safefree(str);
1582     }
1583     else
1584         OutputDebugStringA(SvPV_nolen(ST(0)));
1585
1586     XSRETURN_EMPTY;
1587 }
1588
1589 XS(w32_GetCurrentThreadId)
1590 {
1591     dXSARGS;
1592     EXTEND(SP,1);
1593     XSRETURN_IV(GetCurrentThreadId());
1594 }
1595
1596 XS(w32_CreateDirectory)
1597 {
1598     dXSARGS;
1599     BOOL result;
1600
1601     if (items != 1)
1602         Perl_croak(aTHX_ "usage: Win32::CreateDirectory($dir)");
1603
1604     if (IsWin2000() && SvUTF8(ST(0))) {
1605         WCHAR *dir = sv_to_wstr(aTHX_ ST(0));
1606         result = CreateDirectoryW(dir, NULL);
1607         Safefree(dir);
1608     }
1609     else {
1610         result = CreateDirectoryA(SvPV_nolen(ST(0)), NULL);
1611     }
1612
1613     ST(0) = boolSV(result);
1614     XSRETURN(1);
1615 }
1616
1617 XS(w32_CreateFile)
1618 {
1619     dXSARGS;
1620     HANDLE handle;
1621
1622     if (items != 1)
1623         Perl_croak(aTHX_ "usage: Win32::CreateFile($file)");
1624
1625     if (IsWin2000() && SvUTF8(ST(0))) {
1626         WCHAR *file = sv_to_wstr(aTHX_ ST(0));
1627         handle = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_WRITE,
1628                              NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1629         Safefree(file);
1630     }
1631     else {
1632         handle = CreateFileA(SvPV_nolen(ST(0)), GENERIC_WRITE, FILE_SHARE_WRITE,
1633                              NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1634     }
1635
1636     if (handle != INVALID_HANDLE_VALUE)
1637         CloseHandle(handle);
1638
1639     ST(0) = boolSV(handle != INVALID_HANDLE_VALUE);
1640     XSRETURN(1);
1641 }
1642
1643 MODULE = Win32            PACKAGE = Win32
1644
1645 PROTOTYPES: DISABLE
1646
1647 BOOT:
1648 {
1649     char *file = __FILE__;
1650
1651     if (g_osver.dwOSVersionInfoSize == 0) {
1652         g_osver.dwOSVersionInfoSize = sizeof(g_osver);
1653         if (!GetVersionExA((OSVERSIONINFOA*)&g_osver)) {
1654             g_osver_ex = FALSE;
1655             g_osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1656             GetVersionExA((OSVERSIONINFOA*)&g_osver);
1657         }
1658     }
1659
1660     newXS("Win32::LookupAccountName", w32_LookupAccountName, file);
1661     newXS("Win32::LookupAccountSID", w32_LookupAccountSID, file);
1662     newXS("Win32::InitiateSystemShutdown", w32_InitiateSystemShutdown, file);
1663     newXS("Win32::AbortSystemShutdown", w32_AbortSystemShutdown, file);
1664     newXS("Win32::ExpandEnvironmentStrings", w32_ExpandEnvironmentStrings, file);
1665     newXS("Win32::MsgBox", w32_MsgBox, file);
1666     newXS("Win32::LoadLibrary", w32_LoadLibrary, file);
1667     newXS("Win32::FreeLibrary", w32_FreeLibrary, file);
1668     newXS("Win32::GetProcAddress", w32_GetProcAddress, file);
1669     newXS("Win32::RegisterServer", w32_RegisterServer, file);
1670     newXS("Win32::UnregisterServer", w32_UnregisterServer, file);
1671     newXS("Win32::GetArchName", w32_GetArchName, file);
1672     newXS("Win32::GetChipName", w32_GetChipName, file);
1673     newXS("Win32::GuidGen", w32_GuidGen, file);
1674     newXS("Win32::GetFolderPath", w32_GetFolderPath, file);
1675     newXS("Win32::IsAdminUser", w32_IsAdminUser, file);
1676     newXS("Win32::GetFileVersion", w32_GetFileVersion, file);
1677
1678     newXS("Win32::GetCwd", w32_GetCwd, file);
1679     newXS("Win32::SetCwd", w32_SetCwd, file);
1680     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
1681     newXS("Win32::GetLastError", w32_GetLastError, file);
1682     newXS("Win32::SetLastError", w32_SetLastError, file);
1683     newXS("Win32::LoginName", w32_LoginName, file);
1684     newXS("Win32::NodeName", w32_NodeName, file);
1685     newXS("Win32::DomainName", w32_DomainName, file);
1686     newXS("Win32::FsType", w32_FsType, file);
1687     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
1688     newXS("Win32::IsWinNT", w32_IsWinNT, file);
1689     newXS("Win32::IsWin95", w32_IsWin95, file);
1690     newXS("Win32::FormatMessage", w32_FormatMessage, file);
1691     newXS("Win32::Spawn", w32_Spawn, file);
1692     newXS("Win32::GetTickCount", w32_GetTickCount, file);
1693     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
1694     newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
1695     newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
1696     newXS("Win32::GetANSIPathName", w32_GetANSIPathName, file);
1697     newXS("Win32::CopyFile", w32_CopyFile, file);
1698     newXS("Win32::Sleep", w32_Sleep, file);
1699     newXS("Win32::OutputDebugString", w32_OutputDebugString, file);
1700     newXS("Win32::GetCurrentThreadId", w32_GetCurrentThreadId, file);
1701     newXS("Win32::CreateDirectory", w32_CreateDirectory, file);
1702     newXS("Win32::CreateFile", w32_CreateFile, file);
1703 #ifdef __CYGWIN__
1704     newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
1705 #endif
1706     XSRETURN_YES;
1707 }