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);