This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
The $@ was actually a botched conversion from $make $targ, rather than an
[perl5.git] / win32 / win32.c
index aacc656..0673fc2 100644 (file)
@@ -19,7 +19,7 @@
 #  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 <winnt.h>
 #include <tlhelp32.h>
@@ -60,13 +60,6 @@ typedef struct {
 #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 <shellapi.h>
-#else
-EXTERN_C LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs);
-#endif
-
 #define NO_XSLOCKS
 #define PERL_NO_GET_CONTEXT
 #include "XSUB.h"
@@ -136,6 +129,8 @@ static long         find_pid(int pid);
 static char *          qualified_path(const char *cmd);
 static char *          win32_get_xlib(const char *pl, const char *xlib,
                                       const char *libname);
+static LRESULT  win32_process_message(HWND hwnd, UINT msg,
+                       WPARAM wParam, LPARAM lParam);
 
 #ifdef USE_ITHREADS
 static void            remove_dead_pseudo_process(long child);
@@ -164,7 +159,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, 
@@ -191,6 +201,12 @@ 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)
 {
@@ -204,7 +220,7 @@ set_w32_module_name(void)
     osver.dwOSVersionInfoSize = sizeof(osver);
     GetVersionEx(&osver);
 
-    if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    if (osver.dwMajorVersion > 4) {
         WCHAR modulename[MAX_PATH];
         WCHAR fullname[MAX_PATH];
         char *ansi;
@@ -254,7 +270,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);
@@ -352,7 +368,7 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
        return SvPVX(*prev_pathp);
     }
 
-    return Nullch;
+    return NULL;
 }
 
 char *
@@ -361,7 +377,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);
@@ -369,7 +385,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 *
@@ -378,8 +394,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);
@@ -388,7 +404,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);
@@ -396,10 +412,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)
@@ -535,7 +551,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) {
@@ -570,7 +586,7 @@ tokenize(const char *str, char **dest, char ***destv)
                ++items;
            ret++;
        }
-       retvstart[items] = Nullch;
+       retvstart[items] = NULL;
        *ret++ = '\0';
        *ret = '\0';
     }
@@ -609,6 +625,8 @@ Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
     int flag = P_WAIT;
     int index = 0;
 
+    PERL_ARGS_ASSERT_DO_ASPAWN;
+
     if (sp <= mark)
        return -1;
 
@@ -647,8 +665,7 @@ Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
     }
 
     if (flag == P_NOWAIT) {
-       if (IsWin95())
-           PL_statusvalue = -1;        /* >16bits hint for pp_system() */
+       PL_statusvalue = -1;    /* >16bits hint for pp_system() */
     }
     else {
        if (status < 0) {
@@ -714,7 +731,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:
@@ -743,7 +760,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],
@@ -761,8 +778,7 @@ do_spawn2(pTHX_ const char *cmd, int exectype)
        Safefree(argv);
     }
     if (exectype == EXECF_SPAWN_NOWAIT) {
-       if (IsWin95())
-           PL_statusvalue = -1;        /* >16bits hint for pp_system() */
+       PL_statusvalue = -1;    /* >16bits hint for pp_system() */
     }
     else {
        if (status < 0) {
@@ -782,18 +798,24 @@ do_spawn2(pTHX_ const char *cmd, int exectype)
 int
 Perl_do_spawn(pTHX_ char *cmd)
 {
+    PERL_ARGS_ASSERT_DO_SPAWN;
+
     return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
 }
 
 int
 Perl_do_spawn_nowait(pTHX_ char *cmd)
 {
+    PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
+
     return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
 }
 
 bool
 Perl_do_exec(pTHX_ const char *cmd)
 {
+    PERL_ARGS_ASSERT_DO_EXEC;
+
     do_spawn2(aTHX_ cmd, EXECF_EXEC);
     return FALSE;
 }
@@ -843,7 +865,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);
@@ -934,7 +956,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) {
@@ -1203,7 +1225,7 @@ kill_process_tree_toolhelp(DWORD pid, int sig)
     int killed = 0;
 
     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
-    if (process_handle == INVALID_HANDLE_VALUE)
+    if (process_handle == NULL)
         return 0;
 
     killed += terminate_process(pid, process_handle, sig);
@@ -1238,7 +1260,7 @@ kill_process_tree_sysinfo(SYSTEM_PROCESSES *process_info, DWORD pid, int sig)
     int killed = 0;
 
     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
-    if (process_handle == INVALID_HANDLE_VALUE)
+    if (process_handle == NULL)
         return 0;
 
     killed += terminate_process(pid, process_handle, sig);
@@ -1293,7 +1315,8 @@ my_kill(int pid, int sig)
         return killpg(pid, -sig);
 
     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
-    if (process_handle != INVALID_HANDLE_VALUE) {
+    /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */
+    if (process_handle != NULL) {
         retval = terminate_process(pid, process_handle, sig);
         CloseHandle(process_handle);
     }
@@ -1332,6 +1355,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) {
@@ -1484,9 +1508,22 @@ win32_stat(const char *path, Stat_t *sbuf)
             errno = ENOTDIR;
             return -1;
         }
+       if (S_ISDIR(sbuf->st_mode)) {
+           /* Ensure the "write" bit is switched off in the mode for
+            * directories with the read-only attribute set. Borland (at least)
+            * switches it on for directories, which is technically correct
+            * (directories are indeed always writable unless denied by DACLs),
+            * but we want stat() and -w to reflect the state of the read-only
+            * attribute for symmetry with chmod(). */
+           DWORD r = GetFileAttributesA(path);
+           if (r != 0xffffffff && (r & FILE_ATTRIBUTE_READONLY)) {
+               sbuf->st_mode &= ~S_IWRITE;
+           }
+       }
 #ifdef __BORLANDC__
-       if (S_ISDIR(sbuf->st_mode))
-           sbuf->st_mode |= S_IWRITE | S_IEXEC;
+       if (S_ISDIR(sbuf->st_mode)) {
+           sbuf->st_mode |= S_IEXEC;
+       }
        else if (S_ISREG(sbuf->st_mode)) {
            int perms;
            if (l >= 4 && path[l-4] == '.') {
@@ -1534,7 +1571,7 @@ win32_longpath(char *path)
     char *start = path;
     char sep;
     if (!path)
-       return Nullch;
+       return NULL;
 
     /* drive prefix */
     if (isALPHA(path[0]) && path[1] == ':') {
@@ -1598,14 +1635,14 @@ 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);
@@ -1613,7 +1650,7 @@ win32_longpath(char *path)
 }
 
 static void
-out_of_memory()
+out_of_memory(void)
 {
     if (PL_curinterp) {
         dTHX;
@@ -1682,7 +1719,7 @@ win32_getenv(const char *name)
 {
     dTHX;
     DWORD needlen;
-    SV *curitem = Nullsv;
+    SV *curitem = NULL;
 
     needlen = GetEnvironmentVariableA(name,NULL,0);
     if (needlen != 0) {
@@ -1703,7 +1740,7 @@ win32_getenv(const char *name)
     if (curitem && SvCUR(curitem))
        return SvPVX(curitem);
 
-    return Nullch;
+    return NULL;
 }
 
 DllExport int
@@ -2066,68 +2103,47 @@ win32_async_check(pTHX)
     MSG msg;
     HWND hwnd = w32_message_hwnd;
 
+    /* Reset w32_poll_count before doing anything else, incase we dispatch
+     * messages that end up calling back into perl */
     w32_poll_count = 0;
 
-    if (hwnd == INVALID_HANDLE_VALUE) {
-        /* Call PeekMessage() to mark all pending messages in the queue as "old".
-         * This is necessary when we are being called by win32_msgwait() to
-         * make sure MsgWaitForMultipleObjects() stops reporting the same waiting
-         * message over and over.  An example how this can happen is when
-         * Perl is calling win32_waitpid() inside a GUI application and the GUI
-         * is generating messages before the process terminated.
-         */
-        PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD);
-        if (PL_sig_pending)
-            despatch_signals();
-        return 1;
-    }
-
-    /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages
-     * and ignores window messages - should co-exist better with windows apps e.g. Tk
-     */
-    if (hwnd == NULL)
-        hwnd = (HWND)-1;
-
-    while (PeekMessage(&msg, hwnd, WM_TIMER,    WM_TIMER,    PM_REMOVE|PM_NOYIELD) ||
-           PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD))
-    {
-       switch (msg.message) {
-#ifdef USE_ITHREADS
-        case WM_USER_MESSAGE: {
-            int child = find_pseudo_pid(msg.wParam);
-            if (child >= 0)
-                w32_pseudo_child_message_hwnds[child] = (HWND)msg.lParam;
-            break;
-        }
-#endif
-
-       case WM_USER_KILL: {
-            /* We use WM_USER to fake kill() with other signals */
-           int sig = msg.wParam;
-           if (do_raise(aTHX_ sig))
-                sig_terminate(aTHX_ sig);
-           break;
-       }
-
-       case WM_TIMER: {
-           /* alarm() is a one-shot but SetTimer() repeats so kill it */
-           if (w32_timerid && w32_timerid==msg.wParam) {
-               KillTimer(w32_message_hwnd, w32_timerid);
-               w32_timerid=0;
+    if (hwnd != INVALID_HANDLE_VALUE) {
+        /* Passing PeekMessage -1 as HWND (2nd arg) only gets PostThreadMessage() messages
+        * and ignores window messages - should co-exist better with windows apps e.g. Tk
+        */
+        if (hwnd == NULL)
+            hwnd = (HWND)-1;
+
+        while (PeekMessage(&msg, hwnd, WM_TIMER,    WM_TIMER,    PM_REMOVE|PM_NOYIELD) ||
+               PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD))
+        {
+            /* re-post a WM_QUIT message (we'll mark it as read later) */
+            if(msg.message == WM_QUIT) {
+                PostQuitMessage((int)msg.wParam);
+                break;
+            }
 
-                /* Now fake a call to signal handler */
-                if (do_raise(aTHX_ 14))
-                    sig_terminate(aTHX_ 14);
+            if(!CallMsgFilter(&msg, MSGF_USER))
+            {
+                TranslateMessage(&msg);
+                DispatchMessage(&msg);
             }
-           break;
-       }
-        } /* switch */
+        }
     }
 
+    /* Call PeekMessage() to mark all pending messages in the queue as "old".
+     * This is necessary when we are being called by win32_msgwait() to
+     * make sure MsgWaitForMultipleObjects() stops reporting the same waiting
+     * message over and over.  An example how this can happen is when
+     * Perl is calling win32_waitpid() inside a GUI application and the GUI
+     * is generating messages before the process terminated.
+     */
+    PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD);
+
     /* Above or other stuff may have set a signal flag */
-    if (PL_sig_pending) {
-       despatch_signals();
-    }
+    if (PL_sig_pending)
+        despatch_signals();
+    
     return 1;
 }
 
@@ -2143,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_POSTMESSAGE|QS_TIMER);
+       DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
        if (resultp)
           *resultp = result;
        if (result == WAIT_TIMEOUT) {
@@ -2374,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
 }
 
@@ -2547,7 +2563,7 @@ win32_stdin(void)
 }
 
 DllExport FILE *
-win32_stdout()
+win32_stdout(void)
 {
     return (stdout);
 }
@@ -2929,7 +2945,7 @@ win32_fstat(int fd, Stat_t *sbufptr)
 
     if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &bhfi)) {
 #if defined(WIN64) || defined(USE_LARGE_FILES)    
-        sbufptr->st_size = (bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow ;
+        sbufptr->st_size = ((__int64)bhfi.nFileSizeHigh << 32) | bhfi.nFileSizeLow ;
 #endif
         sbufptr->st_mode &= 0xFE00;
         if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
@@ -3253,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;
 
@@ -3312,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);
@@ -3894,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 == '\\')
@@ -3968,7 +3984,7 @@ qualified_path(const char *cmd)
     }
 
     Safefree(fullcmd);
-    return Nullch;
+    return NULL;
 }
 
 /* The following are just place holders.
@@ -4055,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;
 
@@ -4566,15 +4582,17 @@ Perl_init_os_extras(void)
 {
     dTHX;
     char *file = __FILE__;
-    CV *cv;
-    dXSUB_SYS;
 
-    /* load Win32 CORE stubs, assuming Win32CORE was statically linked */
-    if ((cv = get_cv("Win32CORE::bootstrap", 0))) {
-       dSP;
-       PUSHMARK(SP);
-       (void)call_sv((SV *)cv, G_EVAL|G_DISCARD|G_VOID);
-    }
+    /* 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);
 }
@@ -4648,7 +4666,7 @@ win32_ctrlhandler(DWORD dwCtrlType)
 }
 
 
-#if _MSC_VER >= 1400
+#ifdef SET_INVALID_PARAMETER_HANDLER
 #  include <crtdbg.h>
 #endif
 
@@ -4660,8 +4678,8 @@ ansify_path(void)
     WCHAR *wide_path;
     WCHAR *wide_dir;
 
-    /* there is no Unicode environment on Windows 9X */
-    if (IsWin95())
+    /* win32_ansipath() requires Windows 2000 or later */
+    if (!IsWin2000())
         return;
 
     /* fetch Unicode version of PATH */
@@ -4755,7 +4773,7 @@ Perl_win32_init(int *argcp, char ***argvp)
 {
     HMODULE module;
 
-#if _MSC_VER >= 1400
+#ifdef SET_INVALID_PARAMETER_HANDLER
     _invalid_parameter_handler oldHandler, newHandler;
     newHandler = my_invalid_parameter_handler;
     oldHandler = _set_invalid_parameter_handler(newHandler);
@@ -4828,22 +4846,132 @@ win32_signal(int sig, Sighandler_t subcode)
     }
 }
 
+/* The PerlMessageWindowClass's WindowProc */
+LRESULT CALLBACK
+win32_message_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    return win32_process_message(hwnd, msg, wParam, lParam) ?
+        0 : DefWindowProc(hwnd, msg, wParam, lParam);
+}
 
-#ifdef HAVE_INTERP_INTERN
+/* we use a message filter hook to process thread messages, passing any
+ * messages that we don't process on to the rest of the hook chain
+ * Anyone else writing a message loop that wants to play nicely with perl
+ * should do
+ *   CallMsgFilter(&msg, MSGF_***);
+ * between their GetMessage and DispatchMessage calls.  */
+LRESULT CALLBACK
+win32_message_filter_proc(int code, WPARAM wParam, LPARAM lParam) {
+    LPMSG pmsg = (LPMSG)lParam;
 
-static void
-win32_csighandler(int sig)
+    /* we'll process it if code says we're allowed, and it's a thread message */
+    if (code >= 0 && pmsg->hwnd == NULL
+            && win32_process_message(pmsg->hwnd, pmsg->message,
+                                     pmsg->wParam, pmsg->lParam))
+    {
+            return TRUE;
+    }
+
+    /* XXX: MSDN says that hhk is ignored, but we should really use the
+     * return value from SetWindowsHookEx() in win32_create_message_window().  */
+    return CallNextHookEx(NULL, code, wParam, lParam);
+}
+
+/* The real message handler. Can be called with
+ * hwnd == NULL to process our thread messages. Returns TRUE for any messages
+ * that it processes */
+static LRESULT
+win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
-#if 0
-    dTHXa(PERL_GET_SIG_CONTEXT);
-    Perl_warn(aTHX_ "Got signal %d",sig);
+    /* BEWARE. The context retrieved using dTHX; is the context of the
+     * 'parent' thread during the CreateWindow() phase - i.e. for all messages
+     * up to and including WM_CREATE.  If it ever happens that you need the
+     * 'child' context before this, then it needs to be passed into
+     * win32_create_message_window(), and passed to the WM_NCCREATE handler
+     * from the lparam of CreateWindow().  It could then be stored/retrieved
+     * using [GS]etWindowLongPtr(... GWLP_USERDATA ...), possibly eliminating
+     * the dTHX calls here. */
+    /* XXX For now it is assumed that the overhead of the dTHX; for what
+     * are relativley infrequent code-paths, is better than the added
+     * complexity of getting the correct context passed into
+     * win32_create_message_window() */
+
+    switch(msg) {
+
+#ifdef USE_ITHREADS
+        case WM_USER_MESSAGE: {
+            long child = find_pseudo_pid((int)wParam);
+            if (child >= 0) {
+                dTHX;
+                w32_pseudo_child_message_hwnds[child] = (HWND)lParam;
+                return 1;
+            }
+            break;
+        }
 #endif
-    /* Does nothing */
+
+        case WM_USER_KILL: {
+            dTHX;
+            /* We use WM_USER_KILL to fake kill() with other signals */
+            int sig = (int)wParam;
+            if (do_raise(aTHX_ sig))
+                sig_terminate(aTHX_ sig);
+
+            return 1;
+        }
+
+        case WM_TIMER: {
+            dTHX;
+            /* alarm() is a one-shot but SetTimer() repeats so kill it */
+            if (w32_timerid && w32_timerid==(UINT)wParam) {
+                KillTimer(w32_message_hwnd, w32_timerid);
+                w32_timerid=0;
+
+                /* Now fake a call to signal handler */
+                if (do_raise(aTHX_ 14))
+                    sig_terminate(aTHX_ 14);
+
+                return 1;
+            }
+            break;
+        }
+
+        default:
+            break;
+
+    } /* switch */
+
+    /* Above or other stuff may have set a signal flag, and we may not have
+     * been called from win32_async_check() (e.g. some other GUI's message
+     * loop.  BUT DON'T dispatch signals here: If someone has set a SIGALRM
+     * handler that die's, and the message loop that calls here is wrapped
+     * in an eval, then you may well end up with orphaned windows - signals
+     * are dispatched by win32_async_check() */
+
+    return 0;
+}
+
+void
+win32_create_message_window_class(void)
+{
+    /* create the window class for "message only" windows */
+    WNDCLASS wc;
+
+    Zero(&wc, 1, wc);
+    wc.lpfnWndProc = win32_message_window_proc;
+    wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);
+    wc.lpszClassName = "PerlMessageWindowClass";
+
+    /* second and subsequent calls will fail, but class
+     * will already be registered */
+    RegisterClass(&wc);
 }
 
 HWND
-win32_create_message_window()
+win32_create_message_window(void)
 {
+    HWND hwnd = NULL;
+
     /* "message-only" windows have been implemented in Windows 2000 and later.
      * On earlier versions we'll continue to post messages to a specific
      * thread and use hwnd==NULL.  This is brittle when either an embedding
@@ -4852,10 +4980,42 @@ 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)
-        return NULL;
+    /* Using HWND_MESSAGE appears to work under Win98, despite MSDN
+     * documentation to the contrary, however, there is some evidence that
+     * there may be problems with the implementation on Win98. As it is not
+     * officially supported we take the cautious route and stick with thread
+     * messages (hwnd == NULL) on platforms prior to Win2k.
+     */
+    if (IsWin2000()) {
+        win32_create_message_window_class();
 
-    return CreateWindow("Static", "", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL);
+        hwnd = CreateWindow("PerlMessageWindowClass", "PerlMessageWindow",
+                0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+    }
+
+    /* If we din't create a window for any reason, then we'll use thread
+     * messages for our signalling, so we install a hook which
+     * is called by CallMsgFilter in win32_async_check(), or any other
+     * modal loop (e.g. Win32::MsgBox or any other GUI extention, or anything
+     * that use OLE, etc. */
+    if(!hwnd) {
+        SetWindowsHookEx(WH_MSGFILTER, win32_message_filter_proc,
+                NULL, GetCurrentThreadId());
+    }
+  
+    return hwnd;
+}
+
+#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)
@@ -4869,7 +5029,7 @@ Perl_sys_intern_init(pTHX)
 {
     int i;
 
-    w32_perlshell_tokens       = Nullch;
+    w32_perlshell_tokens       = NULL;
     w32_perlshell_vec          = (char**)NULL;
     w32_perlshell_items                = 0;
     w32_fdpid                  = newAV();
@@ -4894,6 +5054,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);
     }
@@ -4929,7 +5099,9 @@ Perl_sys_intern_clear(pTHX)
 void
 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
 {
-    dst->perlshell_tokens      = Nullch;
+    PERL_ARGS_ASSERT_SYS_INTERN_DUP;
+
+    dst->perlshell_tokens      = NULL;
     dst->perlshell_vec         = (char**)NULL;
     dst->perlshell_items       = 0;
     dst->fdpid                 = newAV();
@@ -4943,32 +5115,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);
-}