diff --git a/crates/libs/core/src/imp/factory_cache.rs b/crates/libs/core/src/imp/factory_cache.rs index fd7e5fc287..06e52676ae 100644 --- a/crates/libs/core/src/imp/factory_cache.rs +++ b/crates/libs/core/src/imp/factory_cache.rs @@ -68,18 +68,17 @@ impl FactoryCache { // This is safe because `FactoryCache` only holds agile factory pointers, which are safe to cache and share between threads. unsafe impl Sync for FactoryCache {} -/// Attempts to load the factory object for the given WinRT class. -/// This can be used to access COM interfaces implemented on a Windows Runtime class factory. -pub fn factory() -> crate::Result { - let mut factory: Option = None; - let name = crate::HSTRING::from(C::NAME); - - let code = unsafe { +#[cfg(not(target_vendor = "win7"))] +fn factory_get_com_factory( + name: &crate::HSTRING, + factory: &mut Option, +) -> crate::HRESULT { + unsafe { let mut get_com_factory = || { crate::HRESULT(RoGetActivationFactory( - transmute_copy(&name), + transmute_copy(name), &I::IID as *const _ as _, - &mut factory as *mut _ as *mut _, + factory as *mut _ as *mut _, )) }; let mut code = get_com_factory(); @@ -95,7 +94,68 @@ pub fn factory() -> crate::Result { } code - }; + } +} + +#[cfg(target_vendor = "win7")] +fn factory_get_com_factory( + name: &crate::HSTRING, + factory: &mut Option, +) -> crate::HRESULT { + type CoIncrementMTAUsageDelay = + extern "system" fn(cookie: *mut *mut std::ffi::c_void) -> crate::HRESULT; + type RoGetActivationFactoryDelay = extern "system" fn( + hstring: *mut std::ffi::c_void, + interface: &crate::GUID, + result: *mut *mut std::ffi::c_void, + ) -> crate::HRESULT; + + if let Some(function) = unsafe { + delay_load::( + crate::s!("combase.dll"), + crate::s!("RoGetActivationFactory"), + ) + } { + unsafe { + let mut code = function( + std::mem::transmute_copy(&name), + &I::IID, + factory as *mut _ as *mut _, + ); + + // If RoGetActivationFactory fails because combase hasn't been loaded yet then load combase + // automatically so that it "just works" for apartment-agnostic code. + if code == CO_E_NOTINITIALIZED { + if let Some(mta) = delay_load::( + crate::s!("ole32.dll"), + crate::s!("CoIncrementMTAUsage"), + ) { + let mut cookie = std::ptr::null_mut(); + let _ = mta(&mut cookie); + } + + // Now try a second time to get the activation factory via the OS. + code = function( + std::mem::transmute_copy(&name), + &I::IID, + factory as *mut _ as *mut _, + ); + } + code + } + } else { + // CLASS_E_CLASSNOTAVAILABLE + crate::HRESULT(0x80040111_u32 as _) + } +} + +/// Attempts to load the factory object for the given WinRT class. +/// This can be used to access COM interfaces implemented on a Windows Runtime class factory. +pub fn factory() -> crate::Result { + let mut factory: Option = None; + let name = crate::HSTRING::from(C::NAME); + + let code = factory_get_com_factory(&name, &mut factory); // If this succeeded then return the resulting factory interface. if let Some(factory) = factory { diff --git a/crates/libs/result/src/bindings.rs b/crates/libs/result/src/bindings.rs index f83832ed9d..2b4e0be90f 100644 --- a/crates/libs/result/src/bindings.rs +++ b/crates/libs/result/src/bindings.rs @@ -7,7 +7,9 @@ )] windows_targets::link!("api-ms-win-core-winrt-error-l1-1-0.dll" "system" fn RoOriginateErrorW(error : HRESULT, cchmax : u32, message : PCWSTR) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); +windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); +windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE); @@ -20,6 +22,7 @@ pub type BSTR = *const u16; pub const ERROR_INVALID_DATA: WIN32_ERROR = 13u32; pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32; pub const E_UNEXPECTED: HRESULT = 0x8000FFFF_u32 as _; +pub type FARPROC = Option isize>; pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32; diff --git a/crates/libs/result/src/error.rs b/crates/libs/result/src/error.rs index 9c448bc6e8..6ae4a6a46b 100644 --- a/crates/libs/result/src/error.rs +++ b/crates/libs/result/src/error.rs @@ -297,6 +297,7 @@ mod error_info { } } + #[cfg(not(target_vendor = "win7"))] pub(crate) fn originate_error(code: HRESULT, message: &str) { let message: Vec<_> = message.encode_utf16().collect(); unsafe { @@ -304,6 +305,21 @@ mod error_info { } } + #[cfg(target_vendor = "win7")] + pub(crate) fn originate_error(code: HRESULT, message: &str) { + type RoOriginateErrorWDelay = + extern "system" fn(error: HRESULT, cchmax: u32, message: PCWSTR) -> BOOL; + unsafe { + if let Some(function) = delay_load::( + "api-ms-win-core-winrt-error-l1-1-0.dll\0".as_ptr(), + "RoOriginateErrorW\0".as_ptr(), + ) { + let message: Vec<_> = message.encode_utf16().collect(); + function(code, message.len() as u32, message.as_ptr()); + } + } + } + pub(crate) fn message(&self) -> Option { use crate::bstr::BasicString; @@ -357,6 +373,27 @@ mod error_info { unsafe impl Send for ErrorInfo {} unsafe impl Sync for ErrorInfo {} + + unsafe fn delay_load(library: crate::PCSTR, function: crate::PCSTR) -> Option { + let library = LoadLibraryExA( + library, + core::ptr::null_mut(), + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, + ); + + if library.is_null() { + return None; + } + + let address = GetProcAddress(library, function); + + if address.is_some() { + return Some(core::mem::transmute_copy(&address)); + } + + FreeLibrary(library); + None + } } #[cfg(not(all(windows, not(windows_slim_errors))))]