[SHELL32] Fix Control_RunDLLW (#5400) master
authorMarcin Jabłoński <nnxcomputing@gmail.com>
Sat, 27 Apr 2024 00:28:46 +0000 (02:28 +0200)
committerGitHub <noreply@github.com>
Sat, 27 Apr 2024 00:28:46 +0000 (18:28 -0600)
This commit makes Control_RunDLLW pass all but one tests from rostests (the one test that fails is the first one, but it only fails if the path to the test program contains a space).

- Rework string parsing in the Control_DoLaunch routine
- Do not send the CPL_STARTWPARMSW message, if no extra parameters were specified (fixes the failing Got NULL lParam2! and some CPL_STARTWPARMSW: expected -1 got %d tests)
- Do not resolve invalid dialog names to index zero, unless the name is empty (fixes some of the failing CPL_DBLCLK: expected -1 got %d tests)
- Handle quotes in the second part of wszCmd

CORE-8981

dll/win32/shell32/wine/control.c
dll/win32/syssetup/wizard.c

index 4acbc31..612dbcc 100644 (file)
@@ -873,6 +873,7 @@ static      void    Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
     *   "a path\foo.cpl"
     */
 {
+#ifndef __REACTOS__
     LPWSTR     buffer;
     LPWSTR     beg = NULL;
     LPWSTR     end;
@@ -952,14 +953,6 @@ static     void    Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
     applet = Control_LoadApplet(hWnd, buffer, panel);
     if (applet)
     {
-#ifdef __REACTOS__
-    ULONG_PTR cookie;
-    BOOL bActivated;
-    ATOM aCPLName;
-    ATOM aCPLFlags;
-    ATOM aCPLPath;
-    AppDlgFindData findData;
-#endif
         /* we've been given a textual parameter (or none at all) */
         if (sp == -1) {
             while ((++sp) != applet->count) {
@@ -975,65 +968,201 @@ static   void    Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
             sp = 0;
         }
 
-#ifdef __REACTOS__
-        bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE);
+        if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
+            applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
+
+        Control_UnloadApplet(applet);
+    }
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+#else
+    LPWSTR     buffer;
+    LPWSTR       ptr;
+    signed     sp = -1;
+    LPCWSTR    extraPmts = L"";
+    BOOL        quoted = FALSE;
+    CPlApplet *applet;
+    LPCWSTR pchFirstComma = NULL, pchSecondComma = NULL;
+    LPCWSTR pchLastUnquotedSpace = NULL;
+    LPWSTR wszDialogBoxName;
+    int i = 0;
+    SIZE_T nLen = lstrlenW(wszCmd);
+
+    buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*buffer) * (nLen + 1));
+    wszDialogBoxName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wszDialogBoxName) * (nLen + 1));
+    if (wszDialogBoxName == NULL || buffer == NULL)
+    {
+        if (buffer != NULL)
+            HeapFree(GetProcessHeap(), 0, buffer);
+        if (wszDialogBoxName != NULL)
+            HeapFree(GetProcessHeap(), 0, wszDialogBoxName);
+        return;
+    }
 
-        aCPLPath = GlobalFindAtomW(applet->cmd);
-        if (!aCPLPath)
+    /* Search for unquoted commas and spaces. */
+    for (i = 0; i < nLen; i++)
+    {
+        if (quoted && wszCmd[i] != L'"')
+            continue;
+        switch (wszCmd[i])
         {
-            aCPLPath = GlobalAddAtomW(applet->cmd);
+            case L'"':
+                quoted = !quoted;
+                break;
+            case L',':
+                if (!pchFirstComma) 
+                    pchFirstComma = &wszCmd[i];
+                else if (!pchSecondComma) 
+                    pchSecondComma = &wszCmd[i];
+                break;
+            case L' ': 
+                pchLastUnquotedSpace = &wszCmd[i];
+                break;
         }
+    }
 
-        aCPLName = GlobalFindAtomW(L"CPLName");
-        if (!aCPLName)
+    /* If no unquoted commas are found, parameters are either space separated, or the entire string
+     * is a CPL path. */
+    if (!pchFirstComma)
+    {
+        /* An unquoted space was found in the string. Assume the last word is the dialog box
+         * name/number. */
+        if (pchLastUnquotedSpace)
+        {
+            int nSpaces = 0;
+
+            while (pchLastUnquotedSpace[nSpaces] == L' ')
+                nSpaces++;
+
+            StringCchCopyNW(buffer, nLen + 1, wszCmd, pchLastUnquotedSpace - wszCmd);
+            StringCchCopyW(wszDialogBoxName, nLen + 1, pchLastUnquotedSpace + nSpaces);
+        }
+        /* No parameters were passed, the entire string is the CPL path. */
+        else
+        {
+            StringCchCopyW(buffer, nLen + 1, wszCmd);
+        }
+    }
+    /* If an unquoted comma was found, there are at least two parts of the string:
+     * - the CPL path
+     * - either a dialog box number preceeded by @, or a dialog box name.
+     * If there was a second unqoted comma, there is another part of the string:
+     * - the rest of the parameters. */
+    else
+    {
+        /* If there was no second unquoted comma in the string, the CPL path ends at thes
+          * null terminator. */
+        if (!pchSecondComma)
+            pchSecondComma = wszCmd + nLen;
+
+        StringCchCopyNW(buffer, nLen + 1, wszCmd, pchFirstComma - wszCmd);
+        StringCchCopyNW(wszDialogBoxName,
+                        nLen + 1,
+                        pchFirstComma + 1,
+                        pchSecondComma - pchFirstComma - 1);
+
+        if (pchSecondComma != wszCmd + nLen)
         {
-            aCPLName = GlobalAddAtomW(L"CPLName");
+            extraPmts = pchSecondComma + 1;
         }
+    }
+
+    /* Remove the quotes from both buffers. */
+    while ((ptr = StrChrW(buffer, '"')))
+        memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
 
-        aCPLFlags = GlobalFindAtomW(L"CPLFlags");
-        if (!aCPLFlags)
+    while ((ptr = StrChrW(wszDialogBoxName, '"')))
+        memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
+
+    if (wszDialogBoxName[0] == L'@')
+    {
+        sp = _wtoi(wszDialogBoxName + 1);
+    }
+
+    TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp);
+
+    applet = Control_LoadApplet(hWnd, buffer, panel);
+    if (applet)
+    {
+        ULONG_PTR cookie;
+        BOOL bActivated;
+        ATOM aCPLName;
+        ATOM aCPLFlags;
+        ATOM aCPLPath;
+        AppDlgFindData findData;
+        
+        /* we've been given a textual parameter (or none at all) */
+        if (sp == -1) 
         {
-            aCPLFlags = GlobalAddAtomW(L"CPLFlags");
+            while ((++sp) != applet->count) 
+            {
+                TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name));
+
+                if (StrCmpIW(wszDialogBoxName, applet->info[sp].name) == 0)
+                    break;
+            }
         }
 
-        findData.szAppFile = applet->cmd;
-        findData.sAppletNo = (UINT_PTR)(sp + 1);
-        findData.aCPLName = aCPLName;
-        findData.aCPLFlags = aCPLFlags;
-        findData.hRunDLL = applet->hWnd;
-        findData.hDlgResult = NULL;
-        // Find the dialog of this applet in the first instance.
-        // Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs.
-        EnumWindows(Control_EnumWinProc, (LPARAM)&findData);
-        if (findData.hDlgResult)
+        if (sp >= applet->count && wszDialogBoxName[0] == L'\0')
         {
-            BringWindowToTop(findData.hDlgResult);
+            sp = 0;
         }
-        else
+
+        bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE);
+
+        if (sp < applet->count)
         {
-            SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLName), (HANDLE)MAKEINTATOM(aCPLPath));
-            SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLFlags), UlongToHandle(sp + 1));
-            Control_ShowAppletInTaskbar(applet, sp);
-#endif
+            aCPLPath = GlobalFindAtomW(applet->cmd);
+            if (!aCPLPath)
+                aCPLPath = GlobalAddAtomW(applet->cmd);
+
+            aCPLName = GlobalFindAtomW(L"CPLName");
+            if (!aCPLName)
+                aCPLName = GlobalAddAtomW(L"CPLName");
+
+            aCPLFlags = GlobalFindAtomW(L"CPLFlags");
+            if (!aCPLFlags)
+                aCPLFlags = GlobalAddAtomW(L"CPLFlags");
+
+            findData.szAppFile = applet->cmd;
+            findData.sAppletNo = (UINT_PTR)(sp + 1);
+            findData.aCPLName = aCPLName;
+            findData.aCPLFlags = aCPLFlags;
+            findData.hRunDLL = applet->hWnd;
+            findData.hDlgResult = NULL;
+            // Find the dialog of this applet in the first instance.
+            // Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs.
+            EnumWindows(Control_EnumWinProc, (LPARAM)&findData);
+            if (findData.hDlgResult)
+            {
+                BringWindowToTop(findData.hDlgResult);
+            }
+            else
+            {
+                SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLName), (HANDLE)MAKEINTATOM(aCPLPath));
+                SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLFlags), UlongToHandle(sp + 1));
+                Control_ShowAppletInTaskbar(applet, sp);
+
+                if (extraPmts[0] == L'\0' ||
+                    !applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
+                {
+                    applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
+                }
 
-        if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
-            applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
-#ifdef __REACTOS__
-            RemovePropW(applet->hWnd, applet->cmd);
-            GlobalDeleteAtom(aCPLPath);
+                RemovePropW(applet->hWnd, applet->cmd);
+                GlobalDeleteAtom(aCPLPath);
+            }
         }
-#endif
 
         Control_UnloadApplet(applet);
 
-#ifdef __REACTOS__
-    if (bActivated)
-        DeactivateActCtx(0, cookie);
-#endif
-
+        if (bActivated)
+            DeactivateActCtx(0, cookie);
     }
 
     HeapFree(GetProcessHeap(), 0, buffer);
+    HeapFree(GetProcessHeap(), 0, wszDialogBoxName);
+#endif
 }
 
 /*************************************************************************
index 9afef48..14c2820 100644 (file)
@@ -2467,7 +2467,7 @@ FinishDlgProc(HWND hwndDlg,
             if (!SetupData->UnattendSetup || !SetupData->DisableGeckoInst)
             {
                 /* Run the Wine Gecko prompt */
-                Control_RunDLLW(hwndDlg, 0, L"appwiz.cpl install_gecko", SW_SHOW);
+                Control_RunDLLW(hwndDlg, 0, L"appwiz.cpl,,install_gecko", SW_SHOW);
             }
 
             /* Set title font */