Skip to content

Commit

Permalink
Restore support for win7
Browse files Browse the repository at this point in the history
  • Loading branch information
Guiguiprim committed Aug 19, 2024
1 parent db06b51 commit 0d1abf8
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 10 deletions.
80 changes: 70 additions & 10 deletions crates/libs/core/src/imp/factory_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,17 @@ impl<C: crate::RuntimeName, I: Interface> FactoryCache<C, I> {
// This is safe because `FactoryCache` only holds agile factory pointers, which are safe to cache and share between threads.
unsafe impl<C, I> Sync for FactoryCache<C, I> {}

/// 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<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
let mut factory: Option<I> = None;
let name = crate::HSTRING::from(C::NAME);

let code = unsafe {
#[cfg(not(target_vendor = "win7"))]
fn factory_get_com_factory<I: Interface>(
name: &crate::HSTRING,
factory: &mut Option<I>,
) -> 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();
Expand All @@ -95,7 +94,68 @@ pub fn factory<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
}

code
};
}
}

#[cfg(target_vendor = "win7")]
fn factory_get_com_factory<I: Interface>(
name: &crate::HSTRING,
factory: &mut Option<I>,
) -> 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::<RoGetActivationFactoryDelay>(
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::<CoIncrementMTAUsageDelay>(
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<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
let mut factory: Option<I> = 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 {
Expand Down
3 changes: 3 additions & 0 deletions crates/libs/result/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<unsafe extern "system" fn() -> 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;
Expand Down
37 changes: 37 additions & 0 deletions crates/libs/result/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,29 @@ 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 {
RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr());
}
}

#[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::<RoOriginateErrorWDelay>(
"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<String> {
use crate::bstr::BasicString;

Expand Down Expand Up @@ -357,6 +373,27 @@ mod error_info {

unsafe impl Send for ErrorInfo {}
unsafe impl Sync for ErrorInfo {}

unsafe fn delay_load<T>(library: crate::PCSTR, function: crate::PCSTR) -> Option<T> {
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))))]
Expand Down

0 comments on commit 0d1abf8

Please sign in to comment.