6587cf29a855282ff32378d42b0fa986c7857fc0
[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)();
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         /* NERR_Success *is* 0*/
1182         if (0 == pfnNetWkstaGetInfo(NULL, 100, &pwi)) {
1183             if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
1184                 WideCharToMultiByte(CP_ACP, 0, pwi->wki100_langroup,
1185                                     -1, (LPSTR)dname, dnamelen, NULL, NULL);
1186             }
1187             else {
1188                 WideCharToMultiByte(CP_ACP, 0, pwi->wki100_computername,
1189                                     -1, (LPSTR)dname, dnamelen, NULL, NULL);
1190             }
1191             pfnNetApiBufferFree(pwi);
1192             FreeLibrary(module);
1193             XSRETURN_PV(dname);
1194         }
1195         FreeLibrary(module);
1196     }
1197     else {
1198         /* Win95 doesn't have NetWksta*(), so do it the old way */
1199         char name[256];
1200         DWORD size = sizeof(name);
1201         if (module)
1202             FreeLibrary(module);
1203         if (GetUserName(name,&size)) {
1204             char sid[ONE_K_BUFSIZE];
1205             DWORD sidlen = sizeof(sid);
1206             char dname[256];
1207             DWORD dnamelen = sizeof(dname);
1208             SID_NAME_USE snu;
1209             if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
1210                                   dname, &dnamelen, &snu)) {
1211                 XSRETURN_PV(dname);             /* all that for this */
1212             }
1213         }
1214     }
1215     XSRETURN_UNDEF;
1216 }
1217
1218 XS(w32_FsType)
1219 {
1220     dXSARGS;
1221     char fsname[256];
1222     DWORD flags, filecomplen;
1223     if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
1224                          &flags, fsname, sizeof(fsname))) {
1225         if (GIMME_V == G_ARRAY) {
1226             XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
1227             XPUSHs(sv_2mortal(newSViv(flags)));
1228             XPUSHs(sv_2mortal(newSViv(filecomplen)));
1229             PUTBACK;
1230             return;
1231         }
1232         EXTEND(SP,1);
1233         XSRETURN_PV(fsname);
1234     }
1235     XSRETURN_EMPTY;
1236 }
1237
1238 XS(w32_GetOSVersion)
1239 {
1240     dXSARGS;
1241
1242     if (GIMME_V == G_SCALAR) {
1243         XSRETURN_IV(g_osver.dwPlatformId);
1244     }
1245     XPUSHs(newSVpvn(g_osver.szCSDVersion, strlen(g_osver.szCSDVersion)));
1246
1247     XPUSHs(newSViv(g_osver.dwMajorVersion));
1248     XPUSHs(newSViv(g_osver.dwMinorVersion));
1249     XPUSHs(newSViv(g_osver.dwBuildNumber));
1250     XPUSHs(newSViv(g_osver.dwPlatformId));
1251     if (g_osver_ex) {
1252         XPUSHs(newSViv(g_osver.wServicePackMajor));
1253         XPUSHs(newSViv(g_osver.wServicePackMinor));
1254         XPUSHs(newSViv(g_osver.wSuiteMask));
1255         XPUSHs(newSViv(g_osver.wProductType));
1256     }
1257     PUTBACK;
1258 }
1259
1260 XS(w32_IsWinNT)
1261 {
1262     dXSARGS;
1263     EXTEND(SP,1);
1264     XSRETURN_IV(IsWinNT());
1265 }
1266
1267 XS(w32_IsWin95)
1268 {
1269     dXSARGS;
1270     EXTEND(SP,1);
1271     XSRETURN_IV(IsWin95());
1272 }
1273
1274 XS(w32_FormatMessage)
1275 {
1276     dXSARGS;
1277     DWORD source = 0;
1278     char msgbuf[ONE_K_BUFSIZE];
1279
1280     if (items != 1)
1281         Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");
1282
1283     if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
1284                        &source, SvIV(ST(0)), 0,
1285                        msgbuf, sizeof(msgbuf)-1, NULL))
1286     {
1287         XSRETURN_PV(msgbuf);
1288     }
1289
1290     XSRETURN_UNDEF;
1291 }
1292
1293 XS(w32_Spawn)
1294 {
1295     dXSARGS;
1296     char *cmd, *args;
1297     void *env;
1298     char *dir;
1299     PROCESS_INFORMATION stProcInfo;
1300     STARTUPINFO stStartInfo;
1301     BOOL bSuccess = FALSE;
1302
1303     if (items != 3)
1304         Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)");
1305
1306     cmd = SvPV_nolen(ST(0));
1307     args = SvPV_nolen(ST(1));
1308
1309     env = PerlEnv_get_childenv();
1310     dir = PerlEnv_get_childdir();
1311
1312     memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
1313     stStartInfo.cb = sizeof(stStartInfo);           /* Set the structure size */
1314     stStartInfo.dwFlags = STARTF_USESHOWWINDOW;     /* Enable wShowWindow control */
1315     stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
1316
1317     if (CreateProcess(
1318                 cmd,                    /* Image path */
1319                 args,                   /* Arguments for command line */
1320                 NULL,                   /* Default process security */
1321                 NULL,                   /* Default thread security */
1322                 FALSE,                  /* Must be TRUE to use std handles */
1323                 NORMAL_PRIORITY_CLASS,  /* No special scheduling */
1324                 env,                    /* Inherit our environment block */
1325                 dir,                    /* Inherit our currrent directory */
1326                 &stStartInfo,           /* -> Startup info */
1327                 &stProcInfo))           /* <- Process info (if OK) */
1328     {
1329         int pid = (int)stProcInfo.dwProcessId;
1330         if (IsWin95() && pid < 0)
1331             pid = -pid;
1332         sv_setiv(ST(2), pid);
1333         CloseHandle(stProcInfo.hThread);/* library source code does this. */
1334         bSuccess = TRUE;
1335     }
1336     PerlEnv_free_childenv(env);
1337     PerlEnv_free_childdir(dir);
1338     XSRETURN_IV(bSuccess);
1339 }
1340
1341 XS(w32_GetTickCount)
1342 {
1343     dXSARGS;
1344     DWORD msec = GetTickCount();
1345     EXTEND(SP,1);
1346     if ((IV)msec > 0)
1347         XSRETURN_IV(msec);
1348     XSRETURN_NV(msec);
1349 }
1350
1351 XS(w32_GetShortPathName)
1352 {
1353     dXSARGS;
1354     SV *shortpath;
1355     DWORD len;
1356
1357     if (items != 1)
1358         Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)");
1359
1360     if (IsWin2000()) {
1361         WCHAR wshort[MAX_PATH+1];
1362         WCHAR *wlong = sv_to_wstr(aTHX_ ST(0));
1363         len = GetShortPathNameW(wlong, wshort, countof(wshort));
1364         Safefree(wlong);
1365         if (len && len < sizeof(wshort)) {
1366             ST(0) = wstr_to_sv(aTHX_ wshort);
1367             XSRETURN(1);
1368         }
1369         XSRETURN_UNDEF;
1370     }
1371
1372     shortpath = sv_mortalcopy(ST(0));
1373     SvUPGRADE(shortpath, SVt_PV);
1374     if (!SvPVX(shortpath) || !SvLEN(shortpath))
1375         XSRETURN_UNDEF;
1376
1377     /* src == target is allowed */
1378     do {
1379         len = GetShortPathName(SvPVX(shortpath),
1380                                SvPVX(shortpath),
1381                                SvLEN(shortpath));
1382     } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
1383     if (len) {
1384         SvCUR_set(shortpath,len);
1385         *SvEND(shortpath) = '\0';
1386         ST(0) = shortpath;
1387         XSRETURN(1);
1388     }
1389     XSRETURN_UNDEF;
1390 }
1391
1392 XS(w32_GetFullPathName)
1393 {
1394     dXSARGS;
1395     char *fullname;
1396     char *ansi = NULL;
1397
1398 /* The code below relies on the fact that PerlDir_mapX() returns an
1399  * absolute path, which is only true under PERL_IMPLICIT_SYS when
1400  * we use the virtualization code from win32/vdir.h.
1401  * Without it PerlDir_mapX() is a no-op and we need to use the same
1402  * code as we use for Cygwin.
1403  */
1404 #if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
1405     char buffer[2*MAX_PATH];
1406 #endif
1407
1408     if (items != 1)
1409         Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)");
1410
1411 #if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
1412     if (IsWin2000()) {
1413         WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
1414         WCHAR full[2*MAX_PATH];
1415         DWORD len = GetFullPathNameW(filename, countof(full), full, NULL);
1416         Safefree(filename);
1417         if (len == 0 || len >= countof(full))
1418             XSRETURN_EMPTY;
1419         ansi = fullname = my_ansipath(full);
1420     }
1421     else {
1422         DWORD len = GetFullPathNameA(SvPV_nolen(ST(0)), countof(buffer), buffer, NULL);
1423         if (len == 0 || len >= countof(buffer))
1424             XSRETURN_EMPTY;
1425         fullname = buffer;
1426     }
1427 #else
1428     /* Don't use my_ansipath() unless the $filename argument is in Unicode.
1429      * If the relative path doesn't exist, GetShortPathName() will fail and
1430      * my_ansipath() will use the long name with replacement characters.
1431      * In that case we will be better off using PerlDir_mapA(), which
1432      * already uses the ANSI name of the current directory.
1433      *
1434      * XXX The one missing case is where we could downgrade $filename
1435      * XXX from UTF8 into the current codepage.
1436      */
1437     if (IsWin2000() && SvUTF8(ST(0))) {
1438         WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
1439         WCHAR *mappedname = PerlDir_mapW(filename);
1440         Safefree(filename);
1441         ansi = fullname = my_ansipath(mappedname);
1442     }
1443     else {
1444         fullname = PerlDir_mapA(SvPV_nolen(ST(0)));
1445     }
1446 #  if PERL_VERSION < 8
1447     {
1448         /* PerlDir_mapX() in Perl 5.6 used to return forward slashes */
1449         char *str = fullname;
1450         while (*str) {
1451             if (*str == '/')
1452                 *str = '\\';
1453             ++str;
1454         }
1455     }
1456 #  endif
1457 #endif
1458
1459     /* GetFullPathName() on Windows NT drops trailing backslash */
1460     if (g_osver.dwMajorVersion == 4 && *fullname) {
1461         STRLEN len;
1462         char *pv = SvPV(ST(0), len);
1463         char *lastchar = fullname + strlen(fullname) - 1;
1464         /* If ST(0) ends with a slash, but fullname doesn't ... */
1465         if (len && (pv[len-1] == '/' || pv[len-1] == '\\') && *lastchar != '\\') {
1466             /* fullname is the MAX_PATH+1 sized buffer returned from PerlDir_mapA()
1467              * or the 2*MAX_PATH sized local buffer in the __CYGWIN__ case.
1468              */
1469             strcpy(lastchar+1, "\\");
1470         }
1471     }
1472
1473     if (GIMME_V == G_ARRAY) {
1474         char *filepart = strrchr(fullname, '\\');
1475
1476         EXTEND(SP,1);
1477         if (filepart) {
1478             XST_mPV(1, ++filepart);
1479             *filepart = '\0';
1480         }
1481         else {
1482             XST_mPVN(1, "", 0);
1483         }
1484         items = 2;
1485     }
1486     XST_mPV(0, fullname);
1487
1488     if (ansi)
1489         Safefree(ansi);
1490     XSRETURN(items);
1491 }
1492
1493 XS(w32_GetLongPathName)
1494 {
1495     dXSARGS;
1496
1497     if (items != 1)
1498         Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)");
1499
1500     if (IsWin2000()) {
1501         WCHAR *wstr = sv_to_wstr(aTHX_ ST(0));
1502         WCHAR wide_path[MAX_PATH+1];
1503         WCHAR *long_path;
1504
1505         wcscpy(wide_path, wstr);
1506         Safefree(wstr);
1507         long_path = my_longpathW(wide_path);
1508         if (long_path) {
1509             ST(0) = wstr_to_sv(aTHX_ long_path);
1510             XSRETURN(1);
1511         }
1512     }
1513     else {
1514         SV *path;
1515         char tmpbuf[MAX_PATH+1];
1516         char *pathstr;
1517         STRLEN len;
1518
1519         path = ST(0);
1520         pathstr = SvPV(path,len);
1521         strcpy(tmpbuf, pathstr);
1522         pathstr = my_longpathA(tmpbuf);
1523         if (pathstr) {
1524             ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
1525             XSRETURN(1);
1526         }
1527     }
1528     XSRETURN_EMPTY;
1529 }
1530
1531 XS(w32_GetANSIPathName)
1532 {
1533     dXSARGS;
1534     WCHAR *wide_path;
1535
1536     if (items != 1)
1537         Perl_croak(aTHX_ "usage: Win32::GetANSIPathName($pathname)");
1538
1539     wide_path = sv_to_wstr(aTHX_ ST(0));
1540     ST(0) = wstr_to_ansipath(aTHX_ wide_path);
1541     Safefree(wide_path);
1542     XSRETURN(1);
1543 }
1544
1545 XS(w32_Sleep)
1546 {
1547     dXSARGS;
1548     if (items != 1)
1549         Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
1550     Sleep(SvIV(ST(0)));
1551     XSRETURN_YES;
1552 }
1553
1554 XS(w32_CopyFile)
1555 {
1556     dXSARGS;
1557     BOOL bResult;
1558     char szSourceFile[MAX_PATH+1];
1559
1560     if (items != 3)
1561         Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
1562     strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
1563     bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));
1564     if (bResult)
1565         XSRETURN_YES;
1566     XSRETURN_NO;
1567 }
1568
1569 XS(w32_OutputDebugString)
1570 {
1571     dXSARGS;
1572     if (items != 1)
1573         Perl_croak(aTHX_ "usage: Win32::OutputDebugString($string)");
1574
1575     if (SvUTF8(ST(0))) {
1576         WCHAR *str = sv_to_wstr(aTHX_ ST(0));
1577         OutputDebugStringW(str);
1578         Safefree(str);
1579     }
1580     else
1581         OutputDebugStringA(SvPV_nolen(ST(0)));
1582
1583     XSRETURN_EMPTY;
1584 }
1585
1586 XS(w32_GetCurrentThreadId)
1587 {
1588     dXSARGS;
1589     EXTEND(SP,1);
1590     XSRETURN_IV(GetCurrentThreadId());
1591 }
1592
1593 XS(w32_CreateDirectory)
1594 {
1595     dXSARGS;
1596     BOOL result;
1597
1598     if (items != 1)
1599         Perl_croak(aTHX_ "usage: Win32::CreateDirectory($dir)");
1600
1601     if (IsWin2000() && SvUTF8(ST(0))) {
1602         WCHAR *dir = sv_to_wstr(aTHX_ ST(0));
1603         result = CreateDirectoryW(dir, NULL);
1604         Safefree(dir);
1605     }
1606     else {
1607         result = CreateDirectoryA(SvPV_nolen(ST(0)), NULL);
1608     }
1609
1610     ST(0) = boolSV(result);
1611     XSRETURN(1);
1612 }
1613
1614 XS(w32_CreateFile)
1615 {
1616     dXSARGS;
1617     HANDLE handle;
1618
1619     if (items != 1)
1620         Perl_croak(aTHX_ "usage: Win32::CreateFile($file)");
1621
1622     if (IsWin2000() && SvUTF8(ST(0))) {
1623         WCHAR *file = sv_to_wstr(aTHX_ ST(0));
1624         handle = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_WRITE,
1625                              NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1626         Safefree(file);
1627     }
1628     else {
1629         handle = CreateFileA(SvPV_nolen(ST(0)), GENERIC_WRITE, FILE_SHARE_WRITE,
1630                              NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1631     }
1632
1633     if (handle != INVALID_HANDLE_VALUE)
1634         CloseHandle(handle);
1635
1636     ST(0) = boolSV(handle != INVALID_HANDLE_VALUE);
1637     XSRETURN(1);
1638 }
1639
1640 MODULE = Win32            PACKAGE = Win32
1641
1642 PROTOTYPES: DISABLE
1643
1644 BOOT:
1645 {
1646     char *file = __FILE__;
1647
1648     if (g_osver.dwOSVersionInfoSize == 0) {
1649         g_osver.dwOSVersionInfoSize = sizeof(g_osver);
1650         if (!GetVersionExA((OSVERSIONINFOA*)&g_osver)) {
1651             g_osver_ex = FALSE;
1652             g_osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1653             GetVersionExA((OSVERSIONINFOA*)&g_osver);
1654         }
1655     }
1656
1657     newXS("Win32::LookupAccountName", w32_LookupAccountName, file);
1658     newXS("Win32::LookupAccountSID", w32_LookupAccountSID, file);
1659     newXS("Win32::InitiateSystemShutdown", w32_InitiateSystemShutdown, file);
1660     newXS("Win32::AbortSystemShutdown", w32_AbortSystemShutdown, file);
1661     newXS("Win32::ExpandEnvironmentStrings", w32_ExpandEnvironmentStrings, file);
1662     newXS("Win32::MsgBox", w32_MsgBox, file);
1663     newXS("Win32::LoadLibrary", w32_LoadLibrary, file);
1664     newXS("Win32::FreeLibrary", w32_FreeLibrary, file);
1665     newXS("Win32::GetProcAddress", w32_GetProcAddress, file);
1666     newXS("Win32::RegisterServer", w32_RegisterServer, file);
1667     newXS("Win32::UnregisterServer", w32_UnregisterServer, file);
1668     newXS("Win32::GetArchName", w32_GetArchName, file);
1669     newXS("Win32::GetChipName", w32_GetChipName, file);
1670     newXS("Win32::GuidGen", w32_GuidGen, file);
1671     newXS("Win32::GetFolderPath", w32_GetFolderPath, file);
1672     newXS("Win32::IsAdminUser", w32_IsAdminUser, file);
1673     newXS("Win32::GetFileVersion", w32_GetFileVersion, file);
1674
1675     newXS("Win32::GetCwd", w32_GetCwd, file);
1676     newXS("Win32::SetCwd", w32_SetCwd, file);
1677     newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
1678     newXS("Win32::GetLastError", w32_GetLastError, file);
1679     newXS("Win32::SetLastError", w32_SetLastError, file);
1680     newXS("Win32::LoginName", w32_LoginName, file);
1681     newXS("Win32::NodeName", w32_NodeName, file);
1682     newXS("Win32::DomainName", w32_DomainName, file);
1683     newXS("Win32::FsType", w32_FsType, file);
1684     newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
1685     newXS("Win32::IsWinNT", w32_IsWinNT, file);
1686     newXS("Win32::IsWin95", w32_IsWin95, file);
1687     newXS("Win32::FormatMessage", w32_FormatMessage, file);
1688     newXS("Win32::Spawn", w32_Spawn, file);
1689     newXS("Win32::GetTickCount", w32_GetTickCount, file);
1690     newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
1691     newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
1692     newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
1693     newXS("Win32::GetANSIPathName", w32_GetANSIPathName, file);
1694     newXS("Win32::CopyFile", w32_CopyFile, file);
1695     newXS("Win32::Sleep", w32_Sleep, file);
1696     newXS("Win32::OutputDebugString", w32_OutputDebugString, file);
1697     newXS("Win32::GetCurrentThreadId", w32_GetCurrentThreadId, file);
1698     newXS("Win32::CreateDirectory", w32_CreateDirectory, file);
1699     newXS("Win32::CreateFile", w32_CreateFile, file);
1700 #ifdef __CYGWIN__
1701     newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
1702 #endif
1703     XSRETURN_YES;
1704 }