/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: wowreg.c Abstract: This is the surragate process for registration of 32 dlls from a 64 bit process. And vice-versa. The parent process passes relevent IPC data on the cmdline, and the surragate process then coordinates the registration of this data with the parent process. Author: Andrew Ritz (andrewr) 3-Feb-2000 Revision History: Andrew Ritz (andrewr) 3-Feb-2000 - Created It --*/ #include #include #include #include #include #include #include #include #include #include #include #ifndef UNICODE #error UNICODE assumed #endif #ifndef _WIN64 #include #endif #include "..\unicode\msg.h" #include "memory.h" #include "..\sputils\locking.h" #include "childreg.h" #include "cntxtlog.h" #define DLLINSTALL "DllInstall" #define DLLREGISTER "DllRegisterServer" #define DLLUNREGISTER "DllUnregisterServer" typedef struct _OLE_CONTROL_DATA { LPTSTR FullPath; UINT RegType; PVOID LogContext; BOOL Register; // or unregister LPCTSTR Argument; } OLE_CONTROL_DATA, *POLE_CONTROL_DATA; #if DBG VOID WowRegAssertFailed( IN PSTR FileName, IN UINT LineNumber, IN PSTR Condition ) { int i; CHAR Name[MAX_PATH]; PCHAR p; LPSTR Msg; DWORD MsgLen; DWORD GlobalSetupFlags = pSetupGetGlobalFlags(); // // Use dll name as caption // GetModuleFileNameA(NULL,Name,MAX_PATH); if(p = strrchr(Name,'\\')) { p++; } else { p = Name; } MsgLen = strlen(p)+strlen(FileName)+strlen(Condition)+128; try { Msg = _alloca(MsgLen); wsprintfA( Msg, "Assertion failure at line %u in file %s!%s: %s%s", LineNumber, p, FileName, Condition, (GlobalSetupFlags & PSPGF_NONINTERACTIVE) ? "\r\n" : "\n\nCall DebugBreak()?" ); OutputDebugStringA(Msg); if(GlobalSetupFlags & PSPGF_NONINTERACTIVE) { i = IDYES; } else { i = MessageBoxA( NULL, Msg, p, MB_YESNO | MB_TASKMODAL | MB_ICONSTOP | MB_SETFOREGROUND ); } } except (EXCEPTION_EXECUTE_HANDLER) { OutputDebugStringA("WOWREG32 ASSERT!!!! (out of stack)\r\n"); i=IDYES; } if(i == IDYES) { DebugBreak(); } } #define MYASSERT(x) if(!(x)) { WowRegAssertFailed(__FILE__,__LINE__,#x); } #else #define MYASSERT(x) #endif VOID DebugPrintEx( DWORD Level, PCTSTR format, ... OPTIONAL ) /*++ Routine Description: Send a formatted string to the debugger. Arguments: format - standard printf format string. Return Value: NONE. --*/ { TCHAR buf[1026]; // bigger than max size va_list arglist; va_start(arglist, format); wvsprintf(buf, format, arglist); DbgPrintEx(DPFLTR_SETUP_ID, Level, (PCH)"%ws",buf); } BOOL RegisterUnregisterControl( PWOW_IPC_REGION_TOSURRAGATE pControlDataFromRegion, PDWORD FailureCode); #define IDLE_TIMER 1000*60 // 60 seconds // // Keep statistics... // INT RegisteredControls = 0; PWSTR RegionName; PWSTR SignalReadyEvent; PWSTR SignalCompleteEvent; PWSTR ThisProgramName; #ifndef _WIN64 BOOL Wow64 = FALSE; #endif BOOL ParseArgs( IN int argc, IN PWSTR *argv ) { int i; ThisProgramName = argv[0]; if(argc != 7) { // program name plus 3 required switches and their input return(FALSE); } for (i = 0; i < argc; i++) { if (0 == _wcsicmp(argv[i],SURRAGATE_REGIONNAME_SWITCH)) { RegionName = argv[i+1]; } if (0 == _wcsicmp(argv[i],SURRAGATE_SIGNALREADY_SWITCH)) { SignalReadyEvent = argv[i+1]; } if (0 == _wcsicmp(argv[i],SURRAGATE_SIGNALCOMPLETE_SWITCH)) { SignalCompleteEvent = argv[i+1]; } } if (!SignalCompleteEvent || !SignalReadyEvent || !RegionName) { return(FALSE); } return(TRUE); } void Usage( VOID ) { TCHAR Buffer[2048]; if(LoadString(GetModuleHandle(NULL),IDS_WRONGUSE,Buffer,sizeof(Buffer)/sizeof(TCHAR))) { _ftprintf( stderr,TEXT("%s\n"),Buffer); } } int __cdecl main( IN int argc, IN char *argvA[] ) { BOOL b; PWSTR *argv; HANDLE hReady = NULL; HANDLE hComplete = NULL; HANDLE hFileMap = NULL; PVOID Region = NULL; PWOW_IPC_REGION_TOSURRAGATE pInput; PWOW_IPC_REGION_FROMSURRAGATE pOutput; HANDLE hEvent[1]; DWORD WaitResult, FailureCode; #ifndef _WIN64 { ULONG_PTR ul = 0; NTSTATUS st; st = NtQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &ul, sizeof(ul), NULL); if (NT_SUCCESS(st) && (0 != ul)) { // 32-bit code running on Win64 Wow64 = TRUE; } } #endif // // Assume failure. // b = FALSE; argv = CommandLineToArgvW(GetCommandLine(), &argc); if (!argv) { // // out of memory ? // DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: Low Memory\n"); goto exit; } if(!ParseArgs(argc,argv)) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: Invalid Usage\n"); Usage(); goto exit; } // // open the region and the named events // hFileMap = OpenFileMapping( FILE_MAP_READ| FILE_MAP_WRITE, FALSE, RegionName ); if (!hFileMap) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: OpenFileMapping (%s) failed, ec = %x\n", RegionName, GetLastError()); goto exit; } Region = MapViewOfFile( hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0 ); if (!Region) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: MapViewOfFile failed, ec = %x\n", GetLastError()); goto exit; } hReady = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE,FALSE, SignalReadyEvent); if (!hReady) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: OpenEvent (%s) failed, ec = %x\n", SignalReadyEvent, GetLastError()); goto exit; } hComplete = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE,FALSE, SignalCompleteEvent); if (!hComplete) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"WOWREG32: OpenEvent (%s) failed, ec = %x\n", SignalCompleteEvent, GetLastError()); goto exit; } pInput = (PWOW_IPC_REGION_TOSURRAGATE) Region; pOutput = (PWOW_IPC_REGION_FROMSURRAGATE) Region; // // the process is now initialized. we now wait for either our event to be // signalled or for our idle timer to fire, in which case we will exit the // program // hEvent[0] = hReady; while (1) { do { WaitResult = MsgWaitForMultipleObjectsEx( 1, &hEvent[0], IDLE_TIMER, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); if (WaitResult == WAIT_OBJECT_0 + 1) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } while(WaitResult != WAIT_TIMEOUT && WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_FAILED); if (WaitResult == WAIT_TIMEOUT) { // // we hit the idle timer, so let the process unwind and go away now. // // // it doesn't matter too much, but make the return code "false" if the // process has gone away without ever registering any controls. // b = (RegisteredControls != 0); break; } MYASSERT(WaitResult == WAIT_OBJECT_0); // // reset our event so we only process each control one time // ResetEvent(hReady); // // register the control // b = RegisterUnregisterControl(pInput,&FailureCode); #ifdef PRERELEASE if (!b) { DebugPrintEx( DPFLTR_ERROR_LEVEL, L"First call to register control failed, trying again.\n" L"If you have a debugger attached to this process, it will" L" now call DebugBreak() so that you can debug this registration failure\n" ); if (IsDebuggerPresent()) { DebugBreak(); } b = RegisterUnregisterControl(pInput,&FailureCode); } #endif // // write an output status. Note that you cannot access pInput after // this point because pOutput and pInput are overloaded views of the // same memory region. // pOutput->Win32Error = b ? ERROR_SUCCESS : GetLastError(); pOutput->FailureCode= b ? SPREG_SUCCESS : FailureCode; // // set an event to tell the parent process to read our status and give // us the next control to register. // SetEvent(hComplete); if (b) { RegisteredControls += 1; } } fprintf( stdout,"Registered %d controls\n", RegisteredControls ); DebugPrintEx( DPFLTR_INFO_LEVEL, L"WOWREG32: registered %d controls\n", RegisteredControls); exit: if (Region) { UnmapViewOfFile( Region ); } if (hFileMap) { CloseHandle(hFileMap); } if (hReady) { CloseHandle(hReady); } if (hComplete) { CloseHandle(hComplete); } return(b); } DWORD pSetupRegisterDllInstall( IN POLE_CONTROL_DATA OleControlData, IN HMODULE ControlDll, IN PDWORD ExtendedStatus ) /*++ Routine Description: call the "DllInstall" entrypoint for the specified dll Arguments: OleControlData - pointer to the OLE_CONTROL_DATA structure for the dll to be registered ControlDll - module handle to the dll to be registered ExtendedStatus - receives updated SPREG_* flag indicating outcome Return Value: Win32 error code indicating outcome. --*/ { LPEXCEPTION_POINTERS ExceptionPointers = NULL; HRESULT (__stdcall *InstallRoutine) (BOOL bInstall, LPCTSTR pszCmdLine); HRESULT InstallStatus; DWORD d = NO_ERROR; // // parameter validation // if (!ControlDll) { *ExtendedStatus = SPREG_UNKNOWN; return ERROR_INVALID_PARAMETER; } // // get function pointer to "DllInstall" entrypoint // InstallRoutine = NULL; // shut up PreFast try { (FARPROC)InstallRoutine = GetProcAddress( ControlDll, DLLINSTALL ); } except ( ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) { } if(ExceptionPointers) { // // something went wrong...record an error // d = ExceptionPointers->ExceptionRecord->ExceptionCode; WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR, MSG_LOG_OLE_CONTROL_INTERNAL_EXCEPTION, NULL, OleControlData->FullPath ); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...exception in GetProcAddress handled\n"); *ExtendedStatus = SPREG_GETPROCADDR; } else if(InstallRoutine) { // // now call the function // DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: installing...\n"); *ExtendedStatus = SPREG_DLLINSTALL; try { InstallStatus = InstallRoutine(OleControlData->Register, OleControlData->Argument); if(FAILED(InstallStatus)) { d = InstallStatus; WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR|SETUP_LOG_BUFFER, MSG_LOG_OLE_CONTROL_API_FAILED, NULL, OleControlData->FullPath, TEXT(DLLINSTALL) ); WriteLogError(OleControlData->LogContext, SETUP_LOG_ERROR, d); } else if(InstallStatus) { WriteLogEntry(OleControlData->LogContext, SETUP_LOG_WARNING, MSG_LOG_OLE_CONTROL_API_WARN, NULL, OleControlData->FullPath, TEXT(DLLINSTALL), InstallStatus ); } else { WriteLogEntry( OleControlData->LogContext, SETUP_LOG_VERBOSE, MSG_LOG_OLE_CONTROL_API_OK, NULL, OleControlData->FullPath, TEXT(DLLINSTALL) ); } } except ( ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) { d = ExceptionPointers->ExceptionRecord->ExceptionCode; WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR, MSG_LOG_OLE_CONTROL_API_EXCEPTION, NULL, OleControlData->FullPath, TEXT(DLLINSTALL) ); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...exception in DllInstall handled\n"); } DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...installed\n"); } else { *ExtendedStatus = SPREG_GETPROCADDR; } return d; } DWORD pSetupRegisterDllRegister( IN POLE_CONTROL_DATA OleControlData, IN HMODULE ControlDll, IN PDWORD ExtendedStatus ) /*++ Routine Description: call the "DllRegisterServer" or "DllUnregisterServer" entrypoint for the specified dll Arguments: OleControlData - contains data about dll to be registered ControlDll - module handle to the dll to be registered ExtendedStatus - receives an extended status depending on the outcome of this operation Return Value: Win32 error code indicating outcome. --*/ { LPEXCEPTION_POINTERS ExceptionPointers = NULL; HRESULT (__stdcall *RegisterRoutine) (VOID); HRESULT RegisterStatus; DWORD d = NO_ERROR; // // parameter validation // if (!ControlDll) { return ERROR_INVALID_PARAMETER; } // // get the function pointer to the actual routine we want to call // RegisterRoutine = NULL; // shut up preFast try { (FARPROC)RegisterRoutine = GetProcAddress( ControlDll, OleControlData->Register ? DLLREGISTER : DLLUNREGISTER); } except ( ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) { } if(ExceptionPointers) { // // something went wrong, horribly wrong // d = ExceptionPointers->ExceptionRecord->ExceptionCode; WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR, MSG_LOG_OLE_CONTROL_INTERNAL_EXCEPTION, NULL, OleControlData->FullPath ); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...exception in GetProcAddress handled\n"); *ExtendedStatus = SPREG_GETPROCADDR; } else if(RegisterRoutine) { DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: registering...\n"); *ExtendedStatus = SPREG_REGSVR; try { RegisterStatus = RegisterRoutine(); if(FAILED(RegisterStatus)) { d = RegisterStatus; WriteLogEntry(OleControlData->LogContext, SETUP_LOG_ERROR | SETUP_LOG_BUFFER, MSG_LOG_OLE_CONTROL_API_FAILED, NULL, OleControlData->FullPath, OleControlData->Register ? TEXT(DLLREGISTER) : TEXT(DLLUNREGISTER) ); WriteLogError(OleControlData->LogContext, SETUP_LOG_ERROR, d); } else if(RegisterStatus) { WriteLogEntry(OleControlData->LogContext, SETUP_LOG_WARNING, MSG_LOG_OLE_CONTROL_API_WARN, NULL, OleControlData->FullPath, OleControlData->Register ? TEXT(DLLREGISTER) : TEXT(DLLUNREGISTER), RegisterStatus ); } else { WriteLogEntry(OleControlData->LogContext, SETUP_LOG_VERBOSE, MSG_LOG_OLE_CONTROL_API_OK, NULL, OleControlData->FullPath, OleControlData->Register ? TEXT(DLLREGISTER) : TEXT(DLLUNREGISTER) ); } } except ( ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) { d = ExceptionPointers->ExceptionRecord->ExceptionCode; WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR, MSG_LOG_OLE_CONTROL_API_EXCEPTION, NULL, OleControlData->FullPath, OleControlData->Register ? TEXT(DLLREGISTER) : TEXT(DLLUNREGISTER) ); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...exception in DllRegisterServer handled\n"); } DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...registered\n"); } else { d = GetLastError(); WriteLogEntry(OleControlData->LogContext, SETUP_LOG_ERROR | SETUP_LOG_BUFFER, MSG_LOG_OLE_CONTROL_NOT_REGISTERED_GETPROC_FAILED, NULL, OleControlData->FullPath, OleControlData->Register ? TEXT(DLLREGISTER) : TEXT(DLLUNREGISTER) ); WriteLogError(OleControlData->LogContext, SETUP_LOG_ERROR, d); *ExtendedStatus = SPREG_GETPROCADDR; } return d; } DWORD pSetupRegisterLoadDll( IN POLE_CONTROL_DATA OleControlData, OUT HMODULE *ControlDll ) /*++ Routine Description: get the module handle to the specified dll Arguments: OleControlData - contains path to dll to be loaded ControlDll - module handle for the dll Return Value: Win32 error code indicating outcome. --*/ { LPEXCEPTION_POINTERS ExceptionPointers = NULL; DWORD d = NO_ERROR; DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: loading dll...\n"); #ifndef _WIN64 if(Wow64) { // // don't remap directory the directory that the caller provided // Wow64DisableFilesystemRedirector(OleControlData->FullPath); } #endif try { *ControlDll = LoadLibrary(OleControlData->FullPath); } except ( ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) { } #ifndef _WIN64 if(Wow64) { // // re-enable the redirection on this file // Wow64EnableFilesystemRedirector(); } #endif if(ExceptionPointers) { WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR, MSG_LOG_OLE_CONTROL_LOADLIBRARY_EXCEPTION, NULL, OleControlData->FullPath ); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...exception in LoadLibrary handled\n"); d = ExceptionPointers->ExceptionRecord->ExceptionCode; } else if (!*ControlDll) { d = GetLastError(); // // LoadLibrary failed. // File not found is not an error. We want to know about // other errors though. // d = GetLastError(); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...dll not loaded (%u)\n",d); WriteLogEntry( OleControlData->LogContext, SETUP_LOG_ERROR|SETUP_LOG_BUFFER, MSG_LOG_OLE_CONTROL_LOADLIBRARY_FAILED, NULL, OleControlData->FullPath ); WriteLogError( OleControlData->LogContext, SETUP_LOG_ERROR, d ); } else { DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: ...dll loaded\n"); } return d; } BOOL RegisterUnregisterControl( PWOW_IPC_REGION_TOSURRAGATE RegistrationData, PDWORD FailureCode ) /*++ Routine Description: main registration routine for registering a dll. Arguments: RegistrationData - pointer to WOW_IPC_REGION_TOSURRAGATE structure indicating file to be processed FailureCode - SPREG_* code indicating outcome of operation. Return Value: Win32 error code indicating outcome. --*/ { LPEXCEPTION_POINTERS ExceptionPointers = NULL; HMODULE ControlDll = NULL; PTSTR Extension; DWORD d = NO_ERROR; DWORD Count; OLE_CONTROL_DATA OleControlData; WCHAR Path[MAX_PATH]; PWSTR p; // // could use CoInitializeEx as an optimization as OleInitialize is // probably overkill...but this is probably just a perf hit at // worst // DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: calling OleInitialize\n"); OleControlData.FullPath = RegistrationData->FullPath; OleControlData.Argument = RegistrationData->Argument; OleControlData.LogContext = NULL; OleControlData.Register = RegistrationData->Register; OleControlData.RegType = RegistrationData->RegType; wcscpy(Path,RegistrationData->FullPath); p = wcsrchr(Path,'\\'); if (p) { *p = L'\0'; } SetCurrentDirectory( Path ); d = (DWORD)OleInitialize(NULL); if (d != NO_ERROR) { *FailureCode = SPREG_UNKNOWN; DebugPrintEx(DPFLTR_ERROR_LEVEL,L"WOWREG32: OleInitialize failed, ec = 0x%08x\n", d); goto clean0; } DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: back from OleInitialize\n"); try { // // protect everything in TRY-EXCEPT, we're calling unknown code (DLL's) // d = pSetupRegisterLoadDll( &OleControlData, &ControlDll ); if (d == NO_ERROR) { // // We successfully loaded it. Now call the appropriate routines. // // // On register, do DLLREGISTER, then DLLINSTALL // On unregister, do DLLINSTALL, then DLLREGISTER // if (OleControlData.Register) { if (OleControlData.RegType & FLG_REGSVR_DLLREGISTER && (d == NO_ERROR) ) { d = pSetupRegisterDllRegister( &OleControlData, ControlDll, FailureCode ); } if (OleControlData.RegType & FLG_REGSVR_DLLINSTALL && (d == NO_ERROR) ) { d = pSetupRegisterDllInstall( &OleControlData, ControlDll, FailureCode ); } } else { if (OleControlData.RegType & FLG_REGSVR_DLLINSTALL && (d == NO_ERROR) ) { d = pSetupRegisterDllInstall( &OleControlData, ControlDll, FailureCode ); } if (OleControlData.RegType & FLG_REGSVR_DLLREGISTER && (d == NO_ERROR) ) { d = pSetupRegisterDllRegister( &OleControlData, ControlDll, FailureCode ); } } } else { ControlDll = NULL; *FailureCode = SPREG_LOADLIBRARY; } } except(EXCEPTION_EXECUTE_HANDLER) { // // If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was // an inpage error dealing with a mapped-in file. // d = ERROR_INVALID_DATA; *FailureCode = SPREG_UNKNOWN; } if (ControlDll) { FreeLibrary(ControlDll); } OleUninitialize(); DebugPrintEx(DPFLTR_TRACE_LEVEL,L"WOWREG32: back from OleUninitialize, exit RegisterUnregisterDll\n"); clean0: if (d == NO_ERROR) { *FailureCode = SPREG_SUCCESS; } SetLastError(d); return (d == NO_ERROR); }