Skip to content

Restore support for win7 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
38 changes: 38 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,28 @@ mod error_info {

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

#[cfg(target_vendor = "win7")]
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
2 changes: 2 additions & 0 deletions crates/tools/bindings/src/result.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Windows.Win32.Foundation.E_UNEXPECTED
Windows.Win32.Foundation.ERROR_INVALID_DATA
Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION
Windows.Win32.Foundation.FreeLibrary
Windows.Win32.Foundation.GetLastError
Windows.Win32.Foundation.SysFreeString
Windows.Win32.Foundation.SysStringLen
Expand All @@ -16,6 +17,7 @@
Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM
Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS
Windows.Win32.System.Diagnostics.Debug.FormatMessageW
Windows.Win32.System.LibraryLoader.GetProcAddress
Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
Windows.Win32.System.LibraryLoader.LoadLibraryExA
Windows.Win32.System.Memory.GetProcessHeap
Expand Down
Loading