Signed-off-by: Vincent Povirk vincent@codeweavers.com --- programs/rundll32/Makefile.in | 2 +- programs/rundll32/rundll32.c | 162 ++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-)
diff --git a/programs/rundll32/Makefile.in b/programs/rundll32/Makefile.in index 6ab5eda37e..adc586ca4f 100644 --- a/programs/rundll32/Makefile.in +++ b/programs/rundll32/Makefile.in @@ -1,5 +1,5 @@ MODULE = rundll32.exe -IMPORTS = user32 +IMPORTS = shlwapi user32
EXTRADLLFLAGS = -mwindows -municode -mno-cygwin
diff --git a/programs/rundll32/rundll32.c b/programs/rundll32/rundll32.c index 3d4a4687e2..ae925b50a4 100644 --- a/programs/rundll32/rundll32.c +++ b/programs/rundll32/rundll32.c @@ -38,6 +38,7 @@ #include "windows.h" #include "wine/winbase16.h" #include "wine/debug.h" +#include "shlwapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(rundll32);
@@ -239,6 +240,149 @@ static LPWSTR get_next_arg(LPWSTR *cmdline) return arg; }
+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 BOOL reexec_self(BOOL as_64bit) +{ + static const WCHAR exe_name[] = {'\','r','u','n','d','l','l','3','2','.','e','x','e',0}; + static const WCHAR sysnative[] = {'\','S','y','s','N','a','t','i','v','e',0}; + WCHAR systemdir[MAX_PATH]; + LPCWSTR args; + WCHAR *cmdline; + STARTUPINFOW si = {0}; + PROCESS_INFORMATION pi; + BOOL result; + + TRACE("restarting %s\n", as_64bit ? "as 64-bit" : "as 32-bit"); + + if (as_64bit) + { + GetWindowsDirectoryW(systemdir, MAX_PATH); + wcscat(systemdir, sysnative); + } + else + { + GetSystemWow64DirectoryW(systemdir, MAX_PATH); + } + + 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)) + { + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + result = TRUE; + } + else + { + WINE_TRACE("failed to restart, err=%d\n", GetLastError()); + result = FALSE; + } + + HeapFree(GetProcessHeap(), 0, cmdline); + + return result; +} + +static WORD get_image_type(LPCWSTR image_name) +{ + HANDLE file; + IMAGE_DOS_HEADER dos_header; + IMAGE_NT_HEADERS nt_headers; + DWORD bytes_read; + WORD result; + + if (PathIsRelativeW(image_name)) + { + /* There's no reliable way to find the actual file. */ + return 0; + } + + file = CreateFileW(image_name, GENERIC_READ, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (file == INVALID_HANDLE_VALUE) + { + WINE_TRACE("unable to open %s for reading, err=%d\n", wine_dbgstr_wn(image_name, -1), GetLastError()); + return 0; + } + + if (!ReadFile(file, &dos_header, sizeof(dos_header), &bytes_read, NULL) || + bytes_read != sizeof(dos_header) || + dos_header.e_magic != IMAGE_DOS_SIGNATURE) + { + WINE_TRACE("unable to read dos header from %s\n", wine_dbgstr_wn(image_name, -1)); + CloseHandle(file); + return 0; + } + + SetFilePointer(file, dos_header.e_lfanew, NULL, FILE_BEGIN); + + if (!ReadFile(file, &nt_headers, sizeof(nt_headers), &bytes_read, NULL) || + bytes_read < 6) /* enough to read magic and machine architecture */ + { + WINE_TRACE("unable to read extended header from %s\n", wine_dbgstr_wn(image_name, -1)); + CloseHandle(file); + return 0; + } + + if (((IMAGE_OS2_HEADER*)&nt_headers)->ne_magic == IMAGE_OS2_SIGNATURE) + { + WINE_TRACE("%s is an NE (16-bit) image\n", wine_dbgstr_wn(image_name, -1)); + result = IMAGE_OS2_SIGNATURE; + } + else if (nt_headers.Signature == IMAGE_NT_SIGNATURE) + { + result = nt_headers.FileHeader.Machine; + WINE_TRACE("%s is a PE image with machine 0x%x\n", wine_dbgstr_wn(image_name, -1), result); + } + else + { + WINE_TRACE("%s is an image with an unrecognized extended header\n", wine_dbgstr_wn(image_name, -1)); + result = 0; + } + + CloseHandle(file); + + return result; +} + int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine, int nCmdShow) { HWND hWnd; @@ -247,6 +391,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine BOOL unicode = FALSE, win16; STARTUPINFOW info; HMODULE hDll; + WORD dll_type;
hWnd=NULL; hDll=NULL; @@ -270,6 +415,23 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE hOldInstance, LPWSTR szCmdLine szEntryPoint = get_next_arg(&szCmdLine); WINE_TRACE("EntryPoint=%s\n",wine_dbgstr_w(szEntryPoint));
+ /* Figure out what type of dll it is. */ + dll_type = get_image_type(szDllName); + + /* Re-exec with another architecture if necessary. */ +#ifdef __i386__ + if (dll_type == IMAGE_FILE_MACHINE_AMD64) { + BOOL is_wow64; + if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64 && reexec_self(TRUE)) + goto CLEANUP; + } +#endif +#ifdef __x86_64__ + if ((dll_type == IMAGE_FILE_MACHINE_I386 || dll_type == IMAGE_OS2_SIGNATURE) && + reexec_self(FALSE)) + goto CLEANUP; +#endif + /* Load the library */ hDll=LoadLibraryW(szDllName); if (hDll)
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47075 Signed-off-by: Vincent Povirk vincent@codeweavers.com --- programs/regsvr32/Makefile.in | 2 +- programs/regsvr32/regsvr32.c | 158 ++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-)
diff --git a/programs/regsvr32/Makefile.in b/programs/regsvr32/Makefile.in index d8aee0ea66..51a03bf3a2 100644 --- a/programs/regsvr32/Makefile.in +++ b/programs/regsvr32/Makefile.in @@ -1,5 +1,5 @@ MODULE = regsvr32.exe -IMPORTS = ole32 user32 +IMPORTS = ole32 shlwapi user32
EXTRADLLFLAGS = -mwindows -municode -mno-cygwin
diff --git a/programs/regsvr32/regsvr32.c b/programs/regsvr32/regsvr32.c index 77a839e475..85c539309a 100644 --- a/programs/regsvr32/regsvr32.c +++ b/programs/regsvr32/regsvr32.c @@ -25,6 +25,7 @@ #include <windows.h> #include <ole2.h> #include "regsvr32.h" +#include "shlwapi.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(regsvr32); @@ -211,6 +212,145 @@ static WCHAR *parse_command_line(WCHAR *command_line) return NULL; }
+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(BOOL as_64bit) +{ + static const WCHAR exe_name[] = {'\','r','e','g','s','v','r','3','2','.','e','x','e',0}; + static const WCHAR sysnative[] = {'\','S','y','s','N','a','t','i','v','e',0}; + WCHAR systemdir[MAX_PATH]; + LPCWSTR args; + WCHAR *cmdline; + STARTUPINFOW si = {0}; + PROCESS_INFORMATION pi; + + TRACE("restarting %s\n", as_64bit ? "as 64-bit" : "as 32-bit"); + + if (as_64bit) + { + GetWindowsDirectoryW(systemdir, MAX_PATH); + wcscat(systemdir, sysnative); + } + else + { + GetSystemWow64DirectoryW(systemdir, MAX_PATH); + } + + 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); +} + +static WORD get_image_type(LPCWSTR image_name) +{ + HANDLE file; + IMAGE_DOS_HEADER dos_header; + IMAGE_NT_HEADERS nt_headers; + DWORD bytes_read; + WORD result; + + if (PathIsRelativeW(image_name)) + { + /* There's no reliable way to find the actual file. */ + return 0; + } + + file = CreateFileW(image_name, GENERIC_READ, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (file == INVALID_HANDLE_VALUE) + { + WINE_TRACE("unable to open %s for reading, err=%d\n", wine_dbgstr_wn(image_name, -1), GetLastError()); + return 0; + } + + if (!ReadFile(file, &dos_header, sizeof(dos_header), &bytes_read, NULL) || + bytes_read != sizeof(dos_header) || + dos_header.e_magic != IMAGE_DOS_SIGNATURE) + { + WINE_TRACE("unable to read dos header from %s\n", wine_dbgstr_wn(image_name, -1)); + CloseHandle(file); + return 0; + } + + SetFilePointer(file, dos_header.e_lfanew, NULL, FILE_BEGIN); + + if (!ReadFile(file, &nt_headers, sizeof(nt_headers), &bytes_read, NULL) || + bytes_read < 6) /* enough to read magic and machine architecture */ + { + WINE_TRACE("unable to read extended header from %s\n", wine_dbgstr_wn(image_name, -1)); + CloseHandle(file); + return 0; + } + + if (((IMAGE_OS2_HEADER*)&nt_headers)->ne_magic == IMAGE_OS2_SIGNATURE) + { + WINE_TRACE("%s is an NE (16-bit) image\n", wine_dbgstr_wn(image_name, -1)); + result = IMAGE_OS2_SIGNATURE; + } + else if (nt_headers.Signature == IMAGE_NT_SIGNATURE) + { + result = nt_headers.FileHeader.Machine; + WINE_TRACE("%s is a PE image with machine 0x%x\n", wine_dbgstr_wn(image_name, -1), result); + } + else + { + WINE_TRACE("%s is an image with an unrecognized extended header\n", wine_dbgstr_wn(image_name, -1)); + result = 0; + } + + CloseHandle(file); + + return result; +} + int wmain(int argc, WCHAR* argv[]) { int i, res, ret = 0; @@ -280,6 +420,24 @@ int wmain(int argc, WCHAR* argv[]) WCHAR *DllName = argv[i]; res = 0;
+ if (!DllFound) + { + /* Re-exec with another architecture if necessary. */ + WORD dll_type = get_image_type(DllName); + +#ifdef __i386__ + if (dll_type == IMAGE_FILE_MACHINE_AMD64) { + BOOL is_wow64; + if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64) + reexec_self(TRUE); + } +#endif +#ifdef __x86_64__ + if (dll_type == IMAGE_FILE_MACHINE_I386) + reexec_self(FALSE); +#endif + } + DllFound = TRUE; if (CallInstall && Unregister) res = InstallDll(!Unregister, DllName, wsCommandLine);
Vincent Povirk vincent@codeweavers.com writes:
- /* Figure out what type of dll it is. */
- dll_type = get_image_type(szDllName);
- /* Re-exec with another architecture if necessary. */
+#ifdef __i386__
- if (dll_type == IMAGE_FILE_MACHINE_AMD64) {
BOOL is_wow64;
if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64 && reexec_self(TRUE))
goto CLEANUP;
- }
+#endif +#ifdef __x86_64__
- if ((dll_type == IMAGE_FILE_MACHINE_I386 || dll_type == IMAGE_OS2_SIGNATURE) &&
reexec_self(FALSE))
goto CLEANUP;
+#endif
You should probably use GetBinaryType(), so that you don't need CPU-specific checks. Also I'd suggest doing this only when loading the dll has failed.