X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=win32%2Fwin32.c;h=d459c94c09400344b3e3e2f4cbd6cc6258438250;hb=d2aeed1648166d254ac68525c35b77dec4ba8772;hp=3d00bb895ec812d707ed189d5b2c038e112f6385;hpb=35cf1ab6c6ff4f898ef39b9c22313127bcebffc4;p=p5sagit%2Fp5-mst-13.2.git diff --git a/win32/win32.c b/win32/win32.c index 3d00bb8..d459c94 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -19,12 +19,37 @@ # define HWND_MESSAGE ((HWND)-3) #endif #ifndef WC_NO_BEST_FIT_CHARS -# define WC_NO_BEST_FIT_CHARS 0x00000400 +# define WC_NO_BEST_FIT_CHARS 0x00000400 /* requires Windows 2000 or later */ #endif #include +#include #include #include +#define SystemProcessesAndThreadsInformation 5 + +/* Inline some definitions from the DDK */ +typedef struct { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; + +typedef struct { + ULONG NextEntryDelta; + ULONG ThreadCount; + ULONG Reserved1[6]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ProcessName; + LONG BasePriority; + ULONG ProcessId; + ULONG InheritedFromProcessId; + /* Remainder of the structure depends on the Windows version, + * but we don't need those additional fields anyways... */ +} SYSTEM_PROCESSES; + /* #include "config.h" */ #if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO) @@ -35,13 +60,6 @@ #include "EXTERN.h" #include "perl.h" -/* GCC-2.95.2/Mingw32-1.1 forgot the WINAPI on CommandLineToArgvW() */ -#if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1) -# include -#else -EXTERN_C LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs); -#endif - #define NO_XSLOCKS #define PERL_NO_GET_CONTEXT #include "XSUB.h" @@ -124,6 +142,11 @@ END_EXTERN_C static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""}; +static HANDLE (WINAPI *pfnCreateToolhelp32Snapshot)(DWORD, DWORD) = NULL; +static BOOL (WINAPI *pfnProcess32First)(HANDLE, PROCESSENTRY32*) = NULL; +static BOOL (WINAPI *pfnProcess32Next)(HANDLE, PROCESSENTRY32*) = NULL; +static LONG (WINAPI *pfnZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG); + #ifdef __BORLANDC__ /* Silence STDERR grumblings from Borland's math library. */ DllExport int @@ -134,7 +157,22 @@ _matherr(struct _exception *a) } #endif -#if _MSC_VER >= 1400 +/* VS2005 (MSC version 14) provides a mechanism to set an invalid + * parameter handler. This functionality is not available in the + * 64-bit compiler from the Platform SDK, which unfortunately also + * believes itself to be MSC version 14. + * + * There is no #define related to _set_invalid_parameter_handler(), + * but we can check for one of the constants defined for + * _set_abort_behavior(), which was introduced into stdlib.h at + * the same time. + */ + +#if _MSC_VER >= 1400 && defined(_WRITE_ABORT_MSG) +# define SET_INVALID_PARAMETER_HANDLER +#endif + +#ifdef SET_INVALID_PARAMETER_HANDLER void my_invalid_parameter_handler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, @@ -161,24 +199,57 @@ IsWinNT(void) return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT); } +int +IsWin2000(void) +{ + return (g_osver.dwMajorVersion > 4); +} + EXTERN_C void set_w32_module_name(void) { + /* this function may be called at DLL_PROCESS_ATTACH time */ char* ptr; - GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE) - ? GetModuleHandle(NULL) - : w32_perldll_handle), - w32_module_name, sizeof(w32_module_name)); + HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE) + ? GetModuleHandle(NULL) + : w32_perldll_handle); + + OSVERSIONINFO osver; /* g_osver may not yet be initialized */ + osver.dwOSVersionInfoSize = sizeof(osver); + GetVersionEx(&osver); + + if (osver.dwMajorVersion > 4) { + WCHAR modulename[MAX_PATH]; + WCHAR fullname[MAX_PATH]; + char *ansi; + + GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR)); - /* remove \\?\ prefix */ - if (memcmp(w32_module_name, "\\\\?\\", 4) == 0) - memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1); + /* Make sure we get an absolute pathname in case the module was loaded + * explicitly by LoadLibrary() with a relative path. */ + GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL); - /* try to get full path to binary (which may be mangled when perl is - * run from a 16-bit app) */ - /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/ - (void)win32_longpath(w32_module_name); - /*PerlIO_printf(Perl_debug_log, "After %s\n", w32_module_name);*/ + /* remove \\?\ prefix */ + if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0) + memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR)); + + ansi = win32_ansipath(fullname); + my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name)); + win32_free(ansi); + } + else { + GetModuleFileName(module, w32_module_name, sizeof(w32_module_name)); + + /* remove \\?\ prefix */ + if (memcmp(w32_module_name, "\\\\?\\", 4) == 0) + memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1); + + /* try to get full path to binary (which may be mangled when perl is + * run from a 16-bit app) */ + /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/ + win32_longpath(w32_module_name); + /*PerlIO_printf(Perl_debug_log, "After %s\n", w32_module_name);*/ + } /* normalize to forward slashes */ ptr = w32_module_name; @@ -197,7 +268,7 @@ get_regstr_from(HKEY hkey, const char *valuename, SV **svp) HKEY handle; DWORD type; const char *subkey = "Software\\Perl"; - char *str = Nullch; + char *str = NULL; long retval; retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle); @@ -295,7 +366,7 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...) return SvPVX(*prev_pathp); } - return Nullch; + return NULL; } char * @@ -304,7 +375,7 @@ win32_get_privlib(const char *pl) dTHX; char *stdlib = "lib"; char buffer[MAX_PATH+1]; - SV *sv = Nullsv; + SV *sv = NULL; /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || ""; */ sprintf(buffer, "%s-%s", stdlib, pl); @@ -312,7 +383,7 @@ win32_get_privlib(const char *pl) (void)get_regstr(stdlib, &sv); /* $stdlib .= ";$EMD/../../lib" */ - return get_emd_part(&sv, stdlib, ARCHNAME, "bin", Nullch); + return get_emd_part(&sv, stdlib, ARCHNAME, "bin", NULL); } static char * @@ -321,8 +392,8 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname) dTHX; char regstr[40]; char pathstr[MAX_PATH+1]; - SV *sv1 = Nullsv; - SV *sv2 = Nullsv; + SV *sv1 = NULL; + SV *sv2 = NULL; /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */ sprintf(regstr, "%s-%s", xlib, pl); @@ -331,7 +402,7 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname) /* $xlib .= * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib"; */ sprintf(pathstr, "%s/%s/lib", libname, pl); - (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch); + (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, NULL); /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */ (void)get_regstr(xlib, &sv2); @@ -339,10 +410,10 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname) /* $xlib .= * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib"; */ sprintf(pathstr, "%s/lib", libname); - (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, Nullch); + (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, NULL); if (!sv1 && !sv2) - return Nullch; + return NULL; if (!sv1) return SvPVX(sv2); if (!sv2) @@ -478,7 +549,7 @@ win32_getpid(void) static long tokenize(const char *str, char **dest, char ***destv) { - char *retstart = Nullch; + char *retstart = NULL; char **retvstart = 0; int items = -1; if (str) { @@ -513,7 +584,7 @@ tokenize(const char *str, char **dest, char ***destv) ++items; ret++; } - retvstart[items] = Nullch; + retvstart[items] = NULL; *ret++ = '\0'; *ret = '\0'; } @@ -657,7 +728,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype) if (*s) *s++ = '\0'; } - *a = Nullch; + *a = NULL; if (argv[0]) { switch (exectype) { case EXECF_SPAWN: @@ -686,7 +757,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype) while (++i < w32_perlshell_items) argv[i] = w32_perlshell_vec[i]; argv[i++] = (char *)cmd; - argv[i] = Nullch; + argv[i] = NULL; switch (exectype) { case EXECF_SPAWN: status = win32_spawnvp(P_WAIT, argv[0], @@ -786,7 +857,7 @@ win32_opendir(const char *filename) scanname[len] = '\0'; /* do the FindFirstFile call */ - if (IsWinNT()) { + if (IsWin2000()) { WCHAR wscanname[sizeof(scanname)]; MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR)); dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData); @@ -877,7 +948,7 @@ win32_readdir(DIR *dirp) /* finding the next file that matches the wildcard * (which should be all of them in this directory!). */ - if (IsWinNT()) { + if (IsWin2000()) { WIN32_FIND_DATAW wFindData; res = FindNextFileW(dirp->handle, &wFindData); if (res) { @@ -1107,20 +1178,155 @@ remove_dead_pseudo_process(long child) } #endif +static int +terminate_process(DWORD pid, HANDLE process_handle, int sig) +{ + switch(sig) { + case 0: + /* "Does process exist?" use of kill */ + return 1; + case 2: + if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid)) + return 1; + break; + case SIGBREAK: + case SIGTERM: + if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid)) + return 1; + break; + default: /* For now be backwards compatible with perl 5.6 */ + case 9: + /* Note that we will only be able to kill processes owned by the + * current process owner, even when we are running as an administrator. + * To kill processes of other owners we would need to set the + * 'SeDebugPrivilege' privilege before obtaining the process handle. + */ + if (TerminateProcess(process_handle, sig)) + return 1; + break; + } + return 0; +} + +/* Traverse process tree using ToolHelp functions */ +static int +kill_process_tree_toolhelp(DWORD pid, int sig) +{ + HANDLE process_handle; + HANDLE snapshot_handle; + int killed = 0; + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (process_handle == NULL) + return 0; + + killed += terminate_process(pid, process_handle, sig); + + snapshot_handle = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot_handle != INVALID_HANDLE_VALUE) { + PROCESSENTRY32 entry; + + entry.dwSize = sizeof(entry); + if (pfnProcess32First(snapshot_handle, &entry)) { + do { + if (entry.th32ParentProcessID == pid) + killed += kill_process_tree_toolhelp(entry.th32ProcessID, sig); + entry.dwSize = sizeof(entry); + } + while (pfnProcess32Next(snapshot_handle, &entry)); + } + CloseHandle(snapshot_handle); + } + CloseHandle(process_handle); + return killed; +} + +/* Traverse process tree using undocumented system information structures. + * This is only necessary on Windows NT, which lacks the ToolHelp functions. + */ +static int +kill_process_tree_sysinfo(SYSTEM_PROCESSES *process_info, DWORD pid, int sig) +{ + HANDLE process_handle; + SYSTEM_PROCESSES *p = process_info; + int killed = 0; + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (process_handle == NULL) + return 0; + + killed += terminate_process(pid, process_handle, sig); + + while (1) { + if (p->InheritedFromProcessId == (DWORD)pid) + killed += kill_process_tree_sysinfo(process_info, p->ProcessId, sig); + + if (p->NextEntryDelta == 0) + break; + + p = (SYSTEM_PROCESSES*)((char*)p + p->NextEntryDelta); + } + + CloseHandle(process_handle); + return killed; +} + +int +killpg(int pid, int sig) +{ + /* Use "documented" method whenever available */ + if (pfnCreateToolhelp32Snapshot && pfnProcess32First && pfnProcess32Next) { + return kill_process_tree_toolhelp((DWORD)pid, sig); + } + + /* Fall back to undocumented Windows internals on Windows NT */ + if (pfnZwQuerySystemInformation) { + dTHX; + char *buffer; + DWORD size = 0; + + pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &size); + Newx(buffer, size, char); + + if (pfnZwQuerySystemInformation(SystemProcessesAndThreadsInformation, buffer, size, NULL) >= 0) { + int killed = kill_process_tree_sysinfo((SYSTEM_PROCESSES*)buffer, (DWORD)pid, sig); + Safefree(buffer); + return killed; + } + } + return 0; +} + +static int +my_kill(int pid, int sig) +{ + int retval = 0; + HANDLE process_handle; + + if (sig < 0) + return killpg(pid, -sig); + + process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */ + if (process_handle != NULL) { + retval = terminate_process(pid, process_handle, sig); + CloseHandle(process_handle); + } + return retval; +} + DllExport int win32_kill(int pid, int sig) { dTHX; - HANDLE hProcess; long child; - int retval; #ifdef USE_ITHREADS if (pid < 0) { /* it is a pseudo-forked child */ child = find_pseudo_pid(-pid); if (child >= 0) { HWND hwnd = w32_pseudo_child_message_hwnds[child]; - hProcess = w32_pseudo_child_handles[child]; + HANDLE hProcess = w32_pseudo_child_handles[child]; switch (sig) { case 0: /* "Does process exist?" use of kill */ @@ -1141,6 +1347,7 @@ win32_kill(int pid, int sig) /* Yield and wait for the other thread to send us its message_hwnd */ Sleep(0); win32_async_check(aTHX); + hwnd = w32_pseudo_child_message_hwnds[child]; ++count; } if (hwnd != INVALID_HANDLE_VALUE) { @@ -1168,58 +1375,19 @@ win32_kill(int pid, int sig) { child = find_pid(pid); if (child >= 0) { - hProcess = w32_child_handles[child]; - switch(sig) { - case 0: - /* "Does process exist?" use of kill */ - return 0; - case 2: - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid)) - return 0; - break; - case SIGBREAK: - case SIGTERM: - if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,pid)) - return 0; - break; - default: /* For now be backwards compatible with perl5.6 */ - case 9: - if (TerminateProcess(hProcess, sig)) { - remove_dead_process(child); - return 0; - } - break; + if (my_kill(pid, sig)) { + DWORD exitcode = 0; + if (GetExitCodeProcess(w32_child_handles[child], &exitcode) && + exitcode != STILL_ACTIVE) + { + remove_dead_process(child); + } + return 0; } } else { alien_process: - retval = -1; - hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, - (IsWin95() ? -pid : pid)); - if (hProcess) { - switch(sig) { - case 0: - /* "Does process exist?" use of kill */ - retval = 0; - break; - case 2: - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid)) - retval = 0; - break; - case SIGBREAK: - case SIGTERM: - if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,pid)) - retval = 0; - break; - default: /* For now be backwards compatible with perl5.6 */ - case 9: - if (TerminateProcess(hProcess, sig)) - retval = 0; - break; - } - } - CloseHandle(hProcess); - if (retval == 0) + if (my_kill((IsWin95() ? -pid : pid), sig)) return 0; } } @@ -1382,7 +1550,7 @@ win32_longpath(char *path) char *start = path; char sep; if (!path) - return Nullch; + return NULL; /* drive prefix */ if (isALPHA(path[0]) && path[1] == ':') { @@ -1446,26 +1614,91 @@ win32_longpath(char *path) else { FindClose(fhand); errno = ERANGE; - return Nullch; + return NULL; } } else { /* failed a step, just return without side effects */ /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/ errno = EINVAL; - return Nullch; + return NULL; } } strcpy(path,tmpbuf); return path; } +static void +out_of_memory() +{ + if (PL_curinterp) { + dTHX; + /* Can't use PerlIO to write as it allocates memory */ + PerlLIO_write(PerlIO_fileno(Perl_error_log), + PL_no_mem, strlen(PL_no_mem)); + my_exit(1); + } + exit(1); +} + +/* The win32_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 win32_free() when it + * it no longer needed. + * + * The argument to win32_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! + */ +DllExport char * +win32_ansipath(const WCHAR *widename) +{ + char *name; + BOOL use_default = FALSE; + size_t widelen = wcslen(widename)+1; + int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen, + NULL, 0, NULL, NULL); + name = win32_malloc(len); + if (!name) + out_of_memory(); + + 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 = win32_malloc(shortlen*sizeof(WCHAR)); + if (!shortname) + out_of_memory(); + shortlen = GetShortPathNameW(widename, shortname, shortlen)+1; + + len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen, + NULL, 0, NULL, NULL); + name = win32_realloc(name, len); + if (!name) + out_of_memory(); + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen, + name, len, NULL, NULL); + win32_free(shortname); + } + } + return name; +} + DllExport char * win32_getenv(const char *name) { dTHX; DWORD needlen; - SV *curitem = Nullsv; + SV *curitem = NULL; needlen = GetEnvironmentVariableA(name,NULL,0); if (needlen != 0) { @@ -1486,7 +1719,7 @@ win32_getenv(const char *name) if (curitem && SvCUR(curitem)) return SvPVX(curitem); - return Nullch; + return NULL; } DllExport int @@ -1926,7 +2159,7 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result timeout += ticks; } while (1) { - DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_ALLEVENTS); + DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_POSTMESSAGE|QS_TIMER); if (resultp) *resultp = result; if (result == WAIT_TIMEOUT) { @@ -2157,7 +2390,7 @@ win32_crypt(const char *txt, const char *salt) return des_fcrypt(txt, salt, w32_crypt_buffer); #else Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia."); - return Nullch; + return NULL; #endif } @@ -3036,7 +3269,7 @@ win32_rename(const char *oname, const char *newname) int retval = 0; char szTmpName[MAX_PATH+1]; char dname[MAX_PATH+1]; - char *endname = Nullch; + char *endname = NULL; STRLEN tmplen = 0; DWORD from_attr, to_attr; @@ -3095,7 +3328,7 @@ win32_rename(const char *oname, const char *newname) retval = rename(szOldName, szNewName); /* if we created a temporary file before ... */ - if (endname != Nullch) { + if (endname != NULL) { /* ...and rename succeeded, delete temporary file/directory */ if (retval == 0) DeleteFile(szTmpName); @@ -3677,7 +3910,7 @@ qualified_path(const char *cmd) int has_slash = 0; if (!cmd) - return Nullch; + return NULL; fullcmd = (char*)cmd; while (*fullcmd) { if (*fullcmd == '/' || *fullcmd == '\\') @@ -3751,7 +3984,7 @@ qualified_path(const char *cmd) } Safefree(fullcmd); - return Nullch; + return NULL; } /* The following are just place holders. @@ -3838,7 +4071,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv) PROCESS_INFORMATION ProcessInformation; DWORD create = 0; char *cmd; - char *fullcmd = Nullch; + char *fullcmd = NULL; char *cname = (char *)cmdname; STRLEN clen = 0; @@ -4344,73 +4577,23 @@ XS(w32_SetChildShowWindow) XSRETURN(1); } -static void -forward(pTHX_ const char *function) -{ - dXSARGS; - Perl_load_module(aTHX_ PERL_LOADMOD_NOIMPORT, newSVpvn("Win32",5), newSVnv(0.27)); - SPAGAIN; - PUSHMARK(SP-items); - call_pv(function, GIMME_V); -} - -#define FORWARD(function) XS(w32_##function){ forward(aTHX_ "Win32::"#function); } -FORWARD(GetCwd) -FORWARD(SetCwd) -FORWARD(GetNextAvailDrive) -FORWARD(GetLastError) -FORWARD(SetLastError) -FORWARD(LoginName) -FORWARD(NodeName) -FORWARD(DomainName) -FORWARD(FsType) -FORWARD(GetOSVersion) -FORWARD(IsWinNT) -FORWARD(IsWin95) -FORWARD(FormatMessage) -FORWARD(Spawn) -FORWARD(GetTickCount) -FORWARD(GetShortPathName) -FORWARD(GetFullPathName) -FORWARD(GetLongPathName) -FORWARD(CopyFile) -FORWARD(Sleep) - -/* Don't forward Win32::SetChildShowWindow(). It accesses the internal variable - * w32_showwindow in thread_intern and is therefore not implemented in Win32.xs. - */ -/* FORWARD(SetChildShowWindow) */ - -#undef FORWARD - void Perl_init_os_extras(void) { dTHX; char *file = __FILE__; - dXSUB_SYS; - - /* these names are Activeware compatible */ - 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::CopyFile", w32_CopyFile, file); - newXS("Win32::Sleep", w32_Sleep, file); + + /* Initialize Win32CORE if it has been statically linked. */ + void (*pfn_init)(pTHX); +#if defined(__BORLANDC__) + /* makedef.pl seems to have given up on fixing this issue in the .def file */ + pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "_init_Win32CORE"); +#else + pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "init_Win32CORE"); +#endif + if (pfn_init) + pfn_init(aTHX); + newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file); } @@ -4483,14 +4666,114 @@ win32_ctrlhandler(DWORD dwCtrlType) } -#if _MSC_VER >= 1400 +#ifdef SET_INVALID_PARAMETER_HANDLER # include #endif +static void +ansify_path(void) +{ + size_t len; + char *ansi_path; + WCHAR *wide_path; + WCHAR *wide_dir; + + /* win32_ansipath() requires Windows 2000 or later */ + if (!IsWin2000()) + return; + + /* fetch Unicode version of PATH */ + len = 2000; + wide_path = win32_malloc(len*sizeof(WCHAR)); + while (wide_path) { + size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len); + if (newlen < len) + break; + len = newlen; + wide_path = win32_realloc(wide_path, len*sizeof(WCHAR)); + } + if (!wide_path) + return; + + /* convert to ANSI pathnames */ + wide_dir = wide_path; + ansi_path = NULL; + while (wide_dir) { + WCHAR *sep = wcschr(wide_dir, ';'); + char *ansi_dir; + size_t ansi_len; + size_t wide_len; + + if (sep) + *sep++ = '\0'; + + /* remove quotes around pathname */ + if (*wide_dir == '"') + ++wide_dir; + wide_len = wcslen(wide_dir); + if (wide_len && wide_dir[wide_len-1] == '"') + wide_dir[wide_len-1] = '\0'; + + /* append ansi_dir to ansi_path */ + ansi_dir = win32_ansipath(wide_dir); + ansi_len = strlen(ansi_dir); + if (ansi_path) { + size_t newlen = len + 1 + ansi_len; + ansi_path = win32_realloc(ansi_path, newlen+1); + if (!ansi_path) + break; + ansi_path[len] = ';'; + memcpy(ansi_path+len+1, ansi_dir, ansi_len+1); + len = newlen; + } + else { + len = ansi_len; + ansi_path = win32_malloc(5+len+1); + if (!ansi_path) + break; + memcpy(ansi_path, "PATH=", 5); + memcpy(ansi_path+5, ansi_dir, len+1); + len += 5; + } + win32_free(ansi_dir); + wide_dir = sep; + } + + if (ansi_path) { + /* Update C RTL environ array. This will only have full effect if + * perl_parse() is later called with `environ` as the `env` argument. + * Otherwise S_init_postdump_symbols() will overwrite PATH again. + * + * We do have to ansify() the PATH before Perl has been fully + * initialized because S_find_script() uses the PATH when perl + * is being invoked with the -S option. This happens before %ENV + * is initialized in S_init_postdump_symbols(). + * + * XXX Is this a bug? Should S_find_script() use the environment + * XXX passed in the `env` arg to parse_perl()? + */ + putenv(ansi_path); + /* Keep system environment in sync because S_init_postdump_symbols() + * will not call mg_set() if it initializes %ENV from `environ`. + */ + SetEnvironmentVariableA("PATH", ansi_path+5); + /* We are intentionally leaking the ansi_path string here because + * the Borland runtime library puts it directly into the environ + * array. The Microsoft runtime library seems to make a copy, + * but will leak the copy should it be replaced again later. + * Since this code is only called once during PERL_SYS_INIT this + * shouldn't really matter. + */ + } + win32_free(wide_path); +} + void Perl_win32_init(int *argcp, char ***argvp) { -#if _MSC_VER >= 1400 + HMODULE module; + +#ifdef SET_INVALID_PARAMETER_HANDLER _invalid_parameter_handler oldHandler, newHandler; newHandler = my_invalid_parameter_handler; oldHandler = _set_invalid_parameter_handler(newHandler); @@ -4506,6 +4789,23 @@ Perl_win32_init(int *argcp, char ***argvp) _control87(MCW_EM, MCW_EM); #endif MALLOC_INIT; + + module = GetModuleHandle("ntdll.dll"); + if (module) { + *(FARPROC*)&pfnZwQuerySystemInformation = GetProcAddress(module, "ZwQuerySystemInformation"); + } + + module = GetModuleHandle("kernel32.dll"); + if (module) { + *(FARPROC*)&pfnCreateToolhelp32Snapshot = GetProcAddress(module, "CreateToolhelp32Snapshot"); + *(FARPROC*)&pfnProcess32First = GetProcAddress(module, "Process32First"); + *(FARPROC*)&pfnProcess32Next = GetProcAddress(module, "Process32Next"); + } + + g_osver.dwOSVersionInfoSize = sizeof(g_osver); + GetVersionEx(&g_osver); + + ansify_path(); } void @@ -4546,20 +4846,6 @@ win32_signal(int sig, Sighandler_t subcode) } } - -#ifdef HAVE_INTERP_INTERN - - -static void -win32_csighandler(int sig) -{ -#if 0 - dTHXa(PERL_GET_SIG_CONTEXT); - Perl_warn(aTHX_ "Got signal %d",sig); -#endif - /* Does nothing */ -} - HWND win32_create_message_window() { @@ -4571,12 +4857,24 @@ win32_create_message_window() * "right" place with DispatchMessage() anymore, as there is no WindowProc * if there is no window handle. */ - if (g_osver.dwMajorVersion < 5) + if (!IsWin2000()) return NULL; return CreateWindow("Static", "", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL); } +#ifdef HAVE_INTERP_INTERN + +static void +win32_csighandler(int sig) +{ +#if 0 + dTHXa(PERL_GET_SIG_CONTEXT); + Perl_warn(aTHX_ "Got signal %d",sig); +#endif + /* Does nothing */ +} + #if defined(__MINGW32__) && defined(__cplusplus) #define CAST_HWND__(x) (HWND__*)(x) #else @@ -4588,12 +4886,7 @@ Perl_sys_intern_init(pTHX) { int i; - if (g_osver.dwOSVersionInfoSize == 0) { - g_osver.dwOSVersionInfoSize = sizeof(g_osver); - GetVersionEx(&g_osver); - } - - w32_perlshell_tokens = Nullch; + w32_perlshell_tokens = NULL; w32_perlshell_vec = (char**)NULL; w32_perlshell_items = 0; w32_fdpid = newAV(); @@ -4610,7 +4903,7 @@ Perl_sys_intern_init(pTHX) for (i=0; i < SIG_SIZE; i++) { w32_sighandler[i] = SIG_DFL; } -# ifdef MULTIPLICTY +# ifdef MULTIPLICITY if (my_perl == PL_curinterp) { # else { @@ -4618,6 +4911,16 @@ Perl_sys_intern_init(pTHX) /* Force C runtime signal stuff to set its console handler */ signal(SIGINT,win32_csighandler); signal(SIGBREAK,win32_csighandler); + + /* We spawn asynchronous processes with the CREATE_NEW_PROCESS_GROUP + * flag. This has the side-effect of disabling Ctrl-C events in all + * processes in this group. At least on Windows NT and later we + * can re-enable Ctrl-C handling by calling SetConsoleCtrlHandler() + * with a NULL handler. This is not valid on Windows 9X. + */ + if (IsWinNT()) + SetConsoleCtrlHandler(NULL,FALSE); + /* Push our handler on top */ SetConsoleCtrlHandler(win32_ctrlhandler,TRUE); } @@ -4653,7 +4956,7 @@ Perl_sys_intern_clear(pTHX) void Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst) { - dst->perlshell_tokens = Nullch; + dst->perlshell_tokens = NULL; dst->perlshell_vec = (char**)NULL; dst->perlshell_items = 0; dst->fdpid = newAV(); @@ -4667,32 +4970,3 @@ Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst) } # endif /* USE_ITHREADS */ #endif /* HAVE_INTERP_INTERN */ - -static void -win32_free_argvw(pTHX_ void *ptr) -{ - char** argv = (char**)ptr; - while(*argv) { - Safefree(*argv); - *argv++ = Nullch; - } -} - -void -win32_argv2utf8(int argc, char** argv) -{ - dTHX; - char* psz; - int length, wargc; - LPWSTR* lpwStr = CommandLineToArgvW(GetCommandLineW(), &wargc); - if (lpwStr && argc) { - while (argc--) { - length = WideCharToMultiByte(CP_UTF8, 0, lpwStr[--wargc], -1, NULL, 0, NULL, NULL); - Newxz(psz, length, char); - WideCharToMultiByte(CP_UTF8, 0, lpwStr[wargc], -1, psz, length, NULL, NULL); - argv[argc] = psz; - } - call_atexit(win32_free_argvw, argv); - } - GlobalFree((HGLOBAL)lpwStr); -}