This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
locale.c: savepv() of getenv()
[perl5.git] / cpan / Win32 / Win32.xs
index 8af0e88..de3764e 100644 (file)
-#include <wctype.h>\r
-#include <windows.h>\r
-#include <shlobj.h>\r
-\r
-#define PERL_NO_GET_CONTEXT\r
-#include "EXTERN.h"\r
-#include "perl.h"\r
-#include "XSUB.h"\r
-\r
-#ifndef countof\r
-#  define countof(array) (sizeof (array) / sizeof (*(array)))\r
-#endif\r
-\r
-#define SE_SHUTDOWN_NAMEA   "SeShutdownPrivilege"\r
-\r
-#ifndef WC_NO_BEST_FIT_CHARS\r
-#  define WC_NO_BEST_FIT_CHARS 0x00000400\r
-#endif\r
-\r
-#define GETPROC(fn) pfn##fn = (PFN##fn)GetProcAddress(module, #fn)\r
-\r
-typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathA)(HWND, char*, int, BOOL);\r
-typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathW)(HWND, WCHAR*, int, BOOL);\r
-typedef HRESULT (WINAPI *PFNSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPTSTR);\r
-typedef HRESULT (WINAPI *PFNSHGetFolderPathW)(HWND, int, HANDLE, DWORD, LPWSTR);\r
-typedef BOOL (WINAPI *PFNCreateEnvironmentBlock)(void**, HANDLE, BOOL);\r
-typedef BOOL (WINAPI *PFNDestroyEnvironmentBlock)(void*);\r
-typedef int (__stdcall *PFNDllRegisterServer)(void);\r
-typedef int (__stdcall *PFNDllUnregisterServer)(void);\r
-typedef DWORD (__stdcall *PFNNetApiBufferFree)(void*);\r
-typedef DWORD (__stdcall *PFNNetWkstaGetInfo)(LPWSTR, DWORD, void*);\r
-\r
-typedef BOOL (__stdcall *PFNOpenProcessToken)(HANDLE, DWORD, HANDLE*);\r
-typedef BOOL (__stdcall *PFNOpenThreadToken)(HANDLE, DWORD, BOOL, HANDLE*);\r
-typedef BOOL (__stdcall *PFNGetTokenInformation)(HANDLE, TOKEN_INFORMATION_CLASS, void*, DWORD, DWORD*);\r
-typedef BOOL (__stdcall *PFNAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD,\r
-                                                      DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);\r
-typedef BOOL (__stdcall *PFNEqualSid)(PSID, PSID);\r
-typedef void* (__stdcall *PFNFreeSid)(PSID);\r
-typedef BOOL (__stdcall *PFNIsUserAnAdmin)(void);\r
-typedef BOOL (WINAPI *PFNGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD*);\r
-typedef void (WINAPI *PFNGetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo);\r
-\r
-#ifndef CSIDL_MYMUSIC\r
-#   define CSIDL_MYMUSIC              0x000D\r
-#endif\r
-#ifndef CSIDL_MYVIDEO\r
-#   define CSIDL_MYVIDEO              0x000E\r
-#endif\r
-#ifndef CSIDL_LOCAL_APPDATA\r
-#   define CSIDL_LOCAL_APPDATA        0x001C\r
-#endif\r
-#ifndef CSIDL_COMMON_FAVORITES\r
-#   define CSIDL_COMMON_FAVORITES     0x001F\r
-#endif\r
-#ifndef CSIDL_INTERNET_CACHE\r
-#   define CSIDL_INTERNET_CACHE       0x0020\r
-#endif\r
-#ifndef CSIDL_COOKIES\r
-#   define CSIDL_COOKIES              0x0021\r
-#endif\r
-#ifndef CSIDL_HISTORY\r
-#   define CSIDL_HISTORY              0x0022\r
-#endif\r
-#ifndef CSIDL_COMMON_APPDATA\r
-#   define CSIDL_COMMON_APPDATA       0x0023\r
-#endif\r
-#ifndef CSIDL_WINDOWS\r
-#   define CSIDL_WINDOWS              0x0024\r
-#endif\r
-#ifndef CSIDL_PROGRAM_FILES\r
-#   define CSIDL_PROGRAM_FILES        0x0026\r
-#endif\r
-#ifndef CSIDL_MYPICTURES\r
-#   define CSIDL_MYPICTURES           0x0027\r
-#endif\r
-#ifndef CSIDL_PROFILE\r
-#   define CSIDL_PROFILE              0x0028\r
-#endif\r
-#ifndef CSIDL_PROGRAM_FILES_COMMON\r
-#   define CSIDL_PROGRAM_FILES_COMMON 0x002B\r
-#endif\r
-#ifndef CSIDL_COMMON_TEMPLATES\r
-#   define CSIDL_COMMON_TEMPLATES     0x002D\r
-#endif\r
-#ifndef CSIDL_COMMON_DOCUMENTS\r
-#   define CSIDL_COMMON_DOCUMENTS     0x002E\r
-#endif\r
-#ifndef CSIDL_COMMON_ADMINTOOLS\r
-#   define CSIDL_COMMON_ADMINTOOLS    0x002F\r
-#endif\r
-#ifndef CSIDL_ADMINTOOLS\r
-#   define CSIDL_ADMINTOOLS           0x0030\r
-#endif\r
-#ifndef CSIDL_COMMON_MUSIC\r
-#   define CSIDL_COMMON_MUSIC         0x0035\r
-#endif\r
-#ifndef CSIDL_COMMON_PICTURES\r
-#   define CSIDL_COMMON_PICTURES      0x0036\r
-#endif\r
-#ifndef CSIDL_COMMON_VIDEO\r
-#   define CSIDL_COMMON_VIDEO         0x0037\r
-#endif\r
-#ifndef CSIDL_CDBURN_AREA\r
-#   define CSIDL_CDBURN_AREA          0x003B\r
-#endif\r
-#ifndef CSIDL_FLAG_CREATE\r
-#   define CSIDL_FLAG_CREATE          0x8000\r
-#endif\r
-\r
-/* Use explicit struct definition because wSuiteMask and\r
- * wProductType are not defined in the VC++ 6.0 headers.\r
- * WORD type has been replaced by unsigned short because\r
- * WORD is already used by Perl itself.\r
- */\r
-struct {\r
-    DWORD dwOSVersionInfoSize;\r
-    DWORD dwMajorVersion;\r
-    DWORD dwMinorVersion;\r
-    DWORD dwBuildNumber;\r
-    DWORD dwPlatformId;\r
-    CHAR  szCSDVersion[128];\r
-    unsigned short wServicePackMajor;\r
-    unsigned short wServicePackMinor;\r
-    unsigned short wSuiteMask;\r
-    BYTE  wProductType;\r
-    BYTE  wReserved;\r
-}   g_osver = {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0};\r
-BOOL g_osver_ex = TRUE;\r
-\r
-#define ONE_K_BUFSIZE  1024\r
-\r
-int\r
-IsWin95(void)\r
-{\r
-    return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);\r
-}\r
-\r
-int\r
-IsWinNT(void)\r
-{\r
-    return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT);\r
-}\r
-\r
-int\r
-IsWin2000(void)\r
-{\r
-    return (g_osver.dwMajorVersion > 4);\r
-}\r
-\r
-/* Convert SV to wide character string.  The return value must be\r
- * freed using Safefree().\r
- */\r
-WCHAR*\r
-sv_to_wstr(pTHX_ SV *sv)\r
-{\r
-    DWORD wlen;\r
-    WCHAR *wstr;\r
-    STRLEN len;\r
-    char *str = SvPV(sv, len);\r
-    UINT cp = SvUTF8(sv) ? CP_UTF8 : CP_ACP;\r
-\r
-    wlen = MultiByteToWideChar(cp, 0, str, (int)(len+1), NULL, 0);\r
-    New(0, wstr, wlen, WCHAR);\r
-    MultiByteToWideChar(cp, 0, str, (int)(len+1), wstr, wlen);\r
-\r
-    return wstr;\r
-}\r
-\r
-/* Convert wide character string to mortal SV.  Use UTF8 encoding\r
- * if the string cannot be represented in the system codepage.\r
- */\r
-SV *\r
-wstr_to_sv(pTHX_ WCHAR *wstr)\r
-{\r
-    int wlen = (int)wcslen(wstr)+1;\r
-    BOOL use_default = FALSE;\r
-    int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, NULL, 0, NULL, NULL);\r
-    SV *sv = sv_2mortal(newSV(len));\r
-\r
-    len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, SvPVX(sv), len, NULL, &use_default);\r
-    if (use_default) {\r
-        len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, NULL, 0, NULL, NULL);\r
-        sv_grow(sv, len);\r
-        len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, SvPVX(sv), len, NULL, NULL);\r
-        SvUTF8_on(sv);\r
-    }\r
-    /* Shouldn't really ever fail since we ask for the required length first, but who knows... */\r
-    if (len) {\r
-        SvPOK_on(sv);\r
-        SvCUR_set(sv, len-1);\r
-    }\r
-    return sv;\r
-}\r
-\r
-/* Retrieve a variable from the Unicode environment in a mortal SV.\r
- *\r
- * Recreates the Unicode environment because a bug in earlier Perl versions\r
- * overwrites it with the ANSI version, which contains replacement\r
- * characters for the characters not in the ANSI codepage.\r
- */\r
-SV*\r
-get_unicode_env(pTHX_ WCHAR *name)\r
-{\r
-    SV *sv = NULL;\r
-    void *env;\r
-    HANDLE token;\r
-    HMODULE module;\r
-    PFNOpenProcessToken pfnOpenProcessToken;\r
-\r
-    /* Get security token for the current process owner */\r
-    module = LoadLibrary("advapi32.dll");\r
-    if (!module)\r
-        return NULL;\r
-\r
-    GETPROC(OpenProcessToken);\r
-\r
-    if (pfnOpenProcessToken == NULL ||\r
-        !pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &token))\r
-    {\r
-        FreeLibrary(module);\r
-        return NULL;\r
-    }\r
-    FreeLibrary(module);\r
-\r
-    /* Create a Unicode environment block for this process */\r
-    module = LoadLibrary("userenv.dll");\r
-    if (module) {\r
-        PFNCreateEnvironmentBlock pfnCreateEnvironmentBlock;\r
-        PFNDestroyEnvironmentBlock pfnDestroyEnvironmentBlock;\r
-\r
-        GETPROC(CreateEnvironmentBlock);\r
-        GETPROC(DestroyEnvironmentBlock);\r
-\r
-        if (pfnCreateEnvironmentBlock && pfnDestroyEnvironmentBlock &&\r
-            pfnCreateEnvironmentBlock(&env, token, FALSE))\r
-        {\r
-            size_t name_len = wcslen(name);\r
-            WCHAR *entry = env;\r
-            while (*entry) {\r
-                size_t i;\r
-                size_t entry_len = wcslen(entry);\r
-                BOOL equal = (entry_len > name_len) && (entry[name_len] == '=');\r
-\r
-                for (i=0; equal && i < name_len; ++i)\r
-                    equal = (towupper(entry[i]) == towupper(name[i]));\r
-\r
-                if (equal) {\r
-                    sv = wstr_to_sv(aTHX_ entry+name_len+1);\r
-                    break;\r
-                }\r
-                entry += entry_len+1;\r
-            }\r
-            pfnDestroyEnvironmentBlock(env);\r
-        }\r
-        FreeLibrary(module);\r
-    }\r
-    CloseHandle(token);\r
-    return sv;\r
-}\r
-\r
-/* Define both an ANSI and a Wide version of win32_longpath */\r
-\r
-#define CHAR_T            char\r
-#define WIN32_FIND_DATA_T WIN32_FIND_DATAA\r
-#define FN_FINDFIRSTFILE  FindFirstFileA\r
-#define FN_STRLEN         strlen\r
-#define FN_STRCPY         strcpy\r
-#define LONGPATH          my_longpathA\r
-#include "longpath.inc"\r
-\r
-#define CHAR_T            WCHAR\r
-#define WIN32_FIND_DATA_T WIN32_FIND_DATAW\r
-#define FN_FINDFIRSTFILE  FindFirstFileW\r
-#define FN_STRLEN         wcslen\r
-#define FN_STRCPY         wcscpy\r
-#define LONGPATH          my_longpathW\r
-#include "longpath.inc"\r
-\r
-/* The my_ansipath() function takes a Unicode filename and converts it\r
- * into the current Windows codepage. If some characters cannot be mapped,\r
- * then it will convert the short name instead.\r
- *\r
- * The buffer to the ansi pathname must be freed with Safefree() when it\r
- * it no longer needed.\r
- *\r
- * The argument to my_ansipath() must exist before this function is\r
- * called; otherwise there is no way to determine the short path name.\r
- *\r
- * Ideas for future refinement:\r
- * - Only convert those segments of the path that are not in the current\r
- *   codepage, but leave the other segments in their long form.\r
- * - If the resulting name is longer than MAX_PATH, start converting\r
- *   additional path segments into short names until the full name\r
- *   is shorter than MAX_PATH.  Shorten the filename part last!\r
- */\r
-\r
-/* This is a modified version of core Perl win32/win32.c(win32_ansipath).\r
- * It uses New() etc. instead of win32_malloc().\r
- */\r
-\r
-char *\r
-my_ansipath(const WCHAR *widename)\r
-{\r
-    char *name;\r
-    BOOL use_default = FALSE;\r
-    int widelen = (int)wcslen(widename)+1;\r
-    int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,\r
-                                  NULL, 0, NULL, NULL);\r
-    New(0, name, len, char);\r
-    WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,\r
-                        name, len, NULL, &use_default);\r
-    if (use_default) {\r
-        DWORD shortlen = GetShortPathNameW(widename, NULL, 0);\r
-        if (shortlen) {\r
-            WCHAR *shortname;\r
-            New(0, shortname, shortlen, WCHAR);\r
-            shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;\r
-\r
-            len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,\r
-                                      NULL, 0, NULL, NULL);\r
-            Renew(name, len, char);\r
-            WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,\r
-                                name, len, NULL, NULL);\r
-            Safefree(shortname);\r
-        }\r
-    }\r
-    return name;\r
-}\r
-\r
-/* Convert wide character path to ANSI path and return as mortal SV. */\r
-SV*\r
-wstr_to_ansipath(pTHX_ WCHAR *wstr)\r
-{\r
-    char *ansi = my_ansipath(wstr);\r
-    SV *sv = sv_2mortal(newSVpvn(ansi, strlen(ansi)));\r
-    Safefree(ansi);\r
-    return sv;\r
-}\r
-\r
-#ifdef __CYGWIN__\r
-\r
-char*\r
-get_childdir(void)\r
-{\r
-    dTHX;\r
-    char* ptr;\r
-\r
-    if (IsWin2000()) {\r
-        WCHAR filename[MAX_PATH+1];\r
-        GetCurrentDirectoryW(MAX_PATH+1, filename);\r
-        ptr = my_ansipath(filename);\r
-    }\r
-    else {\r
-        char filename[MAX_PATH+1];\r
-        GetCurrentDirectoryA(MAX_PATH+1, filename);\r
-        New(0, ptr, strlen(filename)+1, char);\r
-        strcpy(ptr, filename);\r
-    }\r
-    return ptr;\r
-}\r
-\r
-void\r
-free_childdir(char *d)\r
-{\r
-    dTHX;\r
-    Safefree(d);\r
-}\r
-\r
-void*\r
-get_childenv(void)\r
-{\r
-    return NULL;\r
-}\r
-\r
-void\r
-free_childenv(void *d)\r
-{\r
-}\r
-\r
-#  define PerlDir_mapA(dir) (dir)\r
-\r
-#endif\r
-\r
-XS(w32_ExpandEnvironmentStrings)\r
-{\r
-    dXSARGS;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::ExpandEnvironmentStrings($String);\n");\r
-\r
-    if (IsWin2000()) {\r
-        WCHAR value[31*1024];\r
-        WCHAR *source = sv_to_wstr(aTHX_ ST(0));\r
-        ExpandEnvironmentStringsW(source, value, countof(value)-1);\r
-        ST(0) = wstr_to_sv(aTHX_ value);\r
-        Safefree(source);\r
-        XSRETURN(1);\r
-    }\r
-    else {\r
-        char value[31*1024];\r
-        ExpandEnvironmentStringsA(SvPV_nolen(ST(0)), value, countof(value)-2);\r
-        XSRETURN_PV(value);\r
-    }\r
-}\r
-\r
-XS(w32_IsAdminUser)\r
-{\r
-    dXSARGS;\r
-    HMODULE                     module;\r
-    PFNIsUserAnAdmin            pfnIsUserAnAdmin;\r
-    PFNOpenThreadToken          pfnOpenThreadToken;\r
-    PFNOpenProcessToken         pfnOpenProcessToken;\r
-    PFNGetTokenInformation      pfnGetTokenInformation;\r
-    PFNAllocateAndInitializeSid pfnAllocateAndInitializeSid;\r
-    PFNEqualSid                 pfnEqualSid;\r
-    PFNFreeSid                  pfnFreeSid;\r
-    HANDLE                      hTok;\r
-    DWORD                       dwTokInfoLen;\r
-    TOKEN_GROUPS                *lpTokInfo;\r
-    SID_IDENTIFIER_AUTHORITY    NtAuth = SECURITY_NT_AUTHORITY;\r
-    PSID                        pAdminSid;\r
-    int                         iRetVal;\r
-    unsigned int                i;\r
-\r
-    if (items)\r
-        croak("usage: Win32::IsAdminUser()");\r
-\r
-    /* There is no concept of "Administrator" user accounts on Win9x systems,\r
-       so just return true. */\r
-    if (IsWin95())\r
-        XSRETURN_YES;\r
-\r
-    /* Use IsUserAnAdmin() when available.  On Vista this will only return TRUE\r
-     * if the process is running with elevated privileges and not just when the\r
-     * process owner is a member of the "Administrators" group.\r
-     */\r
-    module = LoadLibrary("shell32.dll");\r
-    if (module) {\r
-        GETPROC(IsUserAnAdmin);\r
-        if (pfnIsUserAnAdmin) {\r
-            EXTEND(SP, 1);\r
-            ST(0) = sv_2mortal(newSViv(pfnIsUserAnAdmin() ? 1 : 0));\r
-            FreeLibrary(module);\r
-            XSRETURN(1);\r
-        }\r
-        FreeLibrary(module);\r
-    }\r
-\r
-    module = LoadLibrary("advapi32.dll");\r
-    if (!module) {\r
-        warn("Cannot load advapi32.dll library");\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    GETPROC(OpenThreadToken);\r
-    GETPROC(OpenProcessToken);\r
-    GETPROC(GetTokenInformation);\r
-    GETPROC(AllocateAndInitializeSid);\r
-    GETPROC(EqualSid);\r
-    GETPROC(FreeSid);\r
-\r
-    if (!(pfnOpenThreadToken && pfnOpenProcessToken &&\r
-          pfnGetTokenInformation && pfnAllocateAndInitializeSid &&\r
-          pfnEqualSid && pfnFreeSid))\r
-    {\r
-        warn("Cannot load functions from advapi32.dll library");\r
-        FreeLibrary(module);\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    if (!pfnOpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hTok)) {\r
-        if (!pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTok)) {\r
-            warn("Cannot open thread token or process token");\r
-            FreeLibrary(module);\r
-            XSRETURN_UNDEF;\r
-        }\r
-    }\r
-\r
-    pfnGetTokenInformation(hTok, TokenGroups, NULL, 0, &dwTokInfoLen);\r
-    if (!New(1, lpTokInfo, dwTokInfoLen, TOKEN_GROUPS)) {\r
-        warn("Cannot allocate token information structure");\r
-        CloseHandle(hTok);\r
-        FreeLibrary(module);\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    if (!pfnGetTokenInformation(hTok, TokenGroups, lpTokInfo, dwTokInfoLen,\r
-            &dwTokInfoLen))\r
-    {\r
-        warn("Cannot get token information");\r
-        Safefree(lpTokInfo);\r
-        CloseHandle(hTok);\r
-        FreeLibrary(module);\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    if (!pfnAllocateAndInitializeSid(&NtAuth, 2, SECURITY_BUILTIN_DOMAIN_RID,\r
-            DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid))\r
-    {\r
-        warn("Cannot allocate administrators' SID");\r
-        Safefree(lpTokInfo);\r
-        CloseHandle(hTok);\r
-        FreeLibrary(module);\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    iRetVal = 0;\r
-    for (i = 0; i < lpTokInfo->GroupCount; ++i) {\r
-        if (pfnEqualSid(lpTokInfo->Groups[i].Sid, pAdminSid)) {\r
-            iRetVal = 1;\r
-            break;\r
-        }\r
-    }\r
-\r
-    pfnFreeSid(pAdminSid);\r
-    Safefree(lpTokInfo);\r
-    CloseHandle(hTok);\r
-    FreeLibrary(module);\r
-\r
-    EXTEND(SP, 1);\r
-    ST(0) = sv_2mortal(newSViv(iRetVal));\r
-    XSRETURN(1);\r
-}\r
-\r
-XS(w32_LookupAccountName)\r
-{\r
-    dXSARGS;\r
-    char SID[400];\r
-    DWORD SIDLen;\r
-    SID_NAME_USE snu;\r
-    char Domain[256];\r
-    DWORD DomLen;\r
-    BOOL bResult;\r
-\r
-    if (items != 5)\r
-       croak("usage: Win32::LookupAccountName($system, $account, $domain, "\r
-             "$sid, $sidtype);\n");\r
-\r
-    SIDLen = sizeof(SID);\r
-    DomLen = sizeof(Domain);\r
-\r
-    bResult = LookupAccountNameA(SvPV_nolen(ST(0)),    /* System */\r
-                                 SvPV_nolen(ST(1)),    /* Account name */\r
-                                 &SID,                 /* SID structure */\r
-                                 &SIDLen,              /* Size of SID buffer */\r
-                                 Domain,               /* Domain buffer */\r
-                                 &DomLen,              /* Domain buffer size */\r
-                                 &snu);                        /* SID name type */\r
-    if (bResult) {\r
-       sv_setpv(ST(2), Domain);\r
-       sv_setpvn(ST(3), SID, SIDLen);\r
-       sv_setiv(ST(4), snu);\r
-       XSRETURN_YES;\r
-    }\r
-    XSRETURN_NO;\r
-}\r
-\r
-\r
-XS(w32_LookupAccountSID)\r
-{\r
-    dXSARGS;\r
-    PSID sid;\r
-    char Account[256];\r
-    DWORD AcctLen = sizeof(Account);\r
-    char Domain[256];\r
-    DWORD DomLen = sizeof(Domain);\r
-    SID_NAME_USE snu;\r
-    BOOL bResult;\r
-\r
-    if (items != 5)\r
-       croak("usage: Win32::LookupAccountSID($system, $sid, $account, $domain, $sidtype);\n");\r
-\r
-    sid = SvPV_nolen(ST(1));\r
-    if (IsValidSid(sid)) {\r
-        bResult = LookupAccountSidA(SvPV_nolen(ST(0)), /* System */\r
-                                    sid,               /* SID structure */\r
-                                    Account,           /* Account name buffer */\r
-                                    &AcctLen,          /* name buffer length */\r
-                                    Domain,            /* Domain buffer */\r
-                                    &DomLen,           /* Domain buffer length */\r
-                                    &snu);             /* SID name type */\r
-       if (bResult) {\r
-           sv_setpv(ST(2), Account);\r
-           sv_setpv(ST(3), Domain);\r
-           sv_setiv(ST(4), (IV)snu);\r
-           XSRETURN_YES;\r
-       }\r
-    }\r
-    XSRETURN_NO;\r
-}\r
-\r
-XS(w32_InitiateSystemShutdown)\r
-{\r
-    dXSARGS;\r
-    HANDLE hToken;              /* handle to process token   */\r
-    TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */\r
-    BOOL bRet;\r
-    char *machineName, *message;\r
-\r
-    if (items != 5)\r
-       croak("usage: Win32::InitiateSystemShutdown($machineName, $message, "\r
-             "$timeOut, $forceClose, $reboot);\n");\r
-\r
-    machineName = SvPV_nolen(ST(0));\r
-\r
-    if (OpenProcessToken(GetCurrentProcess(),\r
-                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,\r
-                        &hToken))\r
-    {\r
-        LookupPrivilegeValueA(machineName,\r
-                              SE_SHUTDOWN_NAMEA,\r
-                              &tkp.Privileges[0].Luid);\r
-\r
-       tkp.PrivilegeCount = 1; /* only setting one */\r
-       tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\r
-\r
-       /* Get shutdown privilege for this process. */\r
-       AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,\r
-                             (PTOKEN_PRIVILEGES)NULL, 0);\r
-    }\r
-\r
-    message = SvPV_nolen(ST(1));\r
-    bRet = InitiateSystemShutdownA(machineName, message, (DWORD)SvIV(ST(2)),\r
-                                   (BOOL)SvIV(ST(3)), (BOOL)SvIV(ST(4)));\r
-\r
-    /* Disable shutdown privilege. */\r
-    tkp.Privileges[0].Attributes = 0; \r
-    AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,\r
-                         (PTOKEN_PRIVILEGES)NULL, 0); \r
-    CloseHandle(hToken);\r
-    XSRETURN_IV(bRet);\r
-}\r
-\r
-XS(w32_AbortSystemShutdown)\r
-{\r
-    dXSARGS;\r
-    HANDLE hToken;              /* handle to process token   */\r
-    TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */\r
-    BOOL bRet;\r
-    char *machineName;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::AbortSystemShutdown($machineName);\n");\r
-\r
-    machineName = SvPV_nolen(ST(0));\r
-\r
-    if (OpenProcessToken(GetCurrentProcess(),\r
-                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,\r
-                        &hToken))\r
-    {\r
-        LookupPrivilegeValueA(machineName,\r
-                              SE_SHUTDOWN_NAMEA,\r
-                              &tkp.Privileges[0].Luid);\r
-\r
-       tkp.PrivilegeCount = 1; /* only setting one */\r
-       tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\r
-\r
-       /* Get shutdown privilege for this process. */\r
-       AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,\r
-                             (PTOKEN_PRIVILEGES)NULL, 0);\r
-    }\r
-\r
-    bRet = AbortSystemShutdownA(machineName);\r
-\r
-    /* Disable shutdown privilege. */\r
-    tkp.Privileges[0].Attributes = 0;\r
-    AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,\r
-                         (PTOKEN_PRIVILEGES)NULL, 0);\r
-    CloseHandle(hToken);\r
-    XSRETURN_IV(bRet);\r
-}\r
-\r
-\r
-XS(w32_MsgBox)\r
-{\r
-    dXSARGS;\r
-    DWORD flags = MB_ICONEXCLAMATION;\r
-    I32 result;\r
-\r
-    if (items < 1 || items > 3)\r
-       croak("usage: Win32::MsgBox($message [, $flags [, $title]]);\n");\r
-\r
-    if (items > 1)\r
-        flags = (DWORD)SvIV(ST(1));\r
-\r
-    if (IsWin2000()) {\r
-        WCHAR *title = NULL;\r
-        WCHAR *msg = sv_to_wstr(aTHX_ ST(0));\r
-        if (items > 2)\r
-            title = sv_to_wstr(aTHX_ ST(2));\r
-        result = MessageBoxW(GetActiveWindow(), msg, title ? title : L"Perl", flags);\r
-        Safefree(msg);\r
-        if (title)\r
-            Safefree(title);\r
-    }\r
-    else {\r
-        char *title = "Perl";\r
-        char *msg = SvPV_nolen(ST(0));\r
-        if (items > 2)\r
-            title = SvPV_nolen(ST(2));\r
-        result = MessageBoxA(GetActiveWindow(), msg, title, flags);\r
-    }\r
-    XSRETURN_IV(result);\r
-}\r
-\r
-XS(w32_LoadLibrary)\r
-{\r
-    dXSARGS;\r
-    HANDLE hHandle;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::LoadLibrary($libname)\n");\r
-    hHandle = LoadLibraryA(SvPV_nolen(ST(0)));\r
-#ifdef _WIN64\r
-    XSRETURN_IV((DWORD_PTR)hHandle);\r
-#else\r
-    XSRETURN_IV((DWORD)hHandle);\r
-#endif\r
-}\r
-\r
-XS(w32_FreeLibrary)\r
-{\r
-    dXSARGS;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::FreeLibrary($handle)\n");\r
-    if (FreeLibrary(INT2PTR(HINSTANCE, SvIV(ST(0))))) {\r
-       XSRETURN_YES;\r
-    }\r
-    XSRETURN_NO;\r
-}\r
-\r
-XS(w32_GetProcAddress)\r
-{\r
-    dXSARGS;\r
-\r
-    if (items != 2)\r
-       croak("usage: Win32::GetProcAddress($hinstance, $procname)\n");\r
-    XSRETURN_IV(PTR2IV(GetProcAddress(INT2PTR(HINSTANCE, SvIV(ST(0))), SvPV_nolen(ST(1)))));\r
-}\r
-\r
-XS(w32_RegisterServer)\r
-{\r
-    dXSARGS;\r
-    BOOL result = FALSE;\r
-    HMODULE module;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::RegisterServer($libname)\n");\r
-\r
-    module = LoadLibraryA(SvPV_nolen(ST(0)));\r
-    if (module) {\r
-       PFNDllRegisterServer pfnDllRegisterServer;\r
-        GETPROC(DllRegisterServer);\r
-       if (pfnDllRegisterServer && pfnDllRegisterServer() == 0)\r
-           result = TRUE;\r
-       FreeLibrary(module);\r
-    }\r
-    ST(0) = boolSV(result);\r
-    XSRETURN(1);\r
-}\r
-\r
-XS(w32_UnregisterServer)\r
-{\r
-    dXSARGS;\r
-    BOOL result = FALSE;\r
-    HINSTANCE module;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::UnregisterServer($libname)\n");\r
-\r
-    module = LoadLibraryA(SvPV_nolen(ST(0)));\r
-    if (module) {\r
-       PFNDllUnregisterServer pfnDllUnregisterServer;\r
-        GETPROC(DllUnregisterServer);\r
-       if (pfnDllUnregisterServer && pfnDllUnregisterServer() == 0)\r
-           result = TRUE;\r
-       FreeLibrary(module);\r
-    }\r
-    ST(0) = boolSV(result);\r
-    XSRETURN(1);\r
-}\r
-\r
-/* XXX rather bogus */\r
-XS(w32_GetArchName)\r
-{\r
-    dXSARGS;\r
-    XSRETURN_PV(getenv("PROCESSOR_ARCHITECTURE"));\r
-}\r
-\r
-XS(w32_GetChipName)\r
-{\r
-    dXSARGS;\r
-    SYSTEM_INFO sysinfo;\r
-    HMODULE module;\r
-    PFNGetNativeSystemInfo pfnGetNativeSystemInfo;\r
-\r
-    Zero(&sysinfo,1,SYSTEM_INFO);\r
-    module = GetModuleHandle("kernel32.dll");\r
-    GETPROC(GetNativeSystemInfo);\r
-    if (pfnGetNativeSystemInfo)\r
-        pfnGetNativeSystemInfo(&sysinfo);\r
-    else\r
-        GetSystemInfo(&sysinfo);\r
-\r
-    /* XXX docs say dwProcessorType is deprecated on NT */\r
-    XSRETURN_IV(sysinfo.dwProcessorType);\r
-}\r
-\r
-XS(w32_GuidGen)\r
-{\r
-    dXSARGS;\r
-    GUID guid;\r
-    char szGUID[50] = {'\0'};\r
-    HRESULT  hr     = CoCreateGuid(&guid);\r
-\r
-    if (SUCCEEDED(hr)) {\r
-       LPOLESTR pStr = NULL;\r
-       if (SUCCEEDED(StringFromCLSID(&guid, &pStr))) {\r
-            WideCharToMultiByte(CP_ACP, 0, pStr, (int)wcslen(pStr), szGUID,\r
-                                sizeof(szGUID), NULL, NULL);\r
-            CoTaskMemFree(pStr);\r
-            XSRETURN_PV(szGUID);\r
-        }\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_GetFolderPath)\r
-{\r
-    dXSARGS;\r
-    char path[MAX_PATH+1];\r
-    WCHAR wpath[MAX_PATH+1];\r
-    int folder;\r
-    int create = 0;\r
-    HMODULE module;\r
-\r
-    if (items != 1 && items != 2)\r
-       croak("usage: Win32::GetFolderPath($csidl [, $create])\n");\r
-\r
-    folder = (int)SvIV(ST(0));\r
-    if (items == 2)\r
-        create = SvTRUE(ST(1)) ? CSIDL_FLAG_CREATE : 0;\r
-\r
-    module = LoadLibrary("shfolder.dll");\r
-    if (module) {\r
-        PFNSHGetFolderPathA pfna;\r
-        if (IsWin2000()) {\r
-            PFNSHGetFolderPathW pfnw;\r
-            pfnw = (PFNSHGetFolderPathW)GetProcAddress(module, "SHGetFolderPathW");\r
-            if (pfnw && SUCCEEDED(pfnw(NULL, folder|create, NULL, 0, wpath))) {\r
-                FreeLibrary(module);\r
-                ST(0) = wstr_to_ansipath(aTHX_ wpath);\r
-                XSRETURN(1);\r
-            }\r
-        }\r
-        pfna = (PFNSHGetFolderPathA)GetProcAddress(module, "SHGetFolderPathA");\r
-        if (pfna && SUCCEEDED(pfna(NULL, folder|create, NULL, 0, path))) {\r
-            FreeLibrary(module);\r
-            XSRETURN_PV(path);\r
-        }\r
-        FreeLibrary(module);\r
-    }\r
-\r
-    module = LoadLibrary("shell32.dll");\r
-    if (module) {\r
-        PFNSHGetSpecialFolderPathA pfna;\r
-        if (IsWin2000()) {\r
-            PFNSHGetSpecialFolderPathW pfnw;\r
-            pfnw = (PFNSHGetSpecialFolderPathW)GetProcAddress(module, "SHGetSpecialFolderPathW");\r
-            if (pfnw && pfnw(NULL, wpath, folder, !!create)) {\r
-                FreeLibrary(module);\r
-                ST(0) = wstr_to_ansipath(aTHX_ wpath);\r
-                XSRETURN(1);\r
-            }\r
-        }\r
-        pfna = (PFNSHGetSpecialFolderPathA)GetProcAddress(module, "SHGetSpecialFolderPathA");\r
-        if (pfna && pfna(NULL, path, folder, !!create)) {\r
-            FreeLibrary(module);\r
-            XSRETURN_PV(path);\r
-        }\r
-        FreeLibrary(module);\r
-    }\r
-\r
-    /* SHGetFolderPathW() and SHGetSpecialFolderPathW() may fail on older\r
-     * Perl versions that have replaced the Unicode environment with an\r
-     * ANSI version.  Let's go spelunking in the registry now...\r
-     */\r
-    if (IsWin2000()) {\r
-        SV *sv;\r
-        HKEY hkey;\r
-        HKEY root = HKEY_CURRENT_USER;\r
-        WCHAR *name = NULL;\r
-\r
-        switch (folder) {\r
-        case CSIDL_ADMINTOOLS:                  name = L"Administrative Tools";        break;\r
-        case CSIDL_APPDATA:                     name = L"AppData";                     break;\r
-        case CSIDL_CDBURN_AREA:                 name = L"CD Burning";                  break;\r
-        case CSIDL_COOKIES:                     name = L"Cookies";                     break;\r
-        case CSIDL_DESKTOP:\r
-        case CSIDL_DESKTOPDIRECTORY:            name = L"Desktop";                     break;\r
-        case CSIDL_FAVORITES:                   name = L"Favorites";                   break;\r
-        case CSIDL_FONTS:                       name = L"Fonts";                       break;\r
-        case CSIDL_HISTORY:                     name = L"History";                     break;\r
-        case CSIDL_INTERNET_CACHE:              name = L"Cache";                       break;\r
-        case CSIDL_LOCAL_APPDATA:               name = L"Local AppData";               break;\r
-        case CSIDL_MYMUSIC:                     name = L"My Music";                    break;\r
-        case CSIDL_MYPICTURES:                  name = L"My Pictures";                 break;\r
-        case CSIDL_MYVIDEO:                     name = L"My Video";                    break;\r
-        case CSIDL_NETHOOD:                     name = L"NetHood";                     break;\r
-        case CSIDL_PERSONAL:                    name = L"Personal";                    break;\r
-        case CSIDL_PRINTHOOD:                   name = L"PrintHood";                   break;\r
-        case CSIDL_PROGRAMS:                    name = L"Programs";                    break;\r
-        case CSIDL_RECENT:                      name = L"Recent";                      break;\r
-        case CSIDL_SENDTO:                      name = L"SendTo";                      break;\r
-        case CSIDL_STARTMENU:                   name = L"Start Menu";                  break;\r
-        case CSIDL_STARTUP:                     name = L"Startup";                     break;\r
-        case CSIDL_TEMPLATES:                   name = L"Templates";                   break;\r
-            /* XXX L"Local Settings" */\r
-        }\r
-\r
-        if (!name) {\r
-            root = HKEY_LOCAL_MACHINE;\r
-            switch (folder) {\r
-            case CSIDL_COMMON_ADMINTOOLS:       name = L"Common Administrative Tools"; break;\r
-            case CSIDL_COMMON_APPDATA:          name = L"Common AppData";              break;\r
-            case CSIDL_COMMON_DESKTOPDIRECTORY: name = L"Common Desktop";              break;\r
-            case CSIDL_COMMON_DOCUMENTS:        name = L"Common Documents";            break;\r
-            case CSIDL_COMMON_FAVORITES:        name = L"Common Favorites";            break;\r
-            case CSIDL_COMMON_PROGRAMS:         name = L"Common Programs";             break;\r
-            case CSIDL_COMMON_STARTMENU:        name = L"Common Start Menu";           break;\r
-            case CSIDL_COMMON_STARTUP:          name = L"Common Startup";              break;\r
-            case CSIDL_COMMON_TEMPLATES:        name = L"Common Templates";            break;\r
-            case CSIDL_COMMON_MUSIC:            name = L"CommonMusic";                 break;\r
-            case CSIDL_COMMON_PICTURES:         name = L"CommonPictures";              break;\r
-            case CSIDL_COMMON_VIDEO:            name = L"CommonVideo";                 break;\r
-            }\r
-        }\r
-        /* XXX todo\r
-         * case CSIDL_SYSTEM               # GetSystemDirectory()\r
-         * case CSIDL_RESOURCES            # %windir%\Resources\, For theme and other windows resources.\r
-         * case CSIDL_RESOURCES_LOCALIZED  # %windir%\Resources\<LangID>, for theme and other windows specific resources.\r
-         */\r
-\r
-#define SHELL_FOLDERS "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"\r
-\r
-        if (name && RegOpenKeyEx(root, SHELL_FOLDERS, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {\r
-            WCHAR data[MAX_PATH+1];\r
-            DWORD cb = sizeof(data)-sizeof(WCHAR);\r
-            DWORD type = REG_NONE;\r
-            long rc = RegQueryValueExW(hkey, name, NULL, &type, (BYTE*)&data, &cb);\r
-            RegCloseKey(hkey);\r
-            if (rc == ERROR_SUCCESS && type == REG_SZ && cb > sizeof(WCHAR) && data[0]) {\r
-                /* Make sure the string is properly terminated */\r
-                data[cb/sizeof(WCHAR)] = '\0';\r
-                ST(0) = wstr_to_ansipath(aTHX_ data);\r
-                XSRETURN(1);\r
-            }\r
-        }\r
-\r
-#undef SHELL_FOLDERS\r
-\r
-        /* Unders some circumstances the registry entries seem to have a null string\r
-         * as their value even when the directory already exists.  The environment\r
-         * variables do get set though, so try re-create a Unicode environment and\r
-         * check if they are there.\r
-         */\r
-        sv = NULL;\r
-        switch (folder) {\r
-        case CSIDL_APPDATA:              sv = get_unicode_env(aTHX_ L"APPDATA");            break;\r
-        case CSIDL_PROFILE:              sv = get_unicode_env(aTHX_ L"USERPROFILE");        break;\r
-        case CSIDL_PROGRAM_FILES:        sv = get_unicode_env(aTHX_ L"ProgramFiles");       break;\r
-        case CSIDL_PROGRAM_FILES_COMMON: sv = get_unicode_env(aTHX_ L"CommonProgramFiles"); break;\r
-        case CSIDL_WINDOWS:              sv = get_unicode_env(aTHX_ L"SystemRoot");         break;\r
-        }\r
-        if (sv) {\r
-            ST(0) = sv;\r
-            XSRETURN(1);\r
-        }\r
-    }\r
-\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_GetFileVersion)\r
-{\r
-    dXSARGS;\r
-    DWORD size;\r
-    DWORD handle;\r
-    char *filename;\r
-    char *data;\r
-\r
-    if (items != 1)\r
-       croak("usage: Win32::GetFileVersion($filename)\n");\r
-\r
-    filename = SvPV_nolen(ST(0));\r
-    size = GetFileVersionInfoSize(filename, &handle);\r
-    if (!size)\r
-        XSRETURN_UNDEF;\r
-\r
-    New(0, data, size, char);\r
-    if (!data)\r
-        XSRETURN_UNDEF;\r
-\r
-    if (GetFileVersionInfo(filename, handle, size, data)) {\r
-        VS_FIXEDFILEINFO *info;\r
-        UINT len;\r
-        if (VerQueryValue(data, "\\", (void**)&info, &len)) {\r
-            int dwValueMS1 = (info->dwFileVersionMS>>16);\r
-            int dwValueMS2 = (info->dwFileVersionMS&0xffff);\r
-            int dwValueLS1 = (info->dwFileVersionLS>>16);\r
-            int dwValueLS2 = (info->dwFileVersionLS&0xffff);\r
-\r
-            if (GIMME_V == G_ARRAY) {\r
-                EXTEND(SP, 4);\r
-                XST_mIV(0, dwValueMS1);\r
-                XST_mIV(1, dwValueMS2);\r
-                XST_mIV(2, dwValueLS1);\r
-                XST_mIV(3, dwValueLS2);\r
-                items = 4;\r
-            }\r
-            else {\r
-                char version[50];\r
-                sprintf(version, "%d.%d.%d.%d", dwValueMS1, dwValueMS2, dwValueLS1, dwValueLS2);\r
-                XST_mPV(0, version);\r
-            }\r
-        }\r
-    }\r
-    else\r
-        items = 0;\r
-\r
-    Safefree(data);\r
-    XSRETURN(items);\r
-}\r
-\r
-#ifdef __CYGWIN__\r
-XS(w32_SetChildShowWindow)\r
-{\r
-    /* This function doesn't do anything useful for cygwin.  In the\r
-     * MSWin32 case it modifies w32_showwindow, which is used by\r
-     * win32_spawnvp().  Since w32_showwindow is an internal variable\r
-     * inside the thread_intern structure, the MSWin32 implementation\r
-     * lives in win32/win32.c in the core Perl distribution.\r
-     */\r
-    dXSARGS;\r
-    XSRETURN_UNDEF;\r
-}\r
-#endif\r
-\r
-XS(w32_GetCwd)\r
-{\r
-    dXSARGS;\r
-    /* Make the host for current directory */\r
-    char* ptr = PerlEnv_get_childdir();\r
-    /*\r
-     * If ptr != Nullch\r
-     *   then it worked, set PV valid,\r
-     *   else return 'undef'\r
-     */\r
-    if (ptr) {\r
-       SV *sv = sv_newmortal();\r
-       sv_setpv(sv, ptr);\r
-       PerlEnv_free_childdir(ptr);\r
-\r
-#ifndef INCOMPLETE_TAINTS\r
-       SvTAINTED_on(sv);\r
-#endif\r
-\r
-       EXTEND(SP,1);\r
-       ST(0) = sv;\r
-       XSRETURN(1);\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_SetCwd)\r
-{\r
-    dXSARGS;\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::SetCwd($cwd)");\r
-\r
-    if (IsWin2000() && SvUTF8(ST(0))) {\r
-        WCHAR *wide = sv_to_wstr(aTHX_ ST(0));\r
-        char *ansi = my_ansipath(wide);\r
-        int rc = PerlDir_chdir(ansi);\r
-        Safefree(wide);\r
-        Safefree(ansi);\r
-        if (!rc)\r
-            XSRETURN_YES;\r
-    }\r
-    else {\r
-        if (!PerlDir_chdir(SvPV_nolen(ST(0))))\r
-            XSRETURN_YES;\r
-    }\r
-\r
-    XSRETURN_NO;\r
-}\r
-\r
-XS(w32_GetNextAvailDrive)\r
-{\r
-    dXSARGS;\r
-    char ix = 'C';\r
-    char root[] = "_:\\";\r
-\r
-    EXTEND(SP,1);\r
-    while (ix <= 'Z') {\r
-       root[0] = ix++;\r
-       if (GetDriveType(root) == 1) {\r
-           root[2] = '\0';\r
-           XSRETURN_PV(root);\r
-       }\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_GetLastError)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    XSRETURN_IV(GetLastError());\r
-}\r
-\r
-XS(w32_SetLastError)\r
-{\r
-    dXSARGS;\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::SetLastError($error)");\r
-    SetLastError((DWORD)SvIV(ST(0)));\r
-    XSRETURN_EMPTY;\r
-}\r
-\r
-XS(w32_LoginName)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    if (IsWin2000()) {\r
-        WCHAR name[128];\r
-        DWORD size = countof(name);\r
-        if (GetUserNameW(name, &size)) {\r
-            ST(0) = wstr_to_sv(aTHX_ name);\r
-            XSRETURN(1);\r
-        }\r
-    }\r
-    else {\r
-        char name[128];\r
-        DWORD size = countof(name);\r
-        if (GetUserNameA(name, &size)) {\r
-            /* size includes NULL */\r
-            ST(0) = sv_2mortal(newSVpvn(name, size-1));\r
-            XSRETURN(1);\r
-        }\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_NodeName)\r
-{\r
-    dXSARGS;\r
-    char name[MAX_COMPUTERNAME_LENGTH+1];\r
-    DWORD size = sizeof(name);\r
-    EXTEND(SP,1);\r
-    if (GetComputerName(name,&size)) {\r
-       /* size does NOT include NULL :-( */\r
-       ST(0) = sv_2mortal(newSVpvn(name,size));\r
-       XSRETURN(1);\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-\r
-XS(w32_DomainName)\r
-{\r
-    dXSARGS;\r
-    HMODULE module = LoadLibrary("netapi32.dll");\r
-    PFNNetApiBufferFree pfnNetApiBufferFree;\r
-    PFNNetWkstaGetInfo pfnNetWkstaGetInfo;\r
-\r
-    if (module) {\r
-        GETPROC(NetApiBufferFree);\r
-        GETPROC(NetWkstaGetInfo);\r
-    }\r
-    EXTEND(SP,1);\r
-    if (module && pfnNetWkstaGetInfo && pfnNetApiBufferFree) {\r
-       /* this way is more reliable, in case user has a local account. */\r
-       char dname[256];\r
-       DWORD dnamelen = sizeof(dname);\r
-       struct {\r
-           DWORD   wki100_platform_id;\r
-           LPWSTR  wki100_computername;\r
-           LPWSTR  wki100_langroup;\r
-           DWORD   wki100_ver_major;\r
-           DWORD   wki100_ver_minor;\r
-       } *pwi;\r
-       DWORD retval;\r
-       retval = pfnNetWkstaGetInfo(NULL, 100, &pwi);\r
-       /* NERR_Success *is* 0*/\r
-       if (retval == 0) {\r
-           if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {\r
-               WideCharToMultiByte(CP_ACP, 0, pwi->wki100_langroup,\r
-                                   -1, (LPSTR)dname, dnamelen, NULL, NULL);\r
-           }\r
-           else {\r
-               WideCharToMultiByte(CP_ACP, 0, pwi->wki100_computername,\r
-                                   -1, (LPSTR)dname, dnamelen, NULL, NULL);\r
-           }\r
-           pfnNetApiBufferFree(pwi);\r
-           FreeLibrary(module);\r
-           XSRETURN_PV(dname);\r
-       }\r
-       FreeLibrary(module);\r
-       SetLastError(retval);\r
-    }\r
-    else {\r
-       /* Win95 doesn't have NetWksta*(), so do it the old way */\r
-       char name[256];\r
-       DWORD size = sizeof(name);\r
-       if (module)\r
-           FreeLibrary(module);\r
-       if (GetUserName(name,&size)) {\r
-           char sid[ONE_K_BUFSIZE];\r
-           DWORD sidlen = sizeof(sid);\r
-           char dname[256];\r
-           DWORD dnamelen = sizeof(dname);\r
-           SID_NAME_USE snu;\r
-           if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,\r
-                                 dname, &dnamelen, &snu)) {\r
-               XSRETURN_PV(dname);             /* all that for this */\r
-           }\r
-       }\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_FsType)\r
-{\r
-    dXSARGS;\r
-    char fsname[256];\r
-    DWORD flags, filecomplen;\r
-    if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,\r
-                        &flags, fsname, sizeof(fsname))) {\r
-       if (GIMME_V == G_ARRAY) {\r
-           XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));\r
-           XPUSHs(sv_2mortal(newSViv(flags)));\r
-           XPUSHs(sv_2mortal(newSViv(filecomplen)));\r
-           PUTBACK;\r
-           return;\r
-       }\r
-       EXTEND(SP,1);\r
-       XSRETURN_PV(fsname);\r
-    }\r
-    XSRETURN_EMPTY;\r
-}\r
-\r
-XS(w32_GetOSVersion)\r
-{\r
-    dXSARGS;\r
-\r
-    if (GIMME_V == G_SCALAR) {\r
-        XSRETURN_IV(g_osver.dwPlatformId);\r
-    }\r
-    XPUSHs(sv_2mortal(newSVpvn(g_osver.szCSDVersion, strlen(g_osver.szCSDVersion))));\r
-\r
-    XPUSHs(sv_2mortal(newSViv(g_osver.dwMajorVersion)));\r
-    XPUSHs(sv_2mortal(newSViv(g_osver.dwMinorVersion)));\r
-    XPUSHs(sv_2mortal(newSViv(g_osver.dwBuildNumber)));\r
-    XPUSHs(sv_2mortal(newSViv(g_osver.dwPlatformId)));\r
-    if (g_osver_ex) {\r
-        XPUSHs(sv_2mortal(newSViv(g_osver.wServicePackMajor)));\r
-        XPUSHs(sv_2mortal(newSViv(g_osver.wServicePackMinor)));\r
-        XPUSHs(sv_2mortal(newSViv(g_osver.wSuiteMask)));\r
-        XPUSHs(sv_2mortal(newSViv(g_osver.wProductType)));\r
-    }\r
-    PUTBACK;\r
-}\r
-\r
-XS(w32_IsWinNT)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    XSRETURN_IV(IsWinNT());\r
-}\r
-\r
-XS(w32_IsWin95)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    XSRETURN_IV(IsWin95());\r
-}\r
-\r
-XS(w32_FormatMessage)\r
-{\r
-    dXSARGS;\r
-    DWORD source = 0;\r
-    char msgbuf[ONE_K_BUFSIZE];\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");\r
-\r
-    if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,\r
-                       &source, (DWORD)SvIV(ST(0)), 0,\r
-                       msgbuf, sizeof(msgbuf)-1, NULL))\r
-    {\r
-        XSRETURN_PV(msgbuf);\r
-    }\r
-\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_Spawn)\r
-{\r
-    dXSARGS;\r
-    char *cmd, *args;\r
-    void *env;\r
-    char *dir;\r
-    PROCESS_INFORMATION stProcInfo;\r
-    STARTUPINFO stStartInfo;\r
-    BOOL bSuccess = FALSE;\r
-\r
-    if (items != 3)\r
-       Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)");\r
-\r
-    cmd = SvPV_nolen(ST(0));\r
-    args = SvPV_nolen(ST(1));\r
-\r
-    env = PerlEnv_get_childenv();\r
-    dir = PerlEnv_get_childdir();\r
-\r
-    memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */\r
-    stStartInfo.cb = sizeof(stStartInfo);          /* Set the structure size */\r
-    stStartInfo.dwFlags = STARTF_USESHOWWINDOW;            /* Enable wShowWindow control */\r
-    stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */\r
-\r
-    if (CreateProcess(\r
-               cmd,                    /* Image path */\r
-               args,                   /* Arguments for command line */\r
-               NULL,                   /* Default process security */\r
-               NULL,                   /* Default thread security */\r
-               FALSE,                  /* Must be TRUE to use std handles */\r
-               NORMAL_PRIORITY_CLASS,  /* No special scheduling */\r
-               env,                    /* Inherit our environment block */\r
-               dir,                    /* Inherit our currrent directory */\r
-               &stStartInfo,           /* -> Startup info */\r
-               &stProcInfo))           /* <- Process info (if OK) */\r
-    {\r
-       int pid = (int)stProcInfo.dwProcessId;\r
-       if (IsWin95() && pid < 0)\r
-           pid = -pid;\r
-       sv_setiv(ST(2), pid);\r
-       CloseHandle(stProcInfo.hThread);/* library source code does this. */\r
-       bSuccess = TRUE;\r
-    }\r
-    PerlEnv_free_childenv(env);\r
-    PerlEnv_free_childdir(dir);\r
-    XSRETURN_IV(bSuccess);\r
-}\r
-\r
-XS(w32_GetTickCount)\r
-{\r
-    dXSARGS;\r
-    DWORD msec = GetTickCount();\r
-    EXTEND(SP,1);\r
-    if ((IV)msec > 0)\r
-       XSRETURN_IV(msec);\r
-    XSRETURN_NV(msec);\r
-}\r
-\r
-XS(w32_GetShortPathName)\r
-{\r
-    dXSARGS;\r
-    SV *shortpath;\r
-    DWORD len;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)");\r
-\r
-    if (IsWin2000()) {\r
-        WCHAR wshort[MAX_PATH+1];\r
-        WCHAR *wlong = sv_to_wstr(aTHX_ ST(0));\r
-        len = GetShortPathNameW(wlong, wshort, countof(wshort));\r
-        Safefree(wlong);\r
-        if (len && len < sizeof(wshort)) {\r
-            ST(0) = wstr_to_sv(aTHX_ wshort);\r
-            XSRETURN(1);\r
-        }\r
-        XSRETURN_UNDEF;\r
-    }\r
-\r
-    shortpath = sv_mortalcopy(ST(0));\r
-    SvUPGRADE(shortpath, SVt_PV);\r
-    if (!SvPVX(shortpath) || !SvLEN(shortpath))\r
-        XSRETURN_UNDEF;\r
-\r
-    /* src == target is allowed */\r
-    do {\r
-       len = GetShortPathName(SvPVX(shortpath),\r
-                              SvPVX(shortpath),\r
-                              (DWORD)SvLEN(shortpath));\r
-    } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));\r
-    if (len) {\r
-       SvCUR_set(shortpath,len);\r
-       *SvEND(shortpath) = '\0';\r
-       ST(0) = shortpath;\r
-       XSRETURN(1);\r
-    }\r
-    XSRETURN_UNDEF;\r
-}\r
-\r
-XS(w32_GetFullPathName)\r
-{\r
-    dXSARGS;\r
-    char *fullname;\r
-    char *ansi = NULL;\r
-\r
-/* The code below relies on the fact that PerlDir_mapX() returns an\r
- * absolute path, which is only true under PERL_IMPLICIT_SYS when\r
- * we use the virtualization code from win32/vdir.h.\r
- * Without it PerlDir_mapX() is a no-op and we need to use the same\r
- * code as we use for Cygwin.\r
- */\r
-#if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)\r
-    char buffer[2*MAX_PATH];\r
-#endif\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)");\r
-\r
-#if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)\r
-    if (IsWin2000()) {\r
-        WCHAR *filename = sv_to_wstr(aTHX_ ST(0));\r
-        WCHAR full[2*MAX_PATH];\r
-        DWORD len = GetFullPathNameW(filename, countof(full), full, NULL);\r
-        Safefree(filename);\r
-        if (len == 0 || len >= countof(full))\r
-            XSRETURN_EMPTY;\r
-        ansi = fullname = my_ansipath(full);\r
-    }\r
-    else {\r
-        DWORD len = GetFullPathNameA(SvPV_nolen(ST(0)), countof(buffer), buffer, NULL);\r
-        if (len == 0 || len >= countof(buffer))\r
-            XSRETURN_EMPTY;\r
-        fullname = buffer;\r
-    }\r
-#else\r
-    /* Don't use my_ansipath() unless the $filename argument is in Unicode.\r
-     * If the relative path doesn't exist, GetShortPathName() will fail and\r
-     * my_ansipath() will use the long name with replacement characters.\r
-     * In that case we will be better off using PerlDir_mapA(), which\r
-     * already uses the ANSI name of the current directory.\r
-     *\r
-     * XXX The one missing case is where we could downgrade $filename\r
-     * XXX from UTF8 into the current codepage.\r
-     */\r
-    if (IsWin2000() && SvUTF8(ST(0))) {\r
-        WCHAR *filename = sv_to_wstr(aTHX_ ST(0));\r
-        WCHAR *mappedname = PerlDir_mapW(filename);\r
-        Safefree(filename);\r
-        ansi = fullname = my_ansipath(mappedname);\r
-    }\r
-    else {\r
-        fullname = PerlDir_mapA(SvPV_nolen(ST(0)));\r
-    }\r
-#  if PERL_VERSION < 8\r
-    {\r
-        /* PerlDir_mapX() in Perl 5.6 used to return forward slashes */\r
-        char *str = fullname;\r
-        while (*str) {\r
-            if (*str == '/')\r
-                *str = '\\';\r
-            ++str;\r
-        }\r
-    }\r
-#  endif\r
-#endif\r
-\r
-    /* GetFullPathName() on Windows NT drops trailing backslash */\r
-    if (g_osver.dwMajorVersion == 4 && *fullname) {\r
-        STRLEN len;\r
-        char *pv = SvPV(ST(0), len);\r
-        char *lastchar = fullname + strlen(fullname) - 1;\r
-        /* If ST(0) ends with a slash, but fullname doesn't ... */\r
-        if (len && (pv[len-1] == '/' || pv[len-1] == '\\') && *lastchar != '\\') {\r
-            /* fullname is the MAX_PATH+1 sized buffer returned from PerlDir_mapA()\r
-             * or the 2*MAX_PATH sized local buffer in the __CYGWIN__ case.\r
-             */\r
-            if (lastchar - fullname < MAX_PATH - 1)\r
-                strcpy(lastchar+1, "\\");\r
-        }\r
-    }\r
-\r
-    if (GIMME_V == G_ARRAY) {\r
-        char *filepart = strrchr(fullname, '\\');\r
-\r
-        EXTEND(SP,1);\r
-        if (filepart) {\r
-            XST_mPV(1, ++filepart);\r
-            *filepart = '\0';\r
-        }\r
-        else {\r
-            XST_mPVN(1, "", 0);\r
-        }\r
-        items = 2;\r
-    }\r
-    XST_mPV(0, fullname);\r
-\r
-    if (ansi)\r
-        Safefree(ansi);\r
-    XSRETURN(items);\r
-}\r
-\r
-XS(w32_GetLongPathName)\r
-{\r
-    dXSARGS;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)");\r
-\r
-    if (IsWin2000()) {\r
-        WCHAR *wstr = sv_to_wstr(aTHX_ ST(0));\r
-        WCHAR wide_path[MAX_PATH+1];\r
-        WCHAR *long_path;\r
-\r
-        if (wcslen(wstr) < countof(wide_path)) {\r
-            wcscpy(wide_path, wstr);\r
-            long_path = my_longpathW(wide_path);\r
-            if (long_path) {\r
-                Safefree(wstr);\r
-                ST(0) = wstr_to_sv(aTHX_ long_path);\r
-                XSRETURN(1);\r
-            }\r
-        }\r
-        Safefree(wstr);\r
-    }\r
-    else {\r
-        SV *path;\r
-        char tmpbuf[MAX_PATH+1];\r
-        char *pathstr;\r
-        STRLEN len;\r
-\r
-        path = ST(0);\r
-        pathstr = SvPV(path,len);\r
-        if (len < sizeof(tmpbuf)) {\r
-            strcpy(tmpbuf, pathstr);\r
-            pathstr = my_longpathA(tmpbuf);\r
-            if (pathstr) {\r
-                ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));\r
-                XSRETURN(1);\r
-            }\r
-        }\r
-    }\r
-    XSRETURN_EMPTY;\r
-}\r
-\r
-XS(w32_GetANSIPathName)\r
-{\r
-    dXSARGS;\r
-    WCHAR *wide_path;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::GetANSIPathName($pathname)");\r
-\r
-    wide_path = sv_to_wstr(aTHX_ ST(0));\r
-    ST(0) = wstr_to_ansipath(aTHX_ wide_path);\r
-    Safefree(wide_path);\r
-    XSRETURN(1);\r
-}\r
-\r
-XS(w32_Sleep)\r
-{\r
-    dXSARGS;\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");\r
-    Sleep((DWORD)SvIV(ST(0)));\r
-    XSRETURN_YES;\r
-}\r
-\r
-XS(w32_CopyFile)\r
-{\r
-    dXSARGS;\r
-    BOOL bResult;\r
-    char *pszSourceFile;\r
-    char szSourceFile[MAX_PATH+1];\r
-\r
-    if (items != 3)\r
-       Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");\r
-\r
-    pszSourceFile = PerlDir_mapA(SvPV_nolen(ST(0)));\r
-    if (strlen(pszSourceFile) < sizeof(szSourceFile)) {\r
-        strcpy(szSourceFile, pszSourceFile);\r
-        bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));\r
-        if (bResult)\r
-            XSRETURN_YES;\r
-    }\r
-    XSRETURN_NO;\r
-}\r
-\r
-XS(w32_OutputDebugString)\r
-{\r
-    dXSARGS;\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::OutputDebugString($string)");\r
-\r
-    if (SvUTF8(ST(0))) {\r
-        WCHAR *str = sv_to_wstr(aTHX_ ST(0));\r
-        OutputDebugStringW(str);\r
-        Safefree(str);\r
-    }\r
-    else\r
-        OutputDebugStringA(SvPV_nolen(ST(0)));\r
-\r
-    XSRETURN_EMPTY;\r
-}\r
-\r
-XS(w32_GetCurrentProcessId)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    XSRETURN_IV(GetCurrentProcessId());\r
-}\r
-\r
-XS(w32_GetCurrentThreadId)\r
-{\r
-    dXSARGS;\r
-    EXTEND(SP,1);\r
-    XSRETURN_IV(GetCurrentThreadId());\r
-}\r
-\r
-XS(w32_CreateDirectory)\r
-{\r
-    dXSARGS;\r
-    BOOL result;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::CreateDirectory($dir)");\r
-\r
-    if (IsWin2000() && SvUTF8(ST(0))) {\r
-        WCHAR *dir = sv_to_wstr(aTHX_ ST(0));\r
-        result = CreateDirectoryW(dir, NULL);\r
-        Safefree(dir);\r
-    }\r
-    else {\r
-        result = CreateDirectoryA(SvPV_nolen(ST(0)), NULL);\r
-    }\r
-\r
-    ST(0) = boolSV(result);\r
-    XSRETURN(1);\r
-}\r
-\r
-XS(w32_CreateFile)\r
-{\r
-    dXSARGS;\r
-    HANDLE handle;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::CreateFile($file)");\r
-\r
-    if (IsWin2000() && SvUTF8(ST(0))) {\r
-        WCHAR *file = sv_to_wstr(aTHX_ ST(0));\r
-        handle = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_WRITE,\r
-                             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);\r
-        Safefree(file);\r
-    }\r
-    else {\r
-        handle = CreateFileA(SvPV_nolen(ST(0)), GENERIC_WRITE, FILE_SHARE_WRITE,\r
-                             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);\r
-    }\r
-\r
-    if (handle != INVALID_HANDLE_VALUE)\r
-        CloseHandle(handle);\r
-\r
-    ST(0) = boolSV(handle != INVALID_HANDLE_VALUE);\r
-    XSRETURN(1);\r
-}\r
-\r
-XS(w32_GetSystemMetrics)\r
-{\r
-    dXSARGS;\r
-\r
-    if (items != 1)\r
-       Perl_croak(aTHX_ "usage: Win32::GetSystemMetrics($index)");\r
-\r
-    XSRETURN_IV(GetSystemMetrics((int)SvIV(ST(0))));\r
-}\r
-\r
-XS(w32_GetProductInfo)\r
-{\r
-    dXSARGS;\r
-    DWORD type;\r
-    HMODULE module;\r
-    PFNGetProductInfo pfnGetProductInfo;\r
-\r
-    if (items != 4)\r
-       Perl_croak(aTHX_ "usage: Win32::GetProductInfo($major,$minor,$spmajor,$spminor)");\r
-\r
-    module = GetModuleHandle("kernel32.dll");\r
-    GETPROC(GetProductInfo);\r
-    if (pfnGetProductInfo &&\r
-        pfnGetProductInfo((DWORD)SvIV(ST(0)), (DWORD)SvIV(ST(1)),\r
-                          (DWORD)SvIV(ST(2)), (DWORD)SvIV(ST(3)), &type))\r
-    {\r
-        XSRETURN_IV(type);\r
-    }\r
-\r
-    /* PRODUCT_UNDEFINED */\r
-    XSRETURN_IV(0);\r
-}\r
-\r
-MODULE = Win32            PACKAGE = Win32\r
-\r
-PROTOTYPES: DISABLE\r
-\r
-BOOT:\r
-{\r
-    char *file = __FILE__;\r
-\r
-    if (g_osver.dwOSVersionInfoSize == 0) {\r
-        g_osver.dwOSVersionInfoSize = sizeof(g_osver);\r
-        if (!GetVersionExA((OSVERSIONINFOA*)&g_osver)) {\r
-            g_osver_ex = FALSE;\r
-            g_osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);\r
-            GetVersionExA((OSVERSIONINFOA*)&g_osver);\r
-        }\r
-    }\r
-\r
-    newXS("Win32::LookupAccountName", w32_LookupAccountName, file);\r
-    newXS("Win32::LookupAccountSID", w32_LookupAccountSID, file);\r
-    newXS("Win32::InitiateSystemShutdown", w32_InitiateSystemShutdown, file);\r
-    newXS("Win32::AbortSystemShutdown", w32_AbortSystemShutdown, file);\r
-    newXS("Win32::ExpandEnvironmentStrings", w32_ExpandEnvironmentStrings, file);\r
-    newXS("Win32::MsgBox", w32_MsgBox, file);\r
-    newXS("Win32::LoadLibrary", w32_LoadLibrary, file);\r
-    newXS("Win32::FreeLibrary", w32_FreeLibrary, file);\r
-    newXS("Win32::GetProcAddress", w32_GetProcAddress, file);\r
-    newXS("Win32::RegisterServer", w32_RegisterServer, file);\r
-    newXS("Win32::UnregisterServer", w32_UnregisterServer, file);\r
-    newXS("Win32::GetArchName", w32_GetArchName, file);\r
-    newXS("Win32::GetChipName", w32_GetChipName, file);\r
-    newXS("Win32::GuidGen", w32_GuidGen, file);\r
-    newXS("Win32::GetFolderPath", w32_GetFolderPath, file);\r
-    newXS("Win32::IsAdminUser", w32_IsAdminUser, file);\r
-    newXS("Win32::GetFileVersion", w32_GetFileVersion, file);\r
-\r
-    newXS("Win32::GetCwd", w32_GetCwd, file);\r
-    newXS("Win32::SetCwd", w32_SetCwd, file);\r
-    newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);\r
-    newXS("Win32::GetLastError", w32_GetLastError, file);\r
-    newXS("Win32::SetLastError", w32_SetLastError, file);\r
-    newXS("Win32::LoginName", w32_LoginName, file);\r
-    newXS("Win32::NodeName", w32_NodeName, file);\r
-    newXS("Win32::DomainName", w32_DomainName, file);\r
-    newXS("Win32::FsType", w32_FsType, file);\r
-    newXS("Win32::GetOSVersion", w32_GetOSVersion, file);\r
-    newXS("Win32::IsWinNT", w32_IsWinNT, file);\r
-    newXS("Win32::IsWin95", w32_IsWin95, file);\r
-    newXS("Win32::FormatMessage", w32_FormatMessage, file);\r
-    newXS("Win32::Spawn", w32_Spawn, file);\r
-    newXS("Win32::GetTickCount", w32_GetTickCount, file);\r
-    newXS("Win32::GetShortPathName", w32_GetShortPathName, file);\r
-    newXS("Win32::GetFullPathName", w32_GetFullPathName, file);\r
-    newXS("Win32::GetLongPathName", w32_GetLongPathName, file);\r
-    newXS("Win32::GetANSIPathName", w32_GetANSIPathName, file);\r
-    newXS("Win32::CopyFile", w32_CopyFile, file);\r
-    newXS("Win32::Sleep", w32_Sleep, file);\r
-    newXS("Win32::OutputDebugString", w32_OutputDebugString, file);\r
-    newXS("Win32::GetCurrentProcessId", w32_GetCurrentProcessId, file);\r
-    newXS("Win32::GetCurrentThreadId", w32_GetCurrentThreadId, file);\r
-    newXS("Win32::CreateDirectory", w32_CreateDirectory, file);\r
-    newXS("Win32::CreateFile", w32_CreateFile, file);\r
-    newXS("Win32::GetSystemMetrics", w32_GetSystemMetrics, file);\r
-    newXS("Win32::GetProductInfo", w32_GetProductInfo, file);\r
-#ifdef __CYGWIN__\r
-    newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);\r
-#endif\r
-    XSRETURN_YES;\r
-}\r
+#define WIN32_LEAN_AND_MEAN
+#include <wctype.h>
+#include <windows.h>
+#include <shlobj.h>
+
+#define PERL_NO_GET_CONTEXT
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifndef countof
+#  define countof(array) (sizeof (array) / sizeof (*(array)))
+#endif
+
+#define SE_SHUTDOWN_NAMEA   "SeShutdownPrivilege"
+
+#ifndef WC_NO_BEST_FIT_CHARS
+#  define WC_NO_BEST_FIT_CHARS 0x00000400
+#endif
+
+#define GETPROC(fn) pfn##fn = (PFN##fn)GetProcAddress(module, #fn)
+
+typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathA)(HWND, char*, int, BOOL);
+typedef BOOL (WINAPI *PFNSHGetSpecialFolderPathW)(HWND, WCHAR*, int, BOOL);
+typedef HRESULT (WINAPI *PFNSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPTSTR);
+typedef HRESULT (WINAPI *PFNSHGetFolderPathW)(HWND, int, HANDLE, DWORD, LPWSTR);
+typedef BOOL (WINAPI *PFNCreateEnvironmentBlock)(void**, HANDLE, BOOL);
+typedef BOOL (WINAPI *PFNDestroyEnvironmentBlock)(void*);
+typedef int (__stdcall *PFNDllRegisterServer)(void);
+typedef int (__stdcall *PFNDllUnregisterServer)(void);
+typedef DWORD (__stdcall *PFNNetApiBufferFree)(void*);
+typedef DWORD (__stdcall *PFNNetWkstaGetInfo)(LPWSTR, DWORD, void*);
+
+typedef BOOL (__stdcall *PFNOpenProcessToken)(HANDLE, DWORD, HANDLE*);
+typedef BOOL (__stdcall *PFNOpenThreadToken)(HANDLE, DWORD, BOOL, HANDLE*);
+typedef BOOL (__stdcall *PFNGetTokenInformation)(HANDLE, TOKEN_INFORMATION_CLASS, void*, DWORD, DWORD*);
+typedef BOOL (__stdcall *PFNAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD,
+                                                      DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, PSID*);
+typedef BOOL (__stdcall *PFNEqualSid)(PSID, PSID);
+typedef void* (__stdcall *PFNFreeSid)(PSID);
+typedef BOOL (__stdcall *PFNIsUserAnAdmin)(void);
+typedef BOOL (WINAPI *PFNGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD*);
+typedef void (WINAPI *PFNGetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo);
+
+#ifndef CSIDL_MYMUSIC
+#   define CSIDL_MYMUSIC              0x000D
+#endif
+#ifndef CSIDL_MYVIDEO
+#   define CSIDL_MYVIDEO              0x000E
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#   define CSIDL_LOCAL_APPDATA        0x001C
+#endif
+#ifndef CSIDL_COMMON_FAVORITES
+#   define CSIDL_COMMON_FAVORITES     0x001F
+#endif
+#ifndef CSIDL_INTERNET_CACHE
+#   define CSIDL_INTERNET_CACHE       0x0020
+#endif
+#ifndef CSIDL_COOKIES
+#   define CSIDL_COOKIES              0x0021
+#endif
+#ifndef CSIDL_HISTORY
+#   define CSIDL_HISTORY              0x0022
+#endif
+#ifndef CSIDL_COMMON_APPDATA
+#   define CSIDL_COMMON_APPDATA       0x0023
+#endif
+#ifndef CSIDL_WINDOWS
+#   define CSIDL_WINDOWS              0x0024
+#endif
+#ifndef CSIDL_PROGRAM_FILES
+#   define CSIDL_PROGRAM_FILES        0x0026
+#endif
+#ifndef CSIDL_MYPICTURES
+#   define CSIDL_MYPICTURES           0x0027
+#endif
+#ifndef CSIDL_PROFILE
+#   define CSIDL_PROFILE              0x0028
+#endif
+#ifndef CSIDL_PROGRAM_FILES_COMMON
+#   define CSIDL_PROGRAM_FILES_COMMON 0x002B
+#endif
+#ifndef CSIDL_COMMON_TEMPLATES
+#   define CSIDL_COMMON_TEMPLATES     0x002D
+#endif
+#ifndef CSIDL_COMMON_DOCUMENTS
+#   define CSIDL_COMMON_DOCUMENTS     0x002E
+#endif
+#ifndef CSIDL_COMMON_ADMINTOOLS
+#   define CSIDL_COMMON_ADMINTOOLS    0x002F
+#endif
+#ifndef CSIDL_ADMINTOOLS
+#   define CSIDL_ADMINTOOLS           0x0030
+#endif
+#ifndef CSIDL_COMMON_MUSIC
+#   define CSIDL_COMMON_MUSIC         0x0035
+#endif
+#ifndef CSIDL_COMMON_PICTURES
+#   define CSIDL_COMMON_PICTURES      0x0036
+#endif
+#ifndef CSIDL_COMMON_VIDEO
+#   define CSIDL_COMMON_VIDEO         0x0037
+#endif
+#ifndef CSIDL_CDBURN_AREA
+#   define CSIDL_CDBURN_AREA          0x003B
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#   define CSIDL_FLAG_CREATE          0x8000
+#endif
+
+/* Use explicit struct definition because wSuiteMask and
+ * wProductType are not defined in the VC++ 6.0 headers.
+ * WORD type has been replaced by unsigned short because
+ * WORD is already used by Perl itself.
+ */
+struct {
+    DWORD dwOSVersionInfoSize;
+    DWORD dwMajorVersion;
+    DWORD dwMinorVersion;
+    DWORD dwBuildNumber;
+    DWORD dwPlatformId;
+    CHAR  szCSDVersion[128];
+    unsigned short wServicePackMajor;
+    unsigned short wServicePackMinor;
+    unsigned short wSuiteMask;
+    BYTE  wProductType;
+    BYTE  wReserved;
+}   g_osver = {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0};
+BOOL g_osver_ex = TRUE;
+
+#define ONE_K_BUFSIZE  1024
+
+int
+IsWin95(void)
+{
+    return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
+}
+
+int
+IsWinNT(void)
+{
+    return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+
+int
+IsWin2000(void)
+{
+    return (g_osver.dwMajorVersion > 4);
+}
+
+/* Convert SV to wide character string.  The return value must be
+ * freed using Safefree().
+ */
+WCHAR*
+sv_to_wstr(pTHX_ SV *sv)
+{
+    DWORD wlen;
+    WCHAR *wstr;
+    STRLEN len;
+    char *str = SvPV(sv, len);
+    UINT cp = SvUTF8(sv) ? CP_UTF8 : CP_ACP;
+
+    wlen = MultiByteToWideChar(cp, 0, str, (int)(len+1), NULL, 0);
+    New(0, wstr, wlen, WCHAR);
+    MultiByteToWideChar(cp, 0, str, (int)(len+1), wstr, wlen);
+
+    return wstr;
+}
+
+/* Convert wide character string to mortal SV.  Use UTF8 encoding
+ * if the string cannot be represented in the system codepage.
+ */
+SV *
+wstr_to_sv(pTHX_ WCHAR *wstr)
+{
+    int wlen = (int)wcslen(wstr)+1;
+    BOOL use_default = FALSE;
+    int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, NULL, 0, NULL, NULL);
+    SV *sv = sv_2mortal(newSV(len));
+
+    len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen, SvPVX(sv), len, NULL, &use_default);
+    if (use_default) {
+        len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, NULL, 0, NULL, NULL);
+        sv_grow(sv, len);
+        len = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen, SvPVX(sv), len, NULL, NULL);
+        SvUTF8_on(sv);
+    }
+    /* Shouldn't really ever fail since we ask for the required length first, but who knows... */
+    if (len) {
+        SvPOK_on(sv);
+        SvCUR_set(sv, len-1);
+    }
+    return sv;
+}
+
+/* Retrieve a variable from the Unicode environment in a mortal SV.
+ *
+ * Recreates the Unicode environment because a bug in earlier Perl versions
+ * overwrites it with the ANSI version, which contains replacement
+ * characters for the characters not in the ANSI codepage.
+ */
+SV*
+get_unicode_env(pTHX_ WCHAR *name)
+{
+    SV *sv = NULL;
+    void *env;
+    HANDLE token;
+    HMODULE module;
+    PFNOpenProcessToken pfnOpenProcessToken;
+
+    /* Get security token for the current process owner */
+    module = LoadLibrary("advapi32.dll");
+    if (!module)
+        return NULL;
+
+    GETPROC(OpenProcessToken);
+
+    if (pfnOpenProcessToken == NULL ||
+        !pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &token))
+    {
+        FreeLibrary(module);
+        return NULL;
+    }
+    FreeLibrary(module);
+
+    /* Create a Unicode environment block for this process */
+    module = LoadLibrary("userenv.dll");
+    if (module) {
+        PFNCreateEnvironmentBlock pfnCreateEnvironmentBlock;
+        PFNDestroyEnvironmentBlock pfnDestroyEnvironmentBlock;
+
+        GETPROC(CreateEnvironmentBlock);
+        GETPROC(DestroyEnvironmentBlock);
+
+        if (pfnCreateEnvironmentBlock && pfnDestroyEnvironmentBlock &&
+            pfnCreateEnvironmentBlock(&env, token, FALSE))
+        {
+            size_t name_len = wcslen(name);
+            WCHAR *entry = (WCHAR *)env;
+            while (*entry) {
+                size_t i;
+                size_t entry_len = wcslen(entry);
+                BOOL equal = (entry_len > name_len) && (entry[name_len] == '=');
+
+                for (i=0; equal && i < name_len; ++i)
+                    equal = (towupper(entry[i]) == towupper(name[i]));
+
+                if (equal) {
+                    sv = wstr_to_sv(aTHX_ entry+name_len+1);
+                    break;
+                }
+                entry += entry_len+1;
+            }
+            pfnDestroyEnvironmentBlock(env);
+        }
+        FreeLibrary(module);
+    }
+    CloseHandle(token);
+    return sv;
+}
+
+/* Define both an ANSI and a Wide version of win32_longpath */
+
+#define CHAR_T            char
+#define WIN32_FIND_DATA_T WIN32_FIND_DATAA
+#define FN_FINDFIRSTFILE  FindFirstFileA
+#define FN_STRLEN         strlen
+#define FN_STRCPY         strcpy
+#define LONGPATH          my_longpathA
+#include "longpath.inc"
+
+#define CHAR_T            WCHAR
+#define WIN32_FIND_DATA_T WIN32_FIND_DATAW
+#define FN_FINDFIRSTFILE  FindFirstFileW
+#define FN_STRLEN         wcslen
+#define FN_STRCPY         wcscpy
+#define LONGPATH          my_longpathW
+#include "longpath.inc"
+
+/* The my_ansipath() function takes a Unicode filename and converts it
+ * into the current Windows codepage. If some characters cannot be mapped,
+ * then it will convert the short name instead.
+ *
+ * The buffer to the ansi pathname must be freed with Safefree() when it
+ * it no longer needed.
+ *
+ * The argument to my_ansipath() must exist before this function is
+ * called; otherwise there is no way to determine the short path name.
+ *
+ * Ideas for future refinement:
+ * - Only convert those segments of the path that are not in the current
+ *   codepage, but leave the other segments in their long form.
+ * - If the resulting name is longer than MAX_PATH, start converting
+ *   additional path segments into short names until the full name
+ *   is shorter than MAX_PATH.  Shorten the filename part last!
+ */
+
+/* This is a modified version of core Perl win32/win32.c(win32_ansipath).
+ * It uses New() etc. instead of win32_malloc().
+ */
+
+char *
+my_ansipath(const WCHAR *widename)
+{
+    char *name;
+    BOOL use_default = FALSE;
+    int widelen = (int)wcslen(widename)+1;
+    int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
+                                  NULL, 0, NULL, NULL);
+    New(0, name, len, char);
+    WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
+                        name, len, NULL, &use_default);
+    if (use_default) {
+        DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
+        if (shortlen) {
+            WCHAR *shortname;
+            New(0, shortname, shortlen, WCHAR);
+            shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
+
+            len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
+                                      NULL, 0, NULL, NULL);
+            Renew(name, len, char);
+            WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
+                                name, len, NULL, NULL);
+            Safefree(shortname);
+        }
+    }
+    return name;
+}
+
+/* Convert wide character path to ANSI path and return as mortal SV. */
+SV*
+wstr_to_ansipath(pTHX_ WCHAR *wstr)
+{
+    char *ansi = my_ansipath(wstr);
+    SV *sv = sv_2mortal(newSVpvn(ansi, strlen(ansi)));
+    Safefree(ansi);
+    return sv;
+}
+
+#ifdef __CYGWIN__
+
+char*
+get_childdir(void)
+{
+    dTHX;
+    char* ptr;
+
+    if (IsWin2000()) {
+        WCHAR filename[MAX_PATH+1];
+        GetCurrentDirectoryW(MAX_PATH+1, filename);
+        ptr = my_ansipath(filename);
+    }
+    else {
+        char filename[MAX_PATH+1];
+        GetCurrentDirectoryA(MAX_PATH+1, filename);
+        New(0, ptr, strlen(filename)+1, char);
+        strcpy(ptr, filename);
+    }
+    return ptr;
+}
+
+void
+free_childdir(char *d)
+{
+    dTHX;
+    Safefree(d);
+}
+
+void*
+get_childenv(void)
+{
+    return NULL;
+}
+
+void
+free_childenv(void *d)
+{
+}
+
+#  define PerlDir_mapA(dir) (dir)
+
+#endif
+
+XS(w32_ExpandEnvironmentStrings)
+{
+    dXSARGS;
+
+    if (items != 1)
+       croak("usage: Win32::ExpandEnvironmentStrings($String);\n");
+
+    if (IsWin2000()) {
+        WCHAR value[31*1024];
+        WCHAR *source = sv_to_wstr(aTHX_ ST(0));
+        ExpandEnvironmentStringsW(source, value, countof(value)-1);
+        ST(0) = wstr_to_sv(aTHX_ value);
+        Safefree(source);
+        XSRETURN(1);
+    }
+    else {
+        char value[31*1024];
+        ExpandEnvironmentStringsA(SvPV_nolen(ST(0)), value, countof(value)-2);
+        XSRETURN_PV(value);
+    }
+}
+
+XS(w32_IsAdminUser)
+{
+    dXSARGS;
+    HMODULE                     module;
+    PFNIsUserAnAdmin            pfnIsUserAnAdmin;
+    PFNOpenThreadToken          pfnOpenThreadToken;
+    PFNOpenProcessToken         pfnOpenProcessToken;
+    PFNGetTokenInformation      pfnGetTokenInformation;
+    PFNAllocateAndInitializeSid pfnAllocateAndInitializeSid;
+    PFNEqualSid                 pfnEqualSid;
+    PFNFreeSid                  pfnFreeSid;
+    HANDLE                      hTok;
+    DWORD                       dwTokInfoLen;
+    TOKEN_GROUPS                *lpTokInfo;
+    SID_IDENTIFIER_AUTHORITY    NtAuth = SECURITY_NT_AUTHORITY;
+    PSID                        pAdminSid;
+    int                         iRetVal;
+    unsigned int                i;
+
+    if (items)
+        croak("usage: Win32::IsAdminUser()");
+
+    /* There is no concept of "Administrator" user accounts on Win9x systems,
+       so just return true. */
+    if (IsWin95())
+        XSRETURN_YES;
+
+    /* Use IsUserAnAdmin() when available.  On Vista this will only return TRUE
+     * if the process is running with elevated privileges and not just when the
+     * process owner is a member of the "Administrators" group.
+     */
+    module = LoadLibrary("shell32.dll");
+    if (module) {
+        GETPROC(IsUserAnAdmin);
+        if (pfnIsUserAnAdmin) {
+            EXTEND(SP, 1);
+            ST(0) = sv_2mortal(newSViv(pfnIsUserAnAdmin() ? 1 : 0));
+            FreeLibrary(module);
+            XSRETURN(1);
+        }
+        FreeLibrary(module);
+    }
+
+    module = LoadLibrary("advapi32.dll");
+    if (!module) {
+        warn("Cannot load advapi32.dll library");
+        XSRETURN_UNDEF;
+    }
+
+    GETPROC(OpenThreadToken);
+    GETPROC(OpenProcessToken);
+    GETPROC(GetTokenInformation);
+    GETPROC(AllocateAndInitializeSid);
+    GETPROC(EqualSid);
+    GETPROC(FreeSid);
+
+    if (!(pfnOpenThreadToken && pfnOpenProcessToken &&
+          pfnGetTokenInformation && pfnAllocateAndInitializeSid &&
+          pfnEqualSid && pfnFreeSid))
+    {
+        warn("Cannot load functions from advapi32.dll library");
+        FreeLibrary(module);
+        XSRETURN_UNDEF;
+    }
+
+    if (!pfnOpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hTok)) {
+        if (!pfnOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTok)) {
+            warn("Cannot open thread token or process token");
+            FreeLibrary(module);
+            XSRETURN_UNDEF;
+        }
+    }
+
+    pfnGetTokenInformation(hTok, TokenGroups, NULL, 0, &dwTokInfoLen);
+    if (!New(1, lpTokInfo, dwTokInfoLen, TOKEN_GROUPS)) {
+        warn("Cannot allocate token information structure");
+        CloseHandle(hTok);
+        FreeLibrary(module);
+        XSRETURN_UNDEF;
+    }
+
+    if (!pfnGetTokenInformation(hTok, TokenGroups, lpTokInfo, dwTokInfoLen,
+            &dwTokInfoLen))
+    {
+        warn("Cannot get token information");
+        Safefree(lpTokInfo);
+        CloseHandle(hTok);
+        FreeLibrary(module);
+        XSRETURN_UNDEF;
+    }
+
+    if (!pfnAllocateAndInitializeSid(&NtAuth, 2, SECURITY_BUILTIN_DOMAIN_RID,
+            DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid))
+    {
+        warn("Cannot allocate administrators' SID");
+        Safefree(lpTokInfo);
+        CloseHandle(hTok);
+        FreeLibrary(module);
+        XSRETURN_UNDEF;
+    }
+
+    iRetVal = 0;
+    for (i = 0; i < lpTokInfo->GroupCount; ++i) {
+        if (pfnEqualSid(lpTokInfo->Groups[i].Sid, pAdminSid)) {
+            iRetVal = 1;
+            break;
+        }
+    }
+
+    pfnFreeSid(pAdminSid);
+    Safefree(lpTokInfo);
+    CloseHandle(hTok);
+    FreeLibrary(module);
+
+    EXTEND(SP, 1);
+    ST(0) = sv_2mortal(newSViv(iRetVal));
+    XSRETURN(1);
+}
+
+XS(w32_LookupAccountName)
+{
+    dXSARGS;
+    char SID[400];
+    DWORD SIDLen;
+    SID_NAME_USE snu;
+    char Domain[256];
+    DWORD DomLen;
+    BOOL bResult;
+
+    if (items != 5)
+       croak("usage: Win32::LookupAccountName($system, $account, $domain, "
+             "$sid, $sidtype);\n");
+
+    SIDLen = sizeof(SID);
+    DomLen = sizeof(Domain);
+
+    bResult = LookupAccountNameA(SvPV_nolen(ST(0)),    /* System */
+                                 SvPV_nolen(ST(1)),    /* Account name */
+                                 &SID,                 /* SID structure */
+                                 &SIDLen,              /* Size of SID buffer */
+                                 Domain,               /* Domain buffer */
+                                 &DomLen,              /* Domain buffer size */
+                                 &snu);                        /* SID name type */
+    if (bResult) {
+       sv_setpv(ST(2), Domain);
+       sv_setpvn(ST(3), SID, SIDLen);
+       sv_setiv(ST(4), snu);
+       XSRETURN_YES;
+    }
+    XSRETURN_NO;
+}
+
+
+XS(w32_LookupAccountSID)
+{
+    dXSARGS;
+    PSID sid;
+    char Account[256];
+    DWORD AcctLen = sizeof(Account);
+    char Domain[256];
+    DWORD DomLen = sizeof(Domain);
+    SID_NAME_USE snu;
+    BOOL bResult;
+
+    if (items != 5)
+       croak("usage: Win32::LookupAccountSID($system, $sid, $account, $domain, $sidtype);\n");
+
+    sid = SvPV_nolen(ST(1));
+    if (IsValidSid(sid)) {
+        bResult = LookupAccountSidA(SvPV_nolen(ST(0)), /* System */
+                                    sid,               /* SID structure */
+                                    Account,           /* Account name buffer */
+                                    &AcctLen,          /* name buffer length */
+                                    Domain,            /* Domain buffer */
+                                    &DomLen,           /* Domain buffer length */
+                                    &snu);             /* SID name type */
+       if (bResult) {
+           sv_setpv(ST(2), Account);
+           sv_setpv(ST(3), Domain);
+           sv_setiv(ST(4), (IV)snu);
+           XSRETURN_YES;
+       }
+    }
+    XSRETURN_NO;
+}
+
+XS(w32_InitiateSystemShutdown)
+{
+    dXSARGS;
+    HANDLE hToken;              /* handle to process token   */
+    TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */
+    BOOL bRet;
+    char *machineName, *message;
+
+    if (items != 5)
+       croak("usage: Win32::InitiateSystemShutdown($machineName, $message, "
+             "$timeOut, $forceClose, $reboot);\n");
+
+    machineName = SvPV_nolen(ST(0));
+
+    if (OpenProcessToken(GetCurrentProcess(),
+                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+                        &hToken))
+    {
+        LookupPrivilegeValueA(machineName,
+                              SE_SHUTDOWN_NAMEA,
+                              &tkp.Privileges[0].Luid);
+
+       tkp.PrivilegeCount = 1; /* only setting one */
+       tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+       /* Get shutdown privilege for this process. */
+       AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+                             (PTOKEN_PRIVILEGES)NULL, 0);
+    }
+
+    message = SvPV_nolen(ST(1));
+    bRet = InitiateSystemShutdownA(machineName, message, (DWORD)SvIV(ST(2)),
+                                   (BOOL)SvIV(ST(3)), (BOOL)SvIV(ST(4)));
+
+    /* Disable shutdown privilege. */
+    tkp.Privileges[0].Attributes = 0; 
+    AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+                         (PTOKEN_PRIVILEGES)NULL, 0); 
+    CloseHandle(hToken);
+    XSRETURN_IV(bRet);
+}
+
+XS(w32_AbortSystemShutdown)
+{
+    dXSARGS;
+    HANDLE hToken;              /* handle to process token   */
+    TOKEN_PRIVILEGES tkp;       /* pointer to token structure  */
+    BOOL bRet;
+    char *machineName;
+
+    if (items != 1)
+       croak("usage: Win32::AbortSystemShutdown($machineName);\n");
+
+    machineName = SvPV_nolen(ST(0));
+
+    if (OpenProcessToken(GetCurrentProcess(),
+                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+                        &hToken))
+    {
+        LookupPrivilegeValueA(machineName,
+                              SE_SHUTDOWN_NAMEA,
+                              &tkp.Privileges[0].Luid);
+
+       tkp.PrivilegeCount = 1; /* only setting one */
+       tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+       /* Get shutdown privilege for this process. */
+       AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+                             (PTOKEN_PRIVILEGES)NULL, 0);
+    }
+
+    bRet = AbortSystemShutdownA(machineName);
+
+    /* Disable shutdown privilege. */
+    tkp.Privileges[0].Attributes = 0;
+    AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
+                         (PTOKEN_PRIVILEGES)NULL, 0);
+    CloseHandle(hToken);
+    XSRETURN_IV(bRet);
+}
+
+
+XS(w32_MsgBox)
+{
+    dXSARGS;
+    DWORD flags = MB_ICONEXCLAMATION;
+    I32 result;
+
+    if (items < 1 || items > 3)
+       croak("usage: Win32::MsgBox($message [, $flags [, $title]]);\n");
+
+    if (items > 1)
+        flags = (DWORD)SvIV(ST(1));
+
+    if (IsWin2000()) {
+        WCHAR *title = NULL;
+        WCHAR *msg = sv_to_wstr(aTHX_ ST(0));
+        if (items > 2)
+            title = sv_to_wstr(aTHX_ ST(2));
+        result = MessageBoxW(GetActiveWindow(), msg, title ? title : L"Perl", flags);
+        Safefree(msg);
+        if (title)
+            Safefree(title);
+    }
+    else {
+        char *title = "Perl";
+        char *msg = SvPV_nolen(ST(0));
+        if (items > 2)
+            title = SvPV_nolen(ST(2));
+        result = MessageBoxA(GetActiveWindow(), msg, title, flags);
+    }
+    XSRETURN_IV(result);
+}
+
+XS(w32_LoadLibrary)
+{
+    dXSARGS;
+    HANDLE hHandle;
+
+    if (items != 1)
+       croak("usage: Win32::LoadLibrary($libname)\n");
+    hHandle = LoadLibraryA(SvPV_nolen(ST(0)));
+#ifdef _WIN64
+    XSRETURN_IV((DWORD_PTR)hHandle);
+#else
+    XSRETURN_IV((DWORD)hHandle);
+#endif
+}
+
+XS(w32_FreeLibrary)
+{
+    dXSARGS;
+
+    if (items != 1)
+       croak("usage: Win32::FreeLibrary($handle)\n");
+    if (FreeLibrary(INT2PTR(HINSTANCE, SvIV(ST(0))))) {
+       XSRETURN_YES;
+    }
+    XSRETURN_NO;
+}
+
+XS(w32_GetProcAddress)
+{
+    dXSARGS;
+
+    if (items != 2)
+       croak("usage: Win32::GetProcAddress($hinstance, $procname)\n");
+    XSRETURN_IV(PTR2IV(GetProcAddress(INT2PTR(HINSTANCE, SvIV(ST(0))), SvPV_nolen(ST(1)))));
+}
+
+XS(w32_RegisterServer)
+{
+    dXSARGS;
+    BOOL result = FALSE;
+    HMODULE module;
+
+    if (items != 1)
+       croak("usage: Win32::RegisterServer($libname)\n");
+
+    module = LoadLibraryA(SvPV_nolen(ST(0)));
+    if (module) {
+       PFNDllRegisterServer pfnDllRegisterServer;
+        GETPROC(DllRegisterServer);
+       if (pfnDllRegisterServer && pfnDllRegisterServer() == 0)
+           result = TRUE;
+       FreeLibrary(module);
+    }
+    ST(0) = boolSV(result);
+    XSRETURN(1);
+}
+
+XS(w32_UnregisterServer)
+{
+    dXSARGS;
+    BOOL result = FALSE;
+    HINSTANCE module;
+
+    if (items != 1)
+       croak("usage: Win32::UnregisterServer($libname)\n");
+
+    module = LoadLibraryA(SvPV_nolen(ST(0)));
+    if (module) {
+       PFNDllUnregisterServer pfnDllUnregisterServer;
+        GETPROC(DllUnregisterServer);
+       if (pfnDllUnregisterServer && pfnDllUnregisterServer() == 0)
+           result = TRUE;
+       FreeLibrary(module);
+    }
+    ST(0) = boolSV(result);
+    XSRETURN(1);
+}
+
+/* XXX rather bogus */
+XS(w32_GetArchName)
+{
+    dXSARGS;
+    XSRETURN_PV(getenv("PROCESSOR_ARCHITECTURE"));
+}
+
+XS(w32_GetChipName)
+{
+    dXSARGS;
+    SYSTEM_INFO sysinfo;
+    HMODULE module;
+    PFNGetNativeSystemInfo pfnGetNativeSystemInfo;
+
+    Zero(&sysinfo,1,SYSTEM_INFO);
+    module = GetModuleHandle("kernel32.dll");
+    GETPROC(GetNativeSystemInfo);
+    if (pfnGetNativeSystemInfo)
+        pfnGetNativeSystemInfo(&sysinfo);
+    else
+        GetSystemInfo(&sysinfo);
+
+    /* XXX docs say dwProcessorType is deprecated on NT */
+    XSRETURN_IV(sysinfo.dwProcessorType);
+}
+
+XS(w32_GuidGen)
+{
+    dXSARGS;
+    GUID guid;
+    char szGUID[50] = {'\0'};
+    HRESULT  hr     = CoCreateGuid(&guid);
+
+    if (SUCCEEDED(hr)) {
+       LPOLESTR pStr = NULL;
+#ifdef __cplusplus
+       if (SUCCEEDED(StringFromCLSID(guid, &pStr))) {
+#else
+       if (SUCCEEDED(StringFromCLSID(&guid, &pStr))) {
+#endif
+            WideCharToMultiByte(CP_ACP, 0, pStr, (int)wcslen(pStr), szGUID,
+                                sizeof(szGUID), NULL, NULL);
+            CoTaskMemFree(pStr);
+            XSRETURN_PV(szGUID);
+        }
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_GetFolderPath)
+{
+    dXSARGS;
+    char path[MAX_PATH+1];
+    WCHAR wpath[MAX_PATH+1];
+    int folder;
+    int create = 0;
+    HMODULE module;
+
+    if (items != 1 && items != 2)
+       croak("usage: Win32::GetFolderPath($csidl [, $create])\n");
+
+    folder = (int)SvIV(ST(0));
+    if (items == 2)
+        create = SvTRUE(ST(1)) ? CSIDL_FLAG_CREATE : 0;
+
+    module = LoadLibrary("shfolder.dll");
+    if (module) {
+        PFNSHGetFolderPathA pfna;
+        if (IsWin2000()) {
+            PFNSHGetFolderPathW pfnw;
+            pfnw = (PFNSHGetFolderPathW)GetProcAddress(module, "SHGetFolderPathW");
+            if (pfnw && SUCCEEDED(pfnw(NULL, folder|create, NULL, 0, wpath))) {
+                FreeLibrary(module);
+                ST(0) = wstr_to_ansipath(aTHX_ wpath);
+                XSRETURN(1);
+            }
+        }
+        pfna = (PFNSHGetFolderPathA)GetProcAddress(module, "SHGetFolderPathA");
+        if (pfna && SUCCEEDED(pfna(NULL, folder|create, NULL, 0, path))) {
+            FreeLibrary(module);
+            XSRETURN_PV(path);
+        }
+        FreeLibrary(module);
+    }
+
+    module = LoadLibrary("shell32.dll");
+    if (module) {
+        PFNSHGetSpecialFolderPathA pfna;
+        if (IsWin2000()) {
+            PFNSHGetSpecialFolderPathW pfnw;
+            pfnw = (PFNSHGetSpecialFolderPathW)GetProcAddress(module, "SHGetSpecialFolderPathW");
+            if (pfnw && pfnw(NULL, wpath, folder, !!create)) {
+                FreeLibrary(module);
+                ST(0) = wstr_to_ansipath(aTHX_ wpath);
+                XSRETURN(1);
+            }
+        }
+        pfna = (PFNSHGetSpecialFolderPathA)GetProcAddress(module, "SHGetSpecialFolderPathA");
+        if (pfna && pfna(NULL, path, folder, !!create)) {
+            FreeLibrary(module);
+            XSRETURN_PV(path);
+        }
+        FreeLibrary(module);
+    }
+
+    /* SHGetFolderPathW() and SHGetSpecialFolderPathW() may fail on older
+     * Perl versions that have replaced the Unicode environment with an
+     * ANSI version.  Let's go spelunking in the registry now...
+     */
+    if (IsWin2000()) {
+        SV *sv;
+        HKEY hkey;
+        HKEY root = HKEY_CURRENT_USER;
+        WCHAR *name = NULL;
+
+        switch (folder) {
+        case CSIDL_ADMINTOOLS:                  name = L"Administrative Tools";        break;
+        case CSIDL_APPDATA:                     name = L"AppData";                     break;
+        case CSIDL_CDBURN_AREA:                 name = L"CD Burning";                  break;
+        case CSIDL_COOKIES:                     name = L"Cookies";                     break;
+        case CSIDL_DESKTOP:
+        case CSIDL_DESKTOPDIRECTORY:            name = L"Desktop";                     break;
+        case CSIDL_FAVORITES:                   name = L"Favorites";                   break;
+        case CSIDL_FONTS:                       name = L"Fonts";                       break;
+        case CSIDL_HISTORY:                     name = L"History";                     break;
+        case CSIDL_INTERNET_CACHE:              name = L"Cache";                       break;
+        case CSIDL_LOCAL_APPDATA:               name = L"Local AppData";               break;
+        case CSIDL_MYMUSIC:                     name = L"My Music";                    break;
+        case CSIDL_MYPICTURES:                  name = L"My Pictures";                 break;
+        case CSIDL_MYVIDEO:                     name = L"My Video";                    break;
+        case CSIDL_NETHOOD:                     name = L"NetHood";                     break;
+        case CSIDL_PERSONAL:                    name = L"Personal";                    break;
+        case CSIDL_PRINTHOOD:                   name = L"PrintHood";                   break;
+        case CSIDL_PROGRAMS:                    name = L"Programs";                    break;
+        case CSIDL_RECENT:                      name = L"Recent";                      break;
+        case CSIDL_SENDTO:                      name = L"SendTo";                      break;
+        case CSIDL_STARTMENU:                   name = L"Start Menu";                  break;
+        case CSIDL_STARTUP:                     name = L"Startup";                     break;
+        case CSIDL_TEMPLATES:                   name = L"Templates";                   break;
+            /* XXX L"Local Settings" */
+        }
+
+        if (!name) {
+            root = HKEY_LOCAL_MACHINE;
+            switch (folder) {
+            case CSIDL_COMMON_ADMINTOOLS:       name = L"Common Administrative Tools"; break;
+            case CSIDL_COMMON_APPDATA:          name = L"Common AppData";              break;
+            case CSIDL_COMMON_DESKTOPDIRECTORY: name = L"Common Desktop";              break;
+            case CSIDL_COMMON_DOCUMENTS:        name = L"Common Documents";            break;
+            case CSIDL_COMMON_FAVORITES:        name = L"Common Favorites";            break;
+            case CSIDL_COMMON_PROGRAMS:         name = L"Common Programs";             break;
+            case CSIDL_COMMON_STARTMENU:        name = L"Common Start Menu";           break;
+            case CSIDL_COMMON_STARTUP:          name = L"Common Startup";              break;
+            case CSIDL_COMMON_TEMPLATES:        name = L"Common Templates";            break;
+            case CSIDL_COMMON_MUSIC:            name = L"CommonMusic";                 break;
+            case CSIDL_COMMON_PICTURES:         name = L"CommonPictures";              break;
+            case CSIDL_COMMON_VIDEO:            name = L"CommonVideo";                 break;
+            }
+        }
+        /* XXX todo
+         * case CSIDL_SYSTEM               # GetSystemDirectory()
+         * case CSIDL_RESOURCES            # %windir%\Resources\, For theme and other windows resources.
+         * case CSIDL_RESOURCES_LOCALIZED  # %windir%\Resources\<LangID>, for theme and other windows specific resources.
+         */
+
+#define SHELL_FOLDERS "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
+
+        if (name && RegOpenKeyEx(root, SHELL_FOLDERS, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
+            WCHAR data[MAX_PATH+1];
+            DWORD cb = sizeof(data)-sizeof(WCHAR);
+            DWORD type = REG_NONE;
+            long rc = RegQueryValueExW(hkey, name, NULL, &type, (BYTE*)&data, &cb);
+            RegCloseKey(hkey);
+            if (rc == ERROR_SUCCESS && type == REG_SZ && cb > sizeof(WCHAR) && data[0]) {
+                /* Make sure the string is properly terminated */
+                data[cb/sizeof(WCHAR)] = '\0';
+                ST(0) = wstr_to_ansipath(aTHX_ data);
+                XSRETURN(1);
+            }
+        }
+
+#undef SHELL_FOLDERS
+
+        /* Unders some circumstances the registry entries seem to have a null string
+         * as their value even when the directory already exists.  The environment
+         * variables do get set though, so try re-create a Unicode environment and
+         * check if they are there.
+         */
+        sv = NULL;
+        switch (folder) {
+        case CSIDL_APPDATA:              sv = get_unicode_env(aTHX_ L"APPDATA");            break;
+        case CSIDL_PROFILE:              sv = get_unicode_env(aTHX_ L"USERPROFILE");        break;
+        case CSIDL_PROGRAM_FILES:        sv = get_unicode_env(aTHX_ L"ProgramFiles");       break;
+        case CSIDL_PROGRAM_FILES_COMMON: sv = get_unicode_env(aTHX_ L"CommonProgramFiles"); break;
+        case CSIDL_WINDOWS:              sv = get_unicode_env(aTHX_ L"SystemRoot");         break;
+        }
+        if (sv) {
+            ST(0) = sv;
+            XSRETURN(1);
+        }
+    }
+
+    XSRETURN_UNDEF;
+}
+
+XS(w32_GetFileVersion)
+{
+    dXSARGS;
+    DWORD size;
+    DWORD handle;
+    char *filename;
+    char *data;
+
+    if (items != 1)
+       croak("usage: Win32::GetFileVersion($filename)\n");
+
+    filename = SvPV_nolen(ST(0));
+    size = GetFileVersionInfoSize(filename, &handle);
+    if (!size)
+        XSRETURN_UNDEF;
+
+    New(0, data, size, char);
+    if (!data)
+        XSRETURN_UNDEF;
+
+    if (GetFileVersionInfo(filename, handle, size, data)) {
+        VS_FIXEDFILEINFO *info;
+        UINT len;
+        if (VerQueryValue(data, "\\", (void**)&info, &len)) {
+            int dwValueMS1 = (info->dwFileVersionMS>>16);
+            int dwValueMS2 = (info->dwFileVersionMS&0xffff);
+            int dwValueLS1 = (info->dwFileVersionLS>>16);
+            int dwValueLS2 = (info->dwFileVersionLS&0xffff);
+
+            if (GIMME_V == G_ARRAY) {
+                EXTEND(SP, 4);
+                XST_mIV(0, dwValueMS1);
+                XST_mIV(1, dwValueMS2);
+                XST_mIV(2, dwValueLS1);
+                XST_mIV(3, dwValueLS2);
+                items = 4;
+            }
+            else {
+                char version[50];
+                sprintf(version, "%d.%d.%d.%d", dwValueMS1, dwValueMS2, dwValueLS1, dwValueLS2);
+                XST_mPV(0, version);
+            }
+        }
+    }
+    else
+        items = 0;
+
+    Safefree(data);
+    XSRETURN(items);
+}
+
+#ifdef __CYGWIN__
+XS(w32_SetChildShowWindow)
+{
+    /* This function doesn't do anything useful for cygwin.  In the
+     * MSWin32 case it modifies w32_showwindow, which is used by
+     * win32_spawnvp().  Since w32_showwindow is an internal variable
+     * inside the thread_intern structure, the MSWin32 implementation
+     * lives in win32/win32.c in the core Perl distribution.
+     */
+    dXSARGS;
+    XSRETURN_UNDEF;
+}
+#endif
+
+XS(w32_GetCwd)
+{
+    dXSARGS;
+    /* Make the host for current directory */
+    char* ptr = PerlEnv_get_childdir();
+    /*
+     * If ptr != Nullch
+     *   then it worked, set PV valid,
+     *   else return 'undef'
+     */
+    if (ptr) {
+       SV *sv = sv_newmortal();
+       sv_setpv(sv, ptr);
+       PerlEnv_free_childdir(ptr);
+
+#ifndef INCOMPLETE_TAINTS
+       SvTAINTED_on(sv);
+#endif
+
+       EXTEND(SP,1);
+       ST(0) = sv;
+       XSRETURN(1);
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_SetCwd)
+{
+    dXSARGS;
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::SetCwd($cwd)");
+
+    if (IsWin2000() && SvUTF8(ST(0))) {
+        WCHAR *wide = sv_to_wstr(aTHX_ ST(0));
+        char *ansi = my_ansipath(wide);
+        int rc = PerlDir_chdir(ansi);
+        Safefree(wide);
+        Safefree(ansi);
+        if (!rc)
+            XSRETURN_YES;
+    }
+    else {
+        if (!PerlDir_chdir(SvPV_nolen(ST(0))))
+            XSRETURN_YES;
+    }
+
+    XSRETURN_NO;
+}
+
+XS(w32_GetNextAvailDrive)
+{
+    dXSARGS;
+    char ix = 'C';
+    char root[] = "_:\\";
+
+    EXTEND(SP,1);
+    while (ix <= 'Z') {
+       root[0] = ix++;
+       if (GetDriveType(root) == 1) {
+           root[2] = '\0';
+           XSRETURN_PV(root);
+       }
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_GetLastError)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetLastError());
+}
+
+XS(w32_SetLastError)
+{
+    dXSARGS;
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::SetLastError($error)");
+    SetLastError((DWORD)SvIV(ST(0)));
+    XSRETURN_EMPTY;
+}
+
+XS(w32_LoginName)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    if (IsWin2000()) {
+        WCHAR name[128];
+        DWORD size = countof(name);
+        if (GetUserNameW(name, &size)) {
+            ST(0) = wstr_to_sv(aTHX_ name);
+            XSRETURN(1);
+        }
+    }
+    else {
+        char name[128];
+        DWORD size = countof(name);
+        if (GetUserNameA(name, &size)) {
+            /* size includes NULL */
+            ST(0) = sv_2mortal(newSVpvn(name, size-1));
+            XSRETURN(1);
+        }
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_NodeName)
+{
+    dXSARGS;
+    char name[MAX_COMPUTERNAME_LENGTH+1];
+    DWORD size = sizeof(name);
+    EXTEND(SP,1);
+    if (GetComputerName(name,&size)) {
+       /* size does NOT include NULL :-( */
+       ST(0) = sv_2mortal(newSVpvn(name,size));
+       XSRETURN(1);
+    }
+    XSRETURN_UNDEF;
+}
+
+
+XS(w32_DomainName)
+{
+    dXSARGS;
+    HMODULE module = LoadLibrary("netapi32.dll");
+    PFNNetApiBufferFree pfnNetApiBufferFree;
+    PFNNetWkstaGetInfo pfnNetWkstaGetInfo;
+
+    if (module) {
+        GETPROC(NetApiBufferFree);
+        GETPROC(NetWkstaGetInfo);
+    }
+    EXTEND(SP,1);
+    if (module && pfnNetWkstaGetInfo && pfnNetApiBufferFree) {
+       /* this way is more reliable, in case user has a local account. */
+       char dname[256];
+       DWORD dnamelen = sizeof(dname);
+       struct {
+           DWORD   wki100_platform_id;
+           LPWSTR  wki100_computername;
+           LPWSTR  wki100_langroup;
+           DWORD   wki100_ver_major;
+           DWORD   wki100_ver_minor;
+       } *pwi;
+       DWORD retval;
+       retval = pfnNetWkstaGetInfo(NULL, 100, &pwi);
+       /* NERR_Success *is* 0*/
+       if (retval == 0) {
+           if (pwi->wki100_langroup && *(pwi->wki100_langroup)) {
+               WideCharToMultiByte(CP_ACP, 0, pwi->wki100_langroup,
+                                   -1, (LPSTR)dname, dnamelen, NULL, NULL);
+           }
+           else {
+               WideCharToMultiByte(CP_ACP, 0, pwi->wki100_computername,
+                                   -1, (LPSTR)dname, dnamelen, NULL, NULL);
+           }
+           pfnNetApiBufferFree(pwi);
+           FreeLibrary(module);
+           XSRETURN_PV(dname);
+       }
+       FreeLibrary(module);
+       SetLastError(retval);
+    }
+    else {
+       /* Win95 doesn't have NetWksta*(), so do it the old way */
+       char name[256];
+       DWORD size = sizeof(name);
+       if (module)
+           FreeLibrary(module);
+       if (GetUserName(name,&size)) {
+           char sid[ONE_K_BUFSIZE];
+           DWORD sidlen = sizeof(sid);
+           char dname[256];
+           DWORD dnamelen = sizeof(dname);
+           SID_NAME_USE snu;
+           if (LookupAccountName(NULL, name, (PSID)&sid, &sidlen,
+                                 dname, &dnamelen, &snu)) {
+               XSRETURN_PV(dname);             /* all that for this */
+           }
+       }
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_FsType)
+{
+    dXSARGS;
+    char fsname[256];
+    DWORD flags, filecomplen;
+    if (GetVolumeInformation(NULL, NULL, 0, NULL, &filecomplen,
+                        &flags, fsname, sizeof(fsname))) {
+       if (GIMME_V == G_ARRAY) {
+           XPUSHs(sv_2mortal(newSVpvn(fsname,strlen(fsname))));
+           XPUSHs(sv_2mortal(newSViv(flags)));
+           XPUSHs(sv_2mortal(newSViv(filecomplen)));
+           PUTBACK;
+           return;
+       }
+       EXTEND(SP,1);
+       XSRETURN_PV(fsname);
+    }
+    XSRETURN_EMPTY;
+}
+
+XS(w32_GetOSVersion)
+{
+    dXSARGS;
+
+    if (GIMME_V == G_SCALAR) {
+        XSRETURN_IV(g_osver.dwPlatformId);
+    }
+    XPUSHs(sv_2mortal(newSVpvn(g_osver.szCSDVersion, strlen(g_osver.szCSDVersion))));
+
+    XPUSHs(sv_2mortal(newSViv(g_osver.dwMajorVersion)));
+    XPUSHs(sv_2mortal(newSViv(g_osver.dwMinorVersion)));
+    XPUSHs(sv_2mortal(newSViv(g_osver.dwBuildNumber)));
+    XPUSHs(sv_2mortal(newSViv(g_osver.dwPlatformId)));
+    if (g_osver_ex) {
+        XPUSHs(sv_2mortal(newSViv(g_osver.wServicePackMajor)));
+        XPUSHs(sv_2mortal(newSViv(g_osver.wServicePackMinor)));
+        XPUSHs(sv_2mortal(newSViv(g_osver.wSuiteMask)));
+        XPUSHs(sv_2mortal(newSViv(g_osver.wProductType)));
+    }
+    PUTBACK;
+}
+
+XS(w32_IsWinNT)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(IsWinNT());
+}
+
+XS(w32_IsWin95)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(IsWin95());
+}
+
+XS(w32_FormatMessage)
+{
+    dXSARGS;
+    DWORD source = 0;
+    char msgbuf[ONE_K_BUFSIZE];
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");
+
+    if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
+                       &source, (DWORD)SvIV(ST(0)), 0,
+                       msgbuf, sizeof(msgbuf)-1, NULL))
+    {
+        XSRETURN_PV(msgbuf);
+    }
+
+    XSRETURN_UNDEF;
+}
+
+XS(w32_Spawn)
+{
+    dXSARGS;
+    char *cmd, *args;
+    void *env;
+    char *dir;
+    PROCESS_INFORMATION stProcInfo;
+    STARTUPINFO stStartInfo;
+    BOOL bSuccess = FALSE;
+
+    if (items != 3)
+       Perl_croak(aTHX_ "usage: Win32::Spawn($cmdName, $args, $PID)");
+
+    cmd = SvPV_nolen(ST(0));
+    args = SvPV_nolen(ST(1));
+
+    env = PerlEnv_get_childenv();
+    dir = PerlEnv_get_childdir();
+
+    memset(&stStartInfo, 0, sizeof(stStartInfo));   /* Clear the block */
+    stStartInfo.cb = sizeof(stStartInfo);          /* Set the structure size */
+    stStartInfo.dwFlags = STARTF_USESHOWWINDOW;            /* Enable wShowWindow control */
+    stStartInfo.wShowWindow = SW_SHOWMINNOACTIVE;   /* Start min (normal) */
+
+    if (CreateProcess(
+               cmd,                    /* Image path */
+               args,                   /* Arguments for command line */
+               NULL,                   /* Default process security */
+               NULL,                   /* Default thread security */
+               FALSE,                  /* Must be TRUE to use std handles */
+               NORMAL_PRIORITY_CLASS,  /* No special scheduling */
+               env,                    /* Inherit our environment block */
+               dir,                    /* Inherit our currrent directory */
+               &stStartInfo,           /* -> Startup info */
+               &stProcInfo))           /* <- Process info (if OK) */
+    {
+       int pid = (int)stProcInfo.dwProcessId;
+       if (IsWin95() && pid < 0)
+           pid = -pid;
+       sv_setiv(ST(2), pid);
+       CloseHandle(stProcInfo.hThread);/* library source code does this. */
+       bSuccess = TRUE;
+    }
+    PerlEnv_free_childenv(env);
+    PerlEnv_free_childdir(dir);
+    XSRETURN_IV(bSuccess);
+}
+
+XS(w32_GetTickCount)
+{
+    dXSARGS;
+    DWORD msec = GetTickCount();
+    EXTEND(SP,1);
+    if ((IV)msec > 0)
+       XSRETURN_IV(msec);
+    XSRETURN_NV(msec);
+}
+
+XS(w32_GetShortPathName)
+{
+    dXSARGS;
+    SV *shortpath;
+    DWORD len;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::GetShortPathName($longPathName)");
+
+    if (IsWin2000()) {
+        WCHAR wshort[MAX_PATH+1];
+        WCHAR *wlong = sv_to_wstr(aTHX_ ST(0));
+        len = GetShortPathNameW(wlong, wshort, countof(wshort));
+        Safefree(wlong);
+        if (len && len < sizeof(wshort)) {
+            ST(0) = wstr_to_sv(aTHX_ wshort);
+            XSRETURN(1);
+        }
+        XSRETURN_UNDEF;
+    }
+
+    shortpath = sv_mortalcopy(ST(0));
+    SvUPGRADE(shortpath, SVt_PV);
+    if (!SvPVX(shortpath) || !SvLEN(shortpath))
+        XSRETURN_UNDEF;
+
+    /* src == target is allowed */
+    do {
+       len = GetShortPathName(SvPVX(shortpath),
+                              SvPVX(shortpath),
+                              (DWORD)SvLEN(shortpath));
+    } while (len >= SvLEN(shortpath) && sv_grow(shortpath,len+1));
+    if (len) {
+       SvCUR_set(shortpath,len);
+       *SvEND(shortpath) = '\0';
+       ST(0) = shortpath;
+       XSRETURN(1);
+    }
+    XSRETURN_UNDEF;
+}
+
+XS(w32_GetFullPathName)
+{
+    dXSARGS;
+    char *fullname;
+    char *ansi = NULL;
+
+/* The code below relies on the fact that PerlDir_mapX() returns an
+ * absolute path, which is only true under PERL_IMPLICIT_SYS when
+ * we use the virtualization code from win32/vdir.h.
+ * Without it PerlDir_mapX() is a no-op and we need to use the same
+ * code as we use for Cygwin.
+ */
+#if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
+    char buffer[2*MAX_PATH];
+#endif
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::GetFullPathName($filename)");
+
+#if __CYGWIN__ || !defined(PERL_IMPLICIT_SYS)
+    if (IsWin2000()) {
+        WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
+        WCHAR full[2*MAX_PATH];
+        DWORD len = GetFullPathNameW(filename, countof(full), full, NULL);
+        Safefree(filename);
+        if (len == 0 || len >= countof(full))
+            XSRETURN_EMPTY;
+        ansi = fullname = my_ansipath(full);
+    }
+    else {
+        DWORD len = GetFullPathNameA(SvPV_nolen(ST(0)), countof(buffer), buffer, NULL);
+        if (len == 0 || len >= countof(buffer))
+            XSRETURN_EMPTY;
+        fullname = buffer;
+    }
+#else
+    /* Don't use my_ansipath() unless the $filename argument is in Unicode.
+     * If the relative path doesn't exist, GetShortPathName() will fail and
+     * my_ansipath() will use the long name with replacement characters.
+     * In that case we will be better off using PerlDir_mapA(), which
+     * already uses the ANSI name of the current directory.
+     *
+     * XXX The one missing case is where we could downgrade $filename
+     * XXX from UTF8 into the current codepage.
+     */
+    if (IsWin2000() && SvUTF8(ST(0))) {
+        WCHAR *filename = sv_to_wstr(aTHX_ ST(0));
+        WCHAR *mappedname = PerlDir_mapW(filename);
+        Safefree(filename);
+        ansi = fullname = my_ansipath(mappedname);
+    }
+    else {
+        fullname = PerlDir_mapA(SvPV_nolen(ST(0)));
+    }
+#  if PERL_VERSION < 8
+    {
+        /* PerlDir_mapX() in Perl 5.6 used to return forward slashes */
+        char *str = fullname;
+        while (*str) {
+            if (*str == '/')
+                *str = '\\';
+            ++str;
+        }
+    }
+#  endif
+#endif
+
+    /* GetFullPathName() on Windows NT drops trailing backslash */
+    if (g_osver.dwMajorVersion == 4 && *fullname) {
+        STRLEN len;
+        char *pv = SvPV(ST(0), len);
+        char *lastchar = fullname + strlen(fullname) - 1;
+        /* If ST(0) ends with a slash, but fullname doesn't ... */
+        if (len && (pv[len-1] == '/' || pv[len-1] == '\\') && *lastchar != '\\') {
+            /* fullname is the MAX_PATH+1 sized buffer returned from PerlDir_mapA()
+             * or the 2*MAX_PATH sized local buffer in the __CYGWIN__ case.
+             */
+            if (lastchar - fullname < MAX_PATH - 1)
+                strcpy(lastchar+1, "\\");
+        }
+    }
+
+    if (GIMME_V == G_ARRAY) {
+        char *filepart = strrchr(fullname, '\\');
+
+        EXTEND(SP,1);
+        if (filepart) {
+            XST_mPV(1, ++filepart);
+            *filepart = '\0';
+        }
+        else {
+            XST_mPVN(1, "", 0);
+        }
+        items = 2;
+    }
+    XST_mPV(0, fullname);
+
+    if (ansi)
+        Safefree(ansi);
+    XSRETURN(items);
+}
+
+XS(w32_GetLongPathName)
+{
+    dXSARGS;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::GetLongPathName($pathname)");
+
+    if (IsWin2000()) {
+        WCHAR *wstr = sv_to_wstr(aTHX_ ST(0));
+        WCHAR wide_path[MAX_PATH+1];
+        WCHAR *long_path;
+
+        if (wcslen(wstr) < countof(wide_path)) {
+            wcscpy(wide_path, wstr);
+            long_path = my_longpathW(wide_path);
+            if (long_path) {
+                Safefree(wstr);
+                ST(0) = wstr_to_sv(aTHX_ long_path);
+                XSRETURN(1);
+            }
+        }
+        Safefree(wstr);
+    }
+    else {
+        SV *path;
+        char tmpbuf[MAX_PATH+1];
+        char *pathstr;
+        STRLEN len;
+
+        path = ST(0);
+        pathstr = SvPV(path,len);
+        if (len < sizeof(tmpbuf)) {
+            strcpy(tmpbuf, pathstr);
+            pathstr = my_longpathA(tmpbuf);
+            if (pathstr) {
+                ST(0) = sv_2mortal(newSVpvn(pathstr, strlen(pathstr)));
+                XSRETURN(1);
+            }
+        }
+    }
+    XSRETURN_EMPTY;
+}
+
+XS(w32_GetANSIPathName)
+{
+    dXSARGS;
+    WCHAR *wide_path;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::GetANSIPathName($pathname)");
+
+    wide_path = sv_to_wstr(aTHX_ ST(0));
+    ST(0) = wstr_to_ansipath(aTHX_ wide_path);
+    Safefree(wide_path);
+    XSRETURN(1);
+}
+
+XS(w32_Sleep)
+{
+    dXSARGS;
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::Sleep($milliseconds)");
+    Sleep((DWORD)SvIV(ST(0)));
+    XSRETURN_YES;
+}
+
+XS(w32_CopyFile)
+{
+    dXSARGS;
+    BOOL bResult;
+    char *pszSourceFile;
+    char szSourceFile[MAX_PATH+1];
+
+    if (items != 3)
+       Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
+
+    pszSourceFile = PerlDir_mapA(SvPV_nolen(ST(0)));
+    if (strlen(pszSourceFile) < sizeof(szSourceFile)) {
+        strcpy(szSourceFile, pszSourceFile);
+        bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));
+        if (bResult)
+            XSRETURN_YES;
+    }
+    XSRETURN_NO;
+}
+
+XS(w32_OutputDebugString)
+{
+    dXSARGS;
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::OutputDebugString($string)");
+
+    if (SvUTF8(ST(0))) {
+        WCHAR *str = sv_to_wstr(aTHX_ ST(0));
+        OutputDebugStringW(str);
+        Safefree(str);
+    }
+    else
+        OutputDebugStringA(SvPV_nolen(ST(0)));
+
+    XSRETURN_EMPTY;
+}
+
+XS(w32_GetCurrentProcessId)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetCurrentProcessId());
+}
+
+XS(w32_GetCurrentThreadId)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetCurrentThreadId());
+}
+
+XS(w32_CreateDirectory)
+{
+    dXSARGS;
+    BOOL result;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::CreateDirectory($dir)");
+
+    if (IsWin2000() && SvUTF8(ST(0))) {
+        WCHAR *dir = sv_to_wstr(aTHX_ ST(0));
+        result = CreateDirectoryW(dir, NULL);
+        Safefree(dir);
+    }
+    else {
+        result = CreateDirectoryA(SvPV_nolen(ST(0)), NULL);
+    }
+
+    ST(0) = boolSV(result);
+    XSRETURN(1);
+}
+
+XS(w32_CreateFile)
+{
+    dXSARGS;
+    HANDLE handle;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::CreateFile($file)");
+
+    if (IsWin2000() && SvUTF8(ST(0))) {
+        WCHAR *file = sv_to_wstr(aTHX_ ST(0));
+        handle = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_WRITE,
+                             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+        Safefree(file);
+    }
+    else {
+        handle = CreateFileA(SvPV_nolen(ST(0)), GENERIC_WRITE, FILE_SHARE_WRITE,
+                             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+    }
+
+    if (handle != INVALID_HANDLE_VALUE)
+        CloseHandle(handle);
+
+    ST(0) = boolSV(handle != INVALID_HANDLE_VALUE);
+    XSRETURN(1);
+}
+
+XS(w32_GetSystemMetrics)
+{
+    dXSARGS;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::GetSystemMetrics($index)");
+
+    XSRETURN_IV(GetSystemMetrics((int)SvIV(ST(0))));
+}
+
+XS(w32_GetProductInfo)
+{
+    dXSARGS;
+    DWORD type;
+    HMODULE module;
+    PFNGetProductInfo pfnGetProductInfo;
+
+    if (items != 4)
+       Perl_croak(aTHX_ "usage: Win32::GetProductInfo($major,$minor,$spmajor,$spminor)");
+
+    module = GetModuleHandle("kernel32.dll");
+    GETPROC(GetProductInfo);
+    if (pfnGetProductInfo &&
+        pfnGetProductInfo((DWORD)SvIV(ST(0)), (DWORD)SvIV(ST(1)),
+                          (DWORD)SvIV(ST(2)), (DWORD)SvIV(ST(3)), &type))
+    {
+        XSRETURN_IV(type);
+    }
+
+    /* PRODUCT_UNDEFINED */
+    XSRETURN_IV(0);
+}
+
+XS(w32_GetACP)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetACP());
+}
+
+XS(w32_GetConsoleCP)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetConsoleCP());
+}
+
+XS(w32_GetConsoleOutputCP)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetConsoleOutputCP());
+}
+
+XS(w32_GetOEMCP)
+{
+    dXSARGS;
+    EXTEND(SP,1);
+    XSRETURN_IV(GetOEMCP());
+}
+
+XS(w32_SetConsoleCP)
+{
+    dXSARGS;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::SetConsoleCP($id)");
+
+    XSRETURN_IV(SetConsoleCP((int)SvIV(ST(0))));
+}
+
+XS(w32_SetConsoleOutputCP)
+{
+    dXSARGS;
+
+    if (items != 1)
+       Perl_croak(aTHX_ "usage: Win32::SetConsoleOutputCP($id)");
+
+    XSRETURN_IV(SetConsoleOutputCP((int)SvIV(ST(0))));
+}
+
+MODULE = Win32            PACKAGE = Win32
+
+PROTOTYPES: DISABLE
+
+BOOT:
+{
+    char *file = __FILE__;
+
+    if (g_osver.dwOSVersionInfoSize == 0) {
+        g_osver.dwOSVersionInfoSize = sizeof(g_osver);
+        if (!GetVersionExA((OSVERSIONINFOA*)&g_osver)) {
+            g_osver_ex = FALSE;
+            g_osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+            GetVersionExA((OSVERSIONINFOA*)&g_osver);
+        }
+    }
+
+    newXS("Win32::LookupAccountName", w32_LookupAccountName, file);
+    newXS("Win32::LookupAccountSID", w32_LookupAccountSID, file);
+    newXS("Win32::InitiateSystemShutdown", w32_InitiateSystemShutdown, file);
+    newXS("Win32::AbortSystemShutdown", w32_AbortSystemShutdown, file);
+    newXS("Win32::ExpandEnvironmentStrings", w32_ExpandEnvironmentStrings, file);
+    newXS("Win32::MsgBox", w32_MsgBox, file);
+    newXS("Win32::LoadLibrary", w32_LoadLibrary, file);
+    newXS("Win32::FreeLibrary", w32_FreeLibrary, file);
+    newXS("Win32::GetProcAddress", w32_GetProcAddress, file);
+    newXS("Win32::RegisterServer", w32_RegisterServer, file);
+    newXS("Win32::UnregisterServer", w32_UnregisterServer, file);
+    newXS("Win32::GetArchName", w32_GetArchName, file);
+    newXS("Win32::GetChipName", w32_GetChipName, file);
+    newXS("Win32::GuidGen", w32_GuidGen, file);
+    newXS("Win32::GetFolderPath", w32_GetFolderPath, file);
+    newXS("Win32::IsAdminUser", w32_IsAdminUser, file);
+    newXS("Win32::GetFileVersion", w32_GetFileVersion, file);
+
+    newXS("Win32::GetCwd", w32_GetCwd, file);
+    newXS("Win32::SetCwd", w32_SetCwd, file);
+    newXS("Win32::GetNextAvailDrive", w32_GetNextAvailDrive, file);
+    newXS("Win32::GetLastError", w32_GetLastError, file);
+    newXS("Win32::SetLastError", w32_SetLastError, file);
+    newXS("Win32::LoginName", w32_LoginName, file);
+    newXS("Win32::NodeName", w32_NodeName, file);
+    newXS("Win32::DomainName", w32_DomainName, file);
+    newXS("Win32::FsType", w32_FsType, file);
+    newXS("Win32::GetOSVersion", w32_GetOSVersion, file);
+    newXS("Win32::IsWinNT", w32_IsWinNT, file);
+    newXS("Win32::IsWin95", w32_IsWin95, file);
+    newXS("Win32::FormatMessage", w32_FormatMessage, file);
+    newXS("Win32::Spawn", w32_Spawn, file);
+    newXS("Win32::GetTickCount", w32_GetTickCount, file);
+    newXS("Win32::GetShortPathName", w32_GetShortPathName, file);
+    newXS("Win32::GetFullPathName", w32_GetFullPathName, file);
+    newXS("Win32::GetLongPathName", w32_GetLongPathName, file);
+    newXS("Win32::GetANSIPathName", w32_GetANSIPathName, file);
+    newXS("Win32::CopyFile", w32_CopyFile, file);
+    newXS("Win32::Sleep", w32_Sleep, file);
+    newXS("Win32::OutputDebugString", w32_OutputDebugString, file);
+    newXS("Win32::GetCurrentProcessId", w32_GetCurrentProcessId, file);
+    newXS("Win32::GetCurrentThreadId", w32_GetCurrentThreadId, file);
+    newXS("Win32::CreateDirectory", w32_CreateDirectory, file);
+    newXS("Win32::CreateFile", w32_CreateFile, file);
+    newXS("Win32::GetSystemMetrics", w32_GetSystemMetrics, file);
+    newXS("Win32::GetProductInfo", w32_GetProductInfo, file);
+    newXS("Win32::GetACP", w32_GetACP, file);
+    newXS("Win32::GetConsoleCP", w32_GetConsoleCP, file);
+    newXS("Win32::GetConsoleOutputCP", w32_GetConsoleOutputCP, file);
+    newXS("Win32::GetOEMCP", w32_GetOEMCP, file);
+    newXS("Win32::SetConsoleCP", w32_SetConsoleCP, file);
+    newXS("Win32::SetConsoleOutputCP", w32_SetConsoleOutputCP, file);
+#ifdef __CYGWIN__
+    newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
+#endif
+    XSRETURN_YES;
+}