From: Vincent Povirk Subject: [PATCH] regsvr32: Re-exec as 32-bit or 64-bit if necessary. Message-Id: <20191018204634.24398-1-vincent@codeweavers.com> Date: Fri, 18 Oct 2019 15:46:34 -0500 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47075 Signed-off-by: Vincent Povirk --- programs/regsvr32/regsvr32.c | 122 +++++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 11 deletions(-) diff --git a/programs/regsvr32/regsvr32.c b/programs/regsvr32/regsvr32.c index 4aa3f7d5c1f..a7fb12e0fc1 100644 --- a/programs/regsvr32/regsvr32.c +++ b/programs/regsvr32/regsvr32.c @@ -83,6 +83,97 @@ static void WINAPIV output_write(UINT id, ...) LocalFree(str); } +static LPCWSTR find_arg_start(LPCWSTR cmdline) +{ + LPCWSTR s; + BOOL in_quotes; + int bcount; + + bcount=0; + in_quotes=FALSE; + s=cmdline; + while (1) { + if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) { + /* end of this command line argument */ + break; + } else if (*s=='\\') { + /* '\', count them */ + bcount++; + } else if ((*s=='"') && ((bcount & 1)==0)) { + /* unescaped '"' */ + in_quotes=!in_quotes; + bcount=0; + } else { + /* a regular character */ + bcount=0; + } + s++; + } + return s; +} + +static void reexec_self(void) +{ + /* restart current process as 32-bit or 64-bit with same command line */ + static const WCHAR exe_name[] = {'\\','r','e','g','s','v','r','3','2','.','e','x','e',0}; +#ifndef _WIN64 + static const WCHAR sysnative[] = {'\\','S','y','s','N','a','t','i','v','e',0}; + BOOL wow64; +#endif + WCHAR systemdir[MAX_PATH]; + LPCWSTR args; + WCHAR *cmdline; + STARTUPINFOW si = {0}; + PROCESS_INFORMATION pi; + +#ifdef _WIN64 + TRACE("restarting as 32-bit\n"); + GetSystemWow64DirectoryW(systemdir, MAX_PATH); +#else + TRACE("restarting as 64-bit\n"); + + if (!IsWow64Process(GetCurrentProcess(), &wow64) || !wow64) + { + TRACE("not running in wow64, can't restart as 64-bit\n"); + return; + } + + GetWindowsDirectoryW(systemdir, MAX_PATH); + wcscat(systemdir, sysnative); +#endif + + args = find_arg_start(GetCommandLineW()); + + cmdline = HeapAlloc(GetProcessHeap(), 0, + (wcslen(systemdir)+wcslen(exe_name)+wcslen(args)+1)*sizeof(WCHAR)); + + wcscpy(cmdline, systemdir); + wcscat(cmdline, exe_name); + wcscat(cmdline, args); + + si.cb = sizeof(si); + + if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + DWORD exit_code; + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &exit_code); + ExitProcess(exit_code); + } + else + { + WINE_TRACE("failed to restart, err=%d\n", GetLastError()); + } + + HeapFree(GetProcessHeap(), 0, cmdline); +} + +#ifdef _WIN64 +# define ALT_BINARY_TYPE SCS_32BIT_BINARY +#else +# define ALT_BINARY_TYPE SCS_64BIT_BINARY +#endif + /** * Loads procedure. * @@ -90,14 +181,22 @@ static void WINAPIV output_write(UINT id, ...) * strDll - name of the dll. * procName - name of the procedure to load from the dll. * DllHandle - a variable that receives the handle of the loaded dll. + * firstDll - true if this is the first dll in the command line. */ -static VOID *LoadProc(const WCHAR* strDll, const char* procName, HMODULE* DllHandle) +static VOID *LoadProc(const WCHAR* strDll, const char* procName, HMODULE* DllHandle, BOOL firstDll) { VOID* (*proc)(void); *DllHandle = LoadLibraryExW(strDll, 0, LOAD_WITH_ALTERED_SEARCH_PATH); if(!*DllHandle) { + DWORD binary_type; + if (firstDll && GetLastError() == ERROR_BAD_EXE_FORMAT && + GetBinaryTypeW(strDll, &binary_type) && + binary_type == ALT_BINARY_TYPE) + { + reexec_self(); + } output_write(STRING_DLL_LOAD_FAILED, strDll); ExitProcess(LOADLIBRARY_FAILED); } @@ -111,13 +210,13 @@ static VOID *LoadProc(const WCHAR* strDll, const char* procName, HMODULE* DllHan return proc; } -static int RegisterDll(const WCHAR* strDll) +static int RegisterDll(const WCHAR* strDll, BOOL firstDll) { HRESULT hr; DLLREGISTER pfRegister; HMODULE DllHandle = NULL; - pfRegister = LoadProc(strDll, "DllRegisterServer", &DllHandle); + pfRegister = LoadProc(strDll, "DllRegisterServer", &DllHandle, firstDll); if (!pfRegister) return GETPROCADDRESS_FAILED; @@ -134,13 +233,13 @@ static int RegisterDll(const WCHAR* strDll) return 0; } -static int UnregisterDll(const WCHAR* strDll) +static int UnregisterDll(const WCHAR* strDll, BOOL firstDll) { HRESULT hr; DLLUNREGISTER pfUnregister; HMODULE DllHandle = NULL; - pfUnregister = LoadProc(strDll, "DllUnregisterServer", &DllHandle); + pfUnregister = LoadProc(strDll, "DllUnregisterServer", &DllHandle, firstDll); if (!pfUnregister) return GETPROCADDRESS_FAILED; @@ -157,13 +256,13 @@ static int UnregisterDll(const WCHAR* strDll) return 0; } -static int InstallDll(BOOL install, const WCHAR *strDll, const WCHAR *command_line) +static int InstallDll(BOOL install, const WCHAR *strDll, const WCHAR *command_line, BOOL firstDll) { HRESULT hr; DLLINSTALL pfInstall; HMODULE DllHandle = NULL; - pfInstall = LoadProc(strDll, "DllInstall", &DllHandle); + pfInstall = LoadProc(strDll, "DllInstall", &DllHandle, firstDll); if (!pfInstall) return GETPROCADDRESS_FAILED; @@ -278,11 +377,12 @@ int __cdecl wmain(int argc, WCHAR* argv[]) if (argv[i]) { WCHAR *DllName = argv[i]; + BOOL firstDll = !DllFound; res = 0; DllFound = TRUE; if (CallInstall && Unregister) - res = InstallDll(!Unregister, DllName, wsCommandLine); + res = InstallDll(!Unregister, DllName, wsCommandLine, firstDll); /* The Windows version stops processing the current file on the first error. */ if (res) @@ -294,9 +394,9 @@ int __cdecl wmain(int argc, WCHAR* argv[]) if (!CallInstall || CallRegister) { if(Unregister) - res = UnregisterDll(DllName); + res = UnregisterDll(DllName, firstDll); else - res = RegisterDll(DllName); + res = RegisterDll(DllName, firstDll); } if (res) @@ -306,7 +406,7 @@ int __cdecl wmain(int argc, WCHAR* argv[]) } if (CallInstall && !Unregister) - res = InstallDll(!Unregister, DllName, wsCommandLine); + res = InstallDll(!Unregister, DllName, wsCommandLine, firstDll); if (res) { -- 2.17.1