feat(webview2): update WebView2 SDK and enhance Windows 7 compatibility
- Updated the WebView2 SDK version to 1.0.1020.30 and adjusted related SHA256 checksums for improved compatibility with Windows 7. - Added a new function to install the legacy WebView2 loader, ensuring proper handling of loader replacements and validation. - Enhanced the CI workflow to include steps for enabling Win7-only Cargo patches and verifying the availability of 7-Zip, streamlining the build process for Windows 7 compatibility. - Introduced a new documentation file to record Windows 7 compatibility vendor patches, detailing necessary adjustments for specific dependencies.
This commit is contained in:
1 parent
d223d24db1
commit
94bd7c8758
68 files changed
+49273
-16
No files matched your search
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
# Windows 7 Compatibility Vendor Patches
|
||||
|
||||
This file records dependency patches that are only present because the isolated
|
||||
`x86_64-win7-windows-msvc` compatibility build found a concrete blocker.
|
||||
|
||||
## windows-core 0.62.2
|
||||
|
||||
- Source: crates.io `windows-core` 0.62.2.
|
||||
- Reason: the Win7 PE import audit found `combase.dll` imports from the
|
||||
`windows 0.62.2` dependency group used by Pageant, SSPI, and IronRDP.
|
||||
- Patch: link `CoTaskMemAlloc`, `CoTaskMemFree`, and
|
||||
`CoCreateFreeThreadedMarshaler` from `ole32.dll`, matching the Win7-safe
|
||||
import library used by `windows-core` 0.61.x.
|
||||
- Scope: no normal release workflow is changed; this patch is exercised through
|
||||
Cargo's `[patch.crates-io]` resolution and must remain validated by the
|
||||
`.github/workflows/windows-7-compat.yml` PE audit.
|
||||
|
||||
## webview2-com-sys 0.38.2 / WebView2 Loader SDK 1.0.1020.30
|
||||
|
||||
- Source: crates.io `webview2-com-sys` 0.38.2 plus NuGet
|
||||
`Microsoft.Web.WebView2` 1.0.1020.30 for the x64 static loader.
|
||||
- Reason: the Win7 PE import audit found `EventSetInformation` in newer
|
||||
WebView2 static loaders. The original 1.0.1054.31 loader pin still contains
|
||||
that import; 1.0.1020.30 is the newest stable SDK version checked below that
|
||||
boundary without the symbol.
|
||||
- Patch: the crate is vendored so raw Win7 probe builds link the known-good x64
|
||||
loader from source. `.github/scripts/prepare-webview2-win7-loader.ps1` also
|
||||
verifies/reinstalls that loader and replaces already-copied Cargo
|
||||
`target/**/out/x64` build outputs with the same pinned binary.
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"v":1}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "b74dc5e2b394044bea5191052868ce7a106c202c"
|
||||
},
|
||||
"path_in_vcs": "crates/bindings"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.38.2](https://github.com/wravery/webview2-rs/compare/webview2-com-sys-v0.38.1...webview2-com-sys-v0.38.2) - 2026-01-07
|
||||
|
||||
### Other
|
||||
|
||||
- update WebView2 SDK to 1.0.3650.58
|
||||
|
||||
## [0.38.1](https://github.com/wravery/webview2-rs/compare/webview2-com-sys-v0.38.0...webview2-com-sys-v0.38.1) - 2026-01-07
|
||||
|
||||
### Added
|
||||
|
||||
- update bindings to 1.0.3351.48
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-sys"
|
||||
version = "0.38.2"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"windows",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
name = "webview2-com-sys"
|
||||
version = "0.38.2"
|
||||
build = "build.rs"
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Bindings generated with the windows crate for the WebView2 COM APIs"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"win32",
|
||||
"webview2",
|
||||
]
|
||||
categories = ["os::windows-apis"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/wravery/webview2-rs"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = [
|
||||
"i686-pc-windows-msvc",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"x86_64-pc-windows-gnu",
|
||||
"i686-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[lib]
|
||||
name = "webview2_com_sys"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.61"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_Variant",
|
||||
"Win32_System_WinRT",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[dependencies.windows-core]
|
||||
version = "0.61"
|
||||
|
||||
[build-dependencies.thiserror]
|
||||
version = "2"
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "webview2-com-sys"
|
||||
description = "Bindings generated with the windows crate for the WebView2 COM APIs"
|
||||
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = [
|
||||
"i686-pc-windows-msvc",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"x86_64-pc-windows-gnu",
|
||||
"i686-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
windows-core.workspace = true
|
||||
windows.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
thiserror.workspace = true
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
# webview2-com-sys
|
||||
This crate implements unsafe Rust bindings for the [WebView2](https://aka.ms/webview2) COM APIs using the [Windows](https://github.com/microsoft/windows-rs) crate.
|
||||
|
||||
## Getting Started
|
||||
This crate has a friendlier wrapper in [webview2-com](https://crates.io/crates/webview2-com).
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+91
@@ -0,0 +1,91 @@
|
||||
fn main() -> Result<()> {
|
||||
webview2_link::update_rustc_flags(webview2_link::output_libs(
|
||||
webview2_path::get_manifest_dir()?,
|
||||
webview2_path::get_out_dir()?,
|
||||
)?)?;
|
||||
|
||||
println!("cargo:rustc-link-lib=advapi32");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Var(#[from] std::env::VarError),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
|
||||
mod webview2_path {
|
||||
use std::{convert::From, env, path::PathBuf};
|
||||
|
||||
pub fn get_out_dir() -> super::Result<PathBuf> {
|
||||
Ok(PathBuf::from(env::var("OUT_DIR")?))
|
||||
}
|
||||
|
||||
pub fn get_manifest_dir() -> super::Result<PathBuf> {
|
||||
Ok(PathBuf::from(env::var("CARGO_MANIFEST_DIR")?))
|
||||
}
|
||||
}
|
||||
|
||||
mod webview2_link {
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
pub fn output_libs(manifest_dir: PathBuf, out_dir: PathBuf) -> super::Result<PathBuf> {
|
||||
const WEBVIEW2_LIBS: &[&str] = &[
|
||||
"WebView2Loader.dll",
|
||||
"WebView2Loader.dll.lib",
|
||||
"WebView2LoaderStatic.lib",
|
||||
];
|
||||
const WEBVIEW2_TARGETS: &[&str] = &["arm64", "x64", "x86"];
|
||||
|
||||
for target in WEBVIEW2_TARGETS {
|
||||
for lib in WEBVIEW2_LIBS {
|
||||
use std::fs;
|
||||
|
||||
let mut lib_src = manifest_dir.clone();
|
||||
lib_src.push(target);
|
||||
lib_src.push(lib);
|
||||
|
||||
let mut lib_dest = out_dir.clone();
|
||||
lib_dest.push(target);
|
||||
if !lib_dest.is_dir() {
|
||||
fs::create_dir(lib_dest.as_path())?;
|
||||
}
|
||||
|
||||
lib_dest.push(lib);
|
||||
eprintln!("Copy from {:?} -> {:?}", lib_src, lib_dest);
|
||||
fs::copy(lib_src.as_path(), lib_dest.as_path())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(out_dir)
|
||||
}
|
||||
|
||||
pub fn update_rustc_flags(lib_path: PathBuf) -> super::Result<()> {
|
||||
let target_arch = match env::var("CARGO_CFG_TARGET_ARCH")?.as_str() {
|
||||
"x86_64" => "x64",
|
||||
"x86" => "x86",
|
||||
"arm" => "arm",
|
||||
"aarch64" => "arm64",
|
||||
unimplemented => unimplemented!(
|
||||
"`{}` architecture set by `CARGO_CFG_TARGET_ARCH`",
|
||||
unimplemented
|
||||
),
|
||||
};
|
||||
let mut lib_path = lib_path;
|
||||
lib_path.push(target_arch);
|
||||
|
||||
match lib_path.to_str() {
|
||||
Some(path) if lib_path.exists() => println!("cargo:rustc-link-search=native={path}"),
|
||||
_ => unimplemented!("`{}` is not supported by WebView2", target_arch),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+43991
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,116 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// Generate a list of all `ICoreWebView2...Handler` interfaces declared in `WebView2.h`. This is
|
||||
/// for testing purposes to make sure they are all covered in
|
||||
/// [callback.rs](../../webview2-com/src/callback.rs).
|
||||
pub fn all_declared_callbacks() -> BTreeSet<&'static str> {
|
||||
let mut interfaces = BTreeSet::new();
|
||||
|
||||
interfaces.insert("ICoreWebView2AcceleratorKeyPressedEventHandler");
|
||||
interfaces.insert("ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2BasicAuthenticationRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2BrowserExtensionEnableCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2BrowserExtensionRemoveCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2BrowserProcessExitedEventHandler");
|
||||
interfaces.insert("ICoreWebView2BytesReceivedChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2CallDevToolsProtocolMethodCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2CapturePreviewCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ClearBrowsingDataCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ClearServerCertificateErrorActionsCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ClientCertificateRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ContainsFullScreenElementChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ContentLoadingEventHandler");
|
||||
interfaces.insert("ICoreWebView2ContextMenuRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2CreateCoreWebView2ControllerCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2CursorChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2CustomItemSelectedEventHandler");
|
||||
interfaces.insert("ICoreWebView2DOMContentLoadedEventHandler");
|
||||
interfaces.insert("ICoreWebView2DevToolsProtocolEventReceivedEventHandler");
|
||||
interfaces.insert("ICoreWebView2DocumentTitleChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2DownloadStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2EstimatedEndTimeChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ExecuteScriptCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ExecuteScriptWithResultCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2FaviconChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FindActiveMatchIndexChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FindMatchCountChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FindStartCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2FocusChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameChildFrameCreatedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameContentLoadingEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameCreatedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameDOMContentLoadedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameDestroyedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameNameChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameNavigationCompletedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameNavigationStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2FramePermissionRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameScreenCaptureStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2FrameWebMessageReceivedEventHandler");
|
||||
interfaces.insert("ICoreWebView2GetCookiesCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2GetFaviconCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2GetNonDefaultPermissionSettingsCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2GetProcessExtendedInfosCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2HistoryChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2IsDefaultDownloadDialogOpenChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2IsDocumentPlayingAudioChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2IsMutedChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2LaunchingExternalUriSchemeEventHandler");
|
||||
interfaces.insert("ICoreWebView2MoveFocusRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2NavigationCompletedEventHandler");
|
||||
interfaces.insert("ICoreWebView2NavigationStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2NewBrowserVersionAvailableEventHandler");
|
||||
interfaces.insert("ICoreWebView2NewWindowRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2NonClientRegionChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2NotificationCloseRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2NotificationReceivedEventHandler");
|
||||
interfaces.insert("ICoreWebView2PermissionRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2PrintCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2PrintToPdfCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2PrintToPdfStreamCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ProcessFailedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ProcessInfosChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ProfileAddBrowserExtensionCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ProfileDeletedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ProfileGetBrowserExtensionsCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2RasterizationScaleChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2SaveAsUIShowingEventHandler");
|
||||
interfaces.insert("ICoreWebView2SaveFileSecurityCheckStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2ScreenCaptureStartingEventHandler");
|
||||
interfaces.insert("ICoreWebView2ScriptDialogOpeningEventHandler");
|
||||
interfaces.insert("ICoreWebView2ServerCertificateErrorDetectedEventHandler");
|
||||
interfaces.insert("ICoreWebView2SetPermissionStateCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2ShowSaveAsUICompletedHandler");
|
||||
interfaces.insert("ICoreWebView2SourceChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2StateChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2StatusBarTextChangedEventHandler");
|
||||
interfaces.insert("ICoreWebView2TrySuspendCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2WebMessageReceivedEventHandler");
|
||||
interfaces.insert("ICoreWebView2WebResourceRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2WebResourceResponseReceivedEventHandler");
|
||||
interfaces.insert("ICoreWebView2WebResourceResponseViewGetContentCompletedHandler");
|
||||
interfaces.insert("ICoreWebView2WindowCloseRequestedEventHandler");
|
||||
interfaces.insert("ICoreWebView2ZoomFactorChangedEventHandler");
|
||||
|
||||
interfaces
|
||||
}
|
||||
|
||||
/// Generate a list of all `ICoreWebView2EnvironmentOptions` interfaces declared in `WebView2.h`.
|
||||
/// This is for testing purposes to make sure they are all covered in
|
||||
/// [options.rs](../../webview2-com/src/options.rs).
|
||||
pub fn all_declared_options() -> BTreeSet<&'static str> {
|
||||
let mut interfaces = BTreeSet::new();
|
||||
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions2");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions3");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions4");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions5");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions6");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions7");
|
||||
interfaces.insert("ICoreWebView2EnvironmentOptions8");
|
||||
|
||||
interfaces
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#[allow(
|
||||
non_snake_case,
|
||||
non_upper_case_globals,
|
||||
non_camel_case_types,
|
||||
dead_code,
|
||||
clippy::all
|
||||
)]
|
||||
pub mod Microsoft {
|
||||
pub mod Web {
|
||||
pub mod WebView2 {
|
||||
pub mod Win32 {
|
||||
mod windows_link {
|
||||
macro_rules! link_webview2 {
|
||||
($library:literal $abi:literal fn $($function:tt)*) => (
|
||||
#[cfg_attr(
|
||||
target_env = "msvc",
|
||||
link(name = "WebView2LoaderStatic", kind = "static")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(target_env = "msvc"),
|
||||
link(name = "WebView2Loader.dll")
|
||||
)]
|
||||
extern $abi {
|
||||
pub fn $($function)*;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) use link_webview2 as link;
|
||||
}
|
||||
|
||||
include!("bindings.rs");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod declared_interfaces;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use windows_core::w;
|
||||
|
||||
use crate::Microsoft::Web::WebView2::Win32::*;
|
||||
|
||||
#[test]
|
||||
fn compare_eq() {
|
||||
let mut result = 1;
|
||||
unsafe { CompareBrowserVersions(w!("1.0.0"), w!("1.0.0"), &mut result) }.unwrap();
|
||||
assert_eq!(0, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare_lt() {
|
||||
let mut result = 0;
|
||||
unsafe { CompareBrowserVersions(w!("1.0.0"), w!("1.0.1"), &mut result) }.unwrap();
|
||||
assert_eq!(-1, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare_gt() {
|
||||
let mut result = 0;
|
||||
unsafe { CompareBrowserVersions(w!("2.0.0"), w!("1.0.1"), &mut result) }.unwrap();
|
||||
assert_eq!(1, result);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
{"v":1}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "32c3144490c016fe496a0aed769bce60987a2e9d"
|
||||
},
|
||||
"path_in_vcs": "crates/libs/core"
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.82"
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Core type support for COM and Windows"
|
||||
readme = "readme.md"
|
||||
categories = ["os::windows-apis"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/microsoft/windows-rs"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = []
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"windows-result/std",
|
||||
"windows-strings/std",
|
||||
]
|
||||
|
||||
[lib]
|
||||
name = "windows_core"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.windows-implement]
|
||||
version = "0.60.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.windows-interface]
|
||||
version = "0.59.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.windows-link]
|
||||
version = "0.2.1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.windows-result]
|
||||
version = "0.4.1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.windows-strings]
|
||||
version = "0.5.1"
|
||||
default-features = false
|
||||
|
||||
[lints.rust]
|
||||
missing_unsafe_on_extern = "warn"
|
||||
|
||||
[lints.rust.unexpected_cfgs]
|
||||
level = "warn"
|
||||
priority = 0
|
||||
check-cfg = ["cfg(windows_raw_dylib, windows_slim_errors)"]
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.82"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Core type support for COM and Windows"
|
||||
repository = "https://github.com/microsoft/windows-rs"
|
||||
categories = ["os::windows-apis"]
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
windows-implement = { workspace = true }
|
||||
windows-interface = { workspace = true }
|
||||
windows-link = { workspace = true }
|
||||
windows-result = { workspace = true }
|
||||
windows-strings = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["windows-result/std", "windows-strings/std"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "x86_64-pc-windows-msvc"
|
||||
targets = []
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
## Core type support for COM and Windows
|
||||
|
||||
The [windows-core](https://crates.io/crates/windows-core) crate provides core type support for the windows-* family of crates.
|
||||
|
||||
* [Getting started](https://kennykerr.ca/rust-getting-started/)
|
||||
* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples)
|
||||
* [Releases](https://github.com/microsoft/windows-rs/releases)
|
||||
@@ -0,0 +1,39 @@
|
||||
use super::*;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// A type representing an agile reference to a COM/WinRT object.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct AgileReference<T>(imp::IAgileReference, PhantomData<T>);
|
||||
|
||||
impl<T: Interface> AgileReference<T> {
|
||||
/// Creates an agile reference to the object.
|
||||
pub fn new(object: &T) -> Result<Self> {
|
||||
// TODO: this assert is required until we can catch this at compile time using an "associated const equality" constraint.
|
||||
// For example, <T: Interface<UNKNOWN = true>>
|
||||
// https://github.com/rust-lang/rust/issues/92827
|
||||
assert!(T::UNKNOWN);
|
||||
unsafe {
|
||||
imp::RoGetAgileReference(
|
||||
imp::AGILEREFERENCE_DEFAULT,
|
||||
&T::IID,
|
||||
core::mem::transmute::<&T, &IUnknown>(object),
|
||||
)
|
||||
.map(|reference| Self(reference, Default::default()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a proxy to the target of the `AgileReference` object that may safely be used within any thread context in which get is called.
|
||||
pub fn resolve(&self) -> Result<T> {
|
||||
unsafe { self.0.Resolve() }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Interface> Send for AgileReference<T> {}
|
||||
unsafe impl<T: Interface> Sync for AgileReference<T> {}
|
||||
|
||||
impl<T> core::fmt::Debug for AgileReference<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "AgileReference({:?})", &self.0)
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
use super::*;
|
||||
|
||||
/// A WinRT array stores elements contiguously in a heap-allocated buffer.
|
||||
pub struct Array<T: Type<T>> {
|
||||
pub(crate) data: *mut T::Default,
|
||||
pub(crate) len: u32,
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Default for Array<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: core::ptr::null_mut(),
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Array<T> {
|
||||
/// Creates an empty array.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Creates an array of the given length with default values.
|
||||
pub fn with_len(len: usize) -> Self {
|
||||
assert!(len < u32::MAX as usize);
|
||||
let bytes_amount = len
|
||||
.checked_mul(core::mem::size_of::<T>())
|
||||
.expect("Attempted to allocate too large an Array");
|
||||
|
||||
// WinRT arrays must be allocated with CoTaskMemAlloc.
|
||||
// SAFETY: the call to CoTaskMemAlloc is safe to perform
|
||||
// if len is zero and overflow was checked above.
|
||||
// We ensured we alloc enough space by multiplying len * size_of::<T>
|
||||
let data = unsafe { imp::CoTaskMemAlloc(bytes_amount) as *mut T::Default };
|
||||
|
||||
assert!(!data.is_null(), "Could not successfully allocate for Array");
|
||||
|
||||
// SAFETY: It is by definition safe to zero-initialize WinRT types.
|
||||
// `write_bytes` will write 0 to (len * size_of::<T>())
|
||||
// bytes making the entire array zero initialized. We have assured
|
||||
// above that the data ptr is not null.
|
||||
unsafe {
|
||||
core::ptr::write_bytes(data, 0, len);
|
||||
}
|
||||
|
||||
let len = len as u32;
|
||||
Self { data, len }
|
||||
}
|
||||
|
||||
/// Creates an array by copying the elements from the slice.
|
||||
pub fn from_slice(values: &[T::Default]) -> Self
|
||||
where
|
||||
T::Default: Clone,
|
||||
{
|
||||
let mut array = Self::with_len(values.len());
|
||||
array.clone_from_slice(values);
|
||||
array
|
||||
}
|
||||
|
||||
/// Creates an array from a pointer and length. The `len` argument is the number of elements, not the number of bytes.
|
||||
/// # Safety
|
||||
/// The `data` argument must have been allocated with `CoTaskMemAlloc`.
|
||||
pub unsafe fn from_raw_parts(data: *mut T::Default, len: u32) -> Self {
|
||||
Self { data, len }
|
||||
}
|
||||
|
||||
/// Returns a slice containing the entire array.
|
||||
pub fn as_slice(&self) -> &[T::Default] {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns `true` if the array is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
/// Returns the length of the array.
|
||||
pub fn len(&self) -> usize {
|
||||
self.len as usize
|
||||
}
|
||||
|
||||
/// Clears the contents of the array.
|
||||
pub fn clear(&mut self) {
|
||||
if self.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut data = core::ptr::null_mut();
|
||||
let mut len = 0;
|
||||
|
||||
core::mem::swap(&mut data, &mut self.data);
|
||||
core::mem::swap(&mut len, &mut self.len);
|
||||
|
||||
// SAFETY: At this point, self has been reset to zero so any panics in T's destructor would
|
||||
// only leak data not leave the array in bad state.
|
||||
unsafe {
|
||||
// Call the destructors of all the elements of the old array
|
||||
// SAFETY: the slice cannot be used after the call to `drop_in_place`
|
||||
core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut(data, len as usize));
|
||||
// Free the data memory where the elements were
|
||||
// SAFETY: we have unique access to the data pointer at this point
|
||||
// so freeing it is the right thing to do
|
||||
imp::CoTaskMemFree(data as _);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Get a mutable pointer to the array's length
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is safe but writing to the pointer is not. Calling this without
|
||||
/// a subsequent call to `set_abi` is likely to either leak memory or cause UB
|
||||
pub unsafe fn set_abi_len(&mut self) -> *mut u32 {
|
||||
&mut self.len
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Turn the array into a pointer to its data and its length
|
||||
pub fn into_abi(self) -> (*mut T::Abi, u32) {
|
||||
let abi = (self.data as *mut _, self.len);
|
||||
core::mem::forget(self);
|
||||
abi
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::ops::Deref for Array<T> {
|
||||
type Target = [T::Default];
|
||||
|
||||
fn deref(&self) -> &[T::Default] {
|
||||
if self.is_empty() {
|
||||
return &[];
|
||||
}
|
||||
|
||||
// SAFETY: data must not be null if the array is not empty
|
||||
unsafe { core::slice::from_raw_parts(self.data, self.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::ops::DerefMut for Array<T> {
|
||||
fn deref_mut(&mut self) -> &mut [T::Default] {
|
||||
if self.is_empty() {
|
||||
return &mut [];
|
||||
}
|
||||
|
||||
// SAFETY: data must not be null if the array is not empty
|
||||
unsafe { core::slice::from_raw_parts_mut(self.data, self.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Drop for Array<T> {
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::fmt::Debug for Array<T>
|
||||
where
|
||||
T::Default: core::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
core::ops::Deref::deref(self).fmt(f)
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/// A trait for retrieving the implementation behind a COM or WinRT interface.
|
||||
///
|
||||
/// This trait is automatically implemented when using the `implement` macro.
|
||||
pub trait AsImpl<T> {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that `self` is actually implemented by the
|
||||
/// implementation `T`.
|
||||
unsafe fn as_impl(&self) -> &T {
|
||||
unsafe { self.as_impl_ptr().as_ref() }
|
||||
}
|
||||
|
||||
/// Returns a pointer to the implementation object.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that `self` is actually implemented by the
|
||||
/// implementation `T`.
|
||||
unsafe fn as_impl_ptr(&self) -> core::ptr::NonNull<T>;
|
||||
}
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
use crate::imp::Box;
|
||||
use crate::{IUnknown, IUnknownImpl, Interface, InterfaceRef};
|
||||
use core::any::Any;
|
||||
use core::borrow::Borrow;
|
||||
use core::ops::Deref;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
/// Identifies types that can be placed in [`ComObject`].
|
||||
///
|
||||
/// This trait links types that can be placed in `ComObject` with the types generated by the
|
||||
/// `#[implement]` macro. The `#[implement]` macro generates implementations of this trait.
|
||||
/// The generated types contain the vtable layouts and refcount-related fields for the COM
|
||||
/// object implementation.
|
||||
///
|
||||
/// This trait is an implementation detail of the Windows crates.
|
||||
/// User code should not deal directly with this trait.
|
||||
///
|
||||
/// This trait is sort of the reverse of [`IUnknownImpl`]. This trait allows user code to use
|
||||
/// [`ComObject<T>`] instead of `ComObject<T_Impl>`.
|
||||
pub trait ComObjectInner: Sized {
|
||||
/// The generated `<foo>_Impl` type (aka the "boxed" type or "outer" type).
|
||||
type Outer: IUnknownImpl<Impl = Self>;
|
||||
|
||||
/// Moves an instance of this type into a new ComObject box and returns it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is important that safe Rust code never be able to acquire an owned instance of a
|
||||
/// generated "outer" COM object type, e.g. `<foo>_Impl`. This would be unsafe because the
|
||||
/// `<foo>_Impl` object contains a reference count field and provides methods that adjust
|
||||
/// the reference count, and destroy the object when the reference count reaches zero.
|
||||
///
|
||||
/// Safe Rust code must only be able to interact with these values by accessing them via a
|
||||
/// `ComObject` reference. `ComObject` handles adjusting reference counts and associates the
|
||||
/// lifetime of a `&<foo>_Impl` with the lifetime of the related `ComObject`.
|
||||
///
|
||||
/// The `#[implement]` macro generates the implementation of this `into_object` method.
|
||||
/// The generated `into_object` method encapsulates the construction of the `<foo>_Impl`
|
||||
/// object and immediately places it into the heap and returns a `ComObject` reference to it.
|
||||
/// This ensures that our requirement -- that safe Rust code never own a `<foo>_Impl` value
|
||||
/// directly -- is met.
|
||||
fn into_object(self) -> ComObject<Self>;
|
||||
}
|
||||
|
||||
/// Describes the COM interfaces implemented by a specific COM object.
|
||||
///
|
||||
/// The `#[implement]` macro generates implementations of this trait. Implementations are attached
|
||||
/// to the "outer" types generated by `#[implement]`, e.g. the `MyApp_Impl` type. Each
|
||||
/// implementation knows how to locate the interface-specific field within `MyApp_Impl`.
|
||||
///
|
||||
/// This trait is an implementation detail of the Windows crates.
|
||||
/// User code should not deal directly with this trait.
|
||||
pub trait ComObjectInterface<I: Interface> {
|
||||
/// Gets a borrowed interface that is implemented by `T`.
|
||||
fn as_interface_ref(&self) -> InterfaceRef<'_, I>;
|
||||
}
|
||||
|
||||
/// A counted pointer to a type that implements COM interfaces, where the object has been
|
||||
/// placed in the heap (boxed).
|
||||
///
|
||||
/// This type exists so that you can place an object into the heap and query for COM interfaces,
|
||||
/// without losing the safe reference to the implementation object.
|
||||
///
|
||||
/// Because the pointer inside this type is known to be non-null, `Option<ComObject<T>>` should
|
||||
/// always have the same size as a single pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The contained `ptr` field is an owned, reference-counted pointer to a _pinned_ `Pin<Box<T::Outer>>`.
|
||||
/// Although this code does not currently use `Pin<T>`, it takes care not to expose any unsafe semantics
|
||||
/// to safe code. However, code that calls unsafe functions on [`ComObject`] must, like all unsafe code,
|
||||
/// understand and preserve invariants.
|
||||
#[repr(transparent)]
|
||||
pub struct ComObject<T: ComObjectInner> {
|
||||
ptr: NonNull<T::Outer>,
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> ComObject<T> {
|
||||
/// Allocates a heap cell (box) and moves `value` into it. Returns a counted pointer to `value`.
|
||||
pub fn new(value: T) -> Self {
|
||||
T::into_object(value)
|
||||
}
|
||||
|
||||
/// Creates a new `ComObject` that points to an existing boxed instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` points to a valid, heap-allocated instance of `T::Outer`.
|
||||
/// Normally, this pointer comes from using `Box::into_raw(Box::new(...))`.
|
||||
///
|
||||
/// The pointed-to box must have a reference count that is greater than zero.
|
||||
///
|
||||
/// This function takes ownership of the existing pointer; it does not call `AddRef`.
|
||||
/// The reference count must accurately reflect all outstanding references to the box,
|
||||
/// including `ptr` in the count.
|
||||
pub unsafe fn from_raw(ptr: NonNull<T::Outer>) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Gets a reference to the shared object stored in the box.
|
||||
///
|
||||
/// [`ComObject`] also implements [`Deref`], so you can often deref directly into the object.
|
||||
/// For those situations where using the [`Deref`] impl is inconvenient, you can use
|
||||
/// this method to explicitly get a reference to the contents.
|
||||
#[inline(always)]
|
||||
pub fn get(&self) -> &T {
|
||||
self.get_box().get_impl()
|
||||
}
|
||||
|
||||
/// Gets a reference to the shared object's heap box.
|
||||
#[inline(always)]
|
||||
fn get_box(&self) -> &T::Outer {
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
|
||||
// Note that we _do not_ provide a way to get a mutable reference to the outer box.
|
||||
// It's ok to return `&mut T`, but not `&mut T::Outer`. That would allow someone to replace the
|
||||
// contents of the entire object (box and reference count), which could lead to UB.
|
||||
// This could maybe be solved by returning `Pin<&mut T::Outer>`, but that requires some
|
||||
// additional thinking.
|
||||
|
||||
/// Gets a mutable reference to the object stored in the box, if the reference count
|
||||
/// is exactly 1. If there are multiple references to this object then this returns `None`.
|
||||
#[inline(always)]
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.is_reference_count_one() {
|
||||
// SAFETY: We must only return &mut T, *NOT* &mut T::Outer.
|
||||
// Returning T::Outer would allow swapping the contents of the object, which would
|
||||
// allow (incorrectly) modifying the reference count.
|
||||
unsafe { Some(self.ptr.as_mut().get_impl_mut()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If this object has only a single object reference (i.e. this [`ComObject`] is the only
|
||||
/// reference to the heap allocation), then this method will extract the inner `T`
|
||||
/// (and return it in an `Ok`) and then free the heap allocation.
|
||||
///
|
||||
/// If there is more than one reference to this object, then this returns `Err(self)`.
|
||||
#[inline(always)]
|
||||
pub fn take(self) -> Result<T, Self> {
|
||||
if self.is_reference_count_one() {
|
||||
let outer_box: Box<T::Outer> = unsafe { core::mem::transmute(self) };
|
||||
Ok(outer_box.into_inner())
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts to a given interface type.
|
||||
///
|
||||
/// This always performs a `QueryInterface`, even if `T` is known to implement `I`.
|
||||
/// If you know that `T` implements `I`, then use [`Self::as_interface`] or [`Self::to_interface`] because
|
||||
/// those functions do not require a dynamic `QueryInterface` call.
|
||||
#[inline(always)]
|
||||
pub fn cast<I: Interface>(&self) -> windows_core::Result<I>
|
||||
where
|
||||
T::Outer: ComObjectInterface<IUnknown>,
|
||||
{
|
||||
let unknown = self.as_interface::<IUnknown>();
|
||||
unknown.cast()
|
||||
}
|
||||
|
||||
/// Gets a borrowed reference to an interface that is implemented by `T`.
|
||||
///
|
||||
/// The returned reference does not have an additional reference count.
|
||||
/// You can AddRef it by calling [`InterfaceRef::to_owned`].
|
||||
#[inline(always)]
|
||||
pub fn as_interface<I: Interface>(&self) -> InterfaceRef<'_, I>
|
||||
where
|
||||
T::Outer: ComObjectInterface<I>,
|
||||
{
|
||||
self.get_box().as_interface_ref()
|
||||
}
|
||||
|
||||
/// Gets an owned (counted) reference to an interface that is implemented by this [`ComObject`].
|
||||
#[inline(always)]
|
||||
pub fn to_interface<I: Interface>(&self) -> I
|
||||
where
|
||||
T::Outer: ComObjectInterface<I>,
|
||||
{
|
||||
self.as_interface::<I>().to_owned()
|
||||
}
|
||||
|
||||
/// Converts `self` into an interface that it implements.
|
||||
///
|
||||
/// This does not need to adjust reference counts because `self` is consumed.
|
||||
#[inline(always)]
|
||||
pub fn into_interface<I: Interface>(self) -> I
|
||||
where
|
||||
T::Outer: ComObjectInterface<I>,
|
||||
{
|
||||
unsafe {
|
||||
let raw = self.get_box().as_interface_ref().as_raw();
|
||||
core::mem::forget(self);
|
||||
I::from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// This casts the given COM interface to [`&dyn Any`]. It returns a reference to the "outer"
|
||||
/// object, e.g. `MyApp_Impl`, not the inner `MyApp` object.
|
||||
///
|
||||
/// `T` must be a type that has been annotated with `#[implement]`; this is checked at
|
||||
/// compile-time by the generic constraints of this method. However, note that the
|
||||
/// returned `&dyn Any` refers to the _outer_ implementation object that was generated by
|
||||
/// `#[implement]`, i.e. the `MyApp_Impl` type, not the inner `MyApp` type.
|
||||
///
|
||||
/// If the given object is not a Rust object, or is a Rust object but not `T`, or is a Rust
|
||||
/// object that contains non-static lifetimes, then this function will return `Err(E_NOINTERFACE)`.
|
||||
///
|
||||
/// The returned value is an owned (counted) reference; this function calls `AddRef` on the
|
||||
/// underlying COM object. If you do not need an owned reference, then you can use the
|
||||
/// [`Interface::cast_object_ref`] method instead, and avoid the cost of `AddRef` / `Release`.
|
||||
pub fn cast_from<I>(interface: &I) -> crate::Result<Self>
|
||||
where
|
||||
I: Interface,
|
||||
T::Outer: Any + 'static + IUnknownImpl<Impl = T>,
|
||||
{
|
||||
interface.cast_object()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner + Default> Default for ComObject<T> {
|
||||
fn default() -> Self {
|
||||
Self::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> Drop for ComObject<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
T::Outer::Release(self.ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> Clone for ComObject<T> {
|
||||
#[inline(always)]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
self.ptr.as_ref().AddRef();
|
||||
Self { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> AsRef<T> for ComObject<T> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &T {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> Deref for ComObject<T> {
|
||||
type Target = T::Outer;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.get_box()
|
||||
}
|
||||
}
|
||||
|
||||
// There is no DerefMut implementation because we cannot statically guarantee
|
||||
// that the reference count is 1, which is a requirement for getting exclusive
|
||||
// access to the contents of the object. Use get_mut() for dynamically-checked
|
||||
// exclusive access.
|
||||
|
||||
impl<T: ComObjectInner> From<T> for ComObject<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate hashing, if implemented.
|
||||
impl<T: ComObjectInner + core::hash::Hash> core::hash::Hash for ComObject<T> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.get().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
// If T is Send (or Sync) then the ComObject<T> is also Send (or Sync).
|
||||
// Since the actual object storage is in the heap, the object is never moved.
|
||||
unsafe impl<T: ComObjectInner + Send> Send for ComObject<T> {}
|
||||
unsafe impl<T: ComObjectInner + Sync> Sync for ComObject<T> {}
|
||||
|
||||
impl<T: ComObjectInner + PartialEq> PartialEq for ComObject<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let inner_self: &T = self.get();
|
||||
let other_self: &T = other.get();
|
||||
inner_self == other_self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner + Eq> Eq for ComObject<T> {}
|
||||
|
||||
impl<T: ComObjectInner + PartialOrd> PartialOrd for ComObject<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
let inner_self: &T = self.get();
|
||||
let other_self: &T = other.get();
|
||||
<T as PartialOrd>::partial_cmp(inner_self, other_self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner + Ord> Ord for ComObject<T> {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
let inner_self: &T = self.get();
|
||||
let other_self: &T = other.get();
|
||||
<T as Ord>::cmp(inner_self, other_self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner + core::fmt::Debug> core::fmt::Debug for ComObject<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
<T as core::fmt::Debug>::fmt(self.get(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner + core::fmt::Display> core::fmt::Display for ComObject<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
<T as core::fmt::Display>::fmt(self.get(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ComObjectInner> Borrow<T> for ComObject<T> {
|
||||
fn borrow(&self) -> &T {
|
||||
self.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables applications to define COM objects using static storage. This is useful for factory
|
||||
/// objects, stateless objects, or objects which use need to contain or use mutable global state.
|
||||
///
|
||||
/// COM objects that are defined using `StaticComObject` have their storage placed directly in
|
||||
/// static storage; they are not stored in the heap.
|
||||
///
|
||||
/// COM objects defined using `StaticComObject` do have a reference count and this reference
|
||||
/// count is adjusted when owned COM interface references (e.g. `IFoo` and `IUnknown`) are created
|
||||
/// for the object. The reference count is initialized to 1.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[implement(IFoo)]
|
||||
/// struct MyApp {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// static MY_STATIC_APP: StaticComObject<MyApp> = MyApp { ... }.into_static();
|
||||
///
|
||||
/// fn get_my_static_ifoo() -> IFoo {
|
||||
/// MY_STATIC_APP.to_interface()
|
||||
/// }
|
||||
/// ```
|
||||
pub struct StaticComObject<T>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
{
|
||||
outer: T::Outer,
|
||||
}
|
||||
|
||||
// IMPORTANT: Do not expose any methods that return mutable access to the contents of StaticComObject.
|
||||
// Doing so would violate our safety invariants. For example, we provide a Deref impl but it would
|
||||
// be unsound to provide a DerefMut impl.
|
||||
impl<T> StaticComObject<T>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
{
|
||||
/// Wraps `outer` in a `StaticComObject`.
|
||||
pub const fn from_outer(outer: T::Outer) -> Self {
|
||||
Self { outer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StaticComObject<T>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
{
|
||||
/// Gets access to the contained value.
|
||||
pub const fn get(&'static self) -> &'static T::Outer {
|
||||
&self.outer
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::Deref for StaticComObject<T>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
{
|
||||
type Target = T::Outer;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.outer
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
use super::*;
|
||||
use core::{iter::once, mem::transmute_copy};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// A type that you can use to declare and implement an event of a specified delegate type.
|
||||
///
|
||||
/// The implementation is thread-safe and designed to avoid contention between events being
|
||||
/// raised and delegates being added or removed.
|
||||
pub struct Event<T: Interface> {
|
||||
delegates: RwLock<Option<Arc<[Delegate<T>]>>>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Interface> Send for Event<T> {}
|
||||
unsafe impl<T: Interface> Sync for Event<T> {}
|
||||
|
||||
impl<T: Interface> Default for Event<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Interface> Event<T> {
|
||||
/// Creates a new, empty `Event<T>`.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
delegates: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers a delegate with the event object.
|
||||
pub fn add(&self, delegate: &T) -> Result<i64> {
|
||||
let new_delegate = Delegate::new(delegate)?;
|
||||
let token = new_delegate.to_token();
|
||||
let new_iter = once(new_delegate);
|
||||
let mut guard = self.delegates.write().unwrap();
|
||||
|
||||
let new_list = if let Some(old_delegates) = guard.as_ref() {
|
||||
Arc::from_iter(old_delegates.iter().cloned().chain(new_iter))
|
||||
} else {
|
||||
Arc::from_iter(new_iter)
|
||||
};
|
||||
|
||||
let old_list = guard.replace(new_list);
|
||||
drop(guard);
|
||||
drop(old_list); // drop the old delegates _after_ releasing lock
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
/// Revokes a delegate's registration from the event object.
|
||||
pub fn remove(&self, token: i64) {
|
||||
let mut guard = self.delegates.write().unwrap();
|
||||
let mut old_list = None;
|
||||
if let Some(old_delegates) = guard.as_ref() {
|
||||
// `self.delegates` is only modified if the token is found.
|
||||
if let Some(i) = old_delegates
|
||||
.iter()
|
||||
.position(|old_delegate| old_delegate.to_token() == token)
|
||||
{
|
||||
let new_list = Arc::from_iter(
|
||||
old_delegates[..i]
|
||||
.iter()
|
||||
.chain(old_delegates[i + 1..].iter())
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
old_list = guard.replace(new_list);
|
||||
}
|
||||
}
|
||||
drop(guard);
|
||||
drop(old_list); // drop the old delegates _after_ releasing lock
|
||||
}
|
||||
|
||||
/// Clears the event, removing all delegates.
|
||||
pub fn clear(&self) {
|
||||
let mut guard = self.delegates.write().unwrap();
|
||||
let old_list = guard.take();
|
||||
drop(guard);
|
||||
drop(old_list); // drop the old delegates _after_ releasing lock
|
||||
}
|
||||
|
||||
/// Invokes all of the event object's registered delegates with the provided callback.
|
||||
pub fn call<F: FnMut(&T) -> Result<()>>(&self, mut callback: F) {
|
||||
let delegates = {
|
||||
let guard = self.delegates.read().unwrap();
|
||||
if let Some(delegates) = guard.as_ref() {
|
||||
delegates.clone()
|
||||
} else {
|
||||
// No delegates to call.
|
||||
return;
|
||||
}
|
||||
// <-- lock is released here
|
||||
};
|
||||
|
||||
for delegate in delegates.iter() {
|
||||
if let Err(error) = delegate.call(&mut callback) {
|
||||
const RPC_E_SERVER_UNAVAILABLE: HRESULT = HRESULT(-2147023174); // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE)
|
||||
if matches!(
|
||||
error.code(),
|
||||
imp::RPC_E_DISCONNECTED | imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE
|
||||
) {
|
||||
self.remove(delegate.to_token());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds either a direct or indirect reference to a delegate. A direct reference is typically
|
||||
/// agile while an indirect reference is an agile wrapper.
|
||||
#[derive(Clone)]
|
||||
enum Delegate<T> {
|
||||
Direct(T),
|
||||
Indirect(AgileReference<T>),
|
||||
}
|
||||
|
||||
impl<T: Interface> Delegate<T> {
|
||||
/// Creates a new `Delegate<T>`, containing a suitable reference to the specified delegate.
|
||||
fn new(delegate: &T) -> Result<Self> {
|
||||
if delegate.cast::<imp::IAgileObject>().is_ok() {
|
||||
Ok(Self::Direct(delegate.clone()))
|
||||
} else {
|
||||
Ok(Self::Indirect(AgileReference::new(delegate)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an encoded token to identify the delegate.
|
||||
fn to_token(&self) -> i64 {
|
||||
unsafe {
|
||||
match self {
|
||||
Self::Direct(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64,
|
||||
Self::Indirect(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Invokes the delegates with the provided callback.
|
||||
fn call<F: FnMut(&T) -> Result<()>>(&self, mut callback: F) -> Result<()> {
|
||||
match self {
|
||||
Self::Direct(delegate) => callback(delegate),
|
||||
Self::Indirect(delegate) => callback(&delegate.resolve()?),
|
||||
}
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
use super::*;
|
||||
|
||||
/// A globally unique identifier ([GUID](https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid))
|
||||
/// used to identify COM and WinRT interfaces.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
pub struct GUID {
|
||||
/// Specifies the first 8 hexadecimal digits.
|
||||
pub data1: u32,
|
||||
|
||||
/// Specifies the first group of 4 hexadecimal digits.
|
||||
pub data2: u16,
|
||||
|
||||
/// Specifies the second group of 4 hexadecimal digits.
|
||||
pub data3: u16,
|
||||
|
||||
/// The first 2 bytes contain the third group of 4 hexadecimal digits. The remaining 6 bytes contain the final 12 hexadecimal digits.
|
||||
pub data4: [u8; 8],
|
||||
}
|
||||
|
||||
impl GUID {
|
||||
/// Creates a unique `GUID` value.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut guid = Self::zeroed();
|
||||
let result = unsafe { imp::UuidCreate(&mut guid as *mut _ as _) };
|
||||
|
||||
if matches!(result, 0 | imp::RPC_S_UUID_LOCAL_ONLY) {
|
||||
Ok(guid)
|
||||
} else {
|
||||
Err(Error::from_hresult(HRESULT::from_win32(result as u32)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `GUID` represented by the all-zero byte-pattern.
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
data1: 0,
|
||||
data2: 0,
|
||||
data3: 0,
|
||||
data4: [0, 0, 0, 0, 0, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `GUID` with the given constant values.
|
||||
pub const fn from_values(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self {
|
||||
Self {
|
||||
data1,
|
||||
data2,
|
||||
data3,
|
||||
data4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `GUID` from a `u128` value.
|
||||
pub const fn from_u128(uuid: u128) -> Self {
|
||||
Self {
|
||||
data1: (uuid >> 96) as u32,
|
||||
data2: ((uuid >> 80) & 0xffff) as u16,
|
||||
data3: ((uuid >> 64) & 0xffff) as u16,
|
||||
data4: (uuid as u64).to_be_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `GUID` to a `u128` value.
|
||||
pub const fn to_u128(&self) -> u128 {
|
||||
((self.data1 as u128) << 96)
|
||||
+ ((self.data2 as u128) << 80)
|
||||
+ ((self.data3 as u128) << 64)
|
||||
+ u64::from_be_bytes(self.data4) as u128
|
||||
}
|
||||
|
||||
/// Creates a `GUID` for a "generic" WinRT type.
|
||||
pub const fn from_signature(signature: imp::ConstBuffer) -> Self {
|
||||
let data = imp::ConstBuffer::from_slice(&[
|
||||
0x11, 0xf4, 0x7a, 0xd5, 0x7b, 0x73, 0x42, 0xc0, 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16,
|
||||
0xad, 0xee,
|
||||
]);
|
||||
|
||||
let data = data.push_other(signature);
|
||||
|
||||
let bytes = imp::sha1(&data).bytes();
|
||||
let first = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
|
||||
|
||||
let second = u16::from_be_bytes([bytes[4], bytes[5]]);
|
||||
let mut third = u16::from_be_bytes([bytes[6], bytes[7]]);
|
||||
third = (third & 0x0fff) | (5 << 12);
|
||||
let fourth = (bytes[8] & 0x3f) | 0x80;
|
||||
|
||||
Self::from_values(
|
||||
first,
|
||||
second,
|
||||
third,
|
||||
[
|
||||
fourth, bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeType for GUID {
|
||||
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"g16");
|
||||
}
|
||||
|
||||
impl TypeKind for GUID {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for GUID {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:08X?}-{:04X?}-{:04X?}-{:02X?}{:02X?}-{:02X?}{:02X?}{:02X?}{:02X?}{:02X?}{:02X?}",
|
||||
self.data1,
|
||||
self.data2,
|
||||
self.data3,
|
||||
self.data4[0],
|
||||
self.data4[1],
|
||||
self.data4[2],
|
||||
self.data4[3],
|
||||
self.data4[4],
|
||||
self.data4[5],
|
||||
self.data4[6],
|
||||
self.data4[7]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for GUID {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(from: &str) -> Result<Self> {
|
||||
if from.len() != 36 {
|
||||
return Err(invalid_guid());
|
||||
}
|
||||
|
||||
let bytes = &mut from.bytes();
|
||||
let mut guid = Self::zeroed();
|
||||
|
||||
guid.data1 = try_u32(bytes, true)?;
|
||||
guid.data2 = try_u16(bytes, true)?;
|
||||
guid.data3 = try_u16(bytes, true)?;
|
||||
guid.data4[0] = try_u8(bytes, false)?;
|
||||
guid.data4[1] = try_u8(bytes, true)?;
|
||||
guid.data4[2] = try_u8(bytes, false)?;
|
||||
guid.data4[3] = try_u8(bytes, false)?;
|
||||
guid.data4[4] = try_u8(bytes, false)?;
|
||||
guid.data4[5] = try_u8(bytes, false)?;
|
||||
guid.data4[6] = try_u8(bytes, false)?;
|
||||
guid.data4[7] = try_u8(bytes, false)?;
|
||||
|
||||
Ok(guid)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for GUID {
|
||||
fn from(value: u128) -> Self {
|
||||
Self::from_u128(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GUID> for u128 {
|
||||
fn from(value: GUID) -> Self {
|
||||
value.to_u128()
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_guid() -> Error {
|
||||
Error::from_hresult(imp::E_INVALIDARG)
|
||||
}
|
||||
|
||||
fn try_u32(bytes: &mut core::str::Bytes, delimiter: bool) -> Result<u32> {
|
||||
next(bytes, 8, delimiter).ok_or_else(invalid_guid)
|
||||
}
|
||||
|
||||
fn try_u16(bytes: &mut core::str::Bytes, delimiter: bool) -> Result<u16> {
|
||||
next(bytes, 4, delimiter)
|
||||
.map(|value| value as u16)
|
||||
.ok_or_else(invalid_guid)
|
||||
}
|
||||
|
||||
fn try_u8(bytes: &mut core::str::Bytes, delimiter: bool) -> Result<u8> {
|
||||
next(bytes, 2, delimiter)
|
||||
.map(|value| value as u8)
|
||||
.ok_or_else(invalid_guid)
|
||||
}
|
||||
|
||||
fn next(bytes: &mut core::str::Bytes, len: usize, delimiter: bool) -> Option<u32> {
|
||||
let mut value: u32 = 0;
|
||||
|
||||
for _ in 0..len {
|
||||
let digit = bytes.next()?;
|
||||
|
||||
match digit {
|
||||
b'0'..=b'9' => value = (value << 4) + (digit - b'0') as u32,
|
||||
b'A'..=b'F' => value = (value << 4) + (digit - b'A' + 10) as u32,
|
||||
b'a'..=b'f' => value = (value << 4) + (digit - b'a' + 10) as u32,
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
if delimiter && bytes.next() != Some(b'-') {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/// Custom code to free a handle.
|
||||
///
|
||||
/// This is similar to the [`Drop`] trait, and may be used to implement [`Drop`], but allows handles
|
||||
/// to be dropped depending on context.
|
||||
pub trait Free {
|
||||
/// Calls the handle's free function.
|
||||
///
|
||||
/// # Safety
|
||||
/// The handle must be owned by the caller and safe to free.
|
||||
unsafe fn free(&mut self);
|
||||
}
|
||||
|
||||
/// A wrapper to provide ownership for handles to automatically drop via the handle's [`Free`] trait.
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Eq, Default, Debug)]
|
||||
pub struct Owned<T: Free>(T);
|
||||
|
||||
impl<T: Free> Owned<T> {
|
||||
/// Takes ownership of the handle.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The handle must be owned by the caller and safe to free.
|
||||
pub unsafe fn new(x: T) -> Self {
|
||||
Self(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Free> Drop for Owned<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.free() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Free> core::ops::Deref for Owned<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Free> core::ops::DerefMut for Owned<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use crate::{Array, Type};
|
||||
|
||||
pub struct ArrayProxy<T: Type<T>> {
|
||||
data: *mut *mut T::Default,
|
||||
len: *mut u32,
|
||||
temp: core::mem::ManuallyDrop<Array<T>>,
|
||||
}
|
||||
|
||||
pub unsafe fn array_proxy<T: Type<T>>(data: *mut *mut T::Default, len: *mut u32) -> ArrayProxy<T> {
|
||||
ArrayProxy {
|
||||
data,
|
||||
len,
|
||||
temp: core::mem::ManuallyDrop::new(Array::new()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Drop for ArrayProxy<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
*self.data = self.temp.data;
|
||||
*self.len = self.temp.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::ops::Deref for ArrayProxy<T> {
|
||||
type Target = Array<T>;
|
||||
|
||||
fn deref(&self) -> &Array<T> {
|
||||
&self.temp
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::ops::DerefMut for ArrayProxy<T> {
|
||||
fn deref_mut(&mut self) -> &mut Array<T> {
|
||||
&mut self.temp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
windows_link::link!("combase.dll" "system" fn CoIncrementMTAUsage(pcookie : *mut CO_MTA_USAGE_COOKIE) -> HRESULT);
|
||||
windows_link::link!("ole32.dll" "system" fn CoTaskMemAlloc(cb : usize) -> *mut core::ffi::c_void);
|
||||
windows_link::link!("ole32.dll" "system" fn CoTaskMemFree(pv : *const core::ffi::c_void));
|
||||
windows_link::link!("kernel32.dll" "system" fn EncodePointer(ptr : *const core::ffi::c_void) -> *mut core::ffi::c_void);
|
||||
windows_link::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
|
||||
windows_link::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
|
||||
windows_link::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE);
|
||||
windows_link::link!("api-ms-win-core-winrt-l1-1-0.dll" "system" fn RoGetActivationFactory(activatableclassid : HSTRING, iid : *const GUID, factory : *mut *mut core::ffi::c_void) -> HRESULT);
|
||||
windows_link::link!("rpcrt4.dll" "system" fn UuidCreate(uuid : *mut GUID) -> RPC_STATUS);
|
||||
pub type BOOL = i32;
|
||||
pub type CO_MTA_USAGE_COOKIE = *mut core::ffi::c_void;
|
||||
pub type FARPROC = Option<unsafe extern "system" fn() -> isize>;
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GUID {
|
||||
pub data1: u32,
|
||||
pub data2: u16,
|
||||
pub data3: u16,
|
||||
pub data4: [u8; 8],
|
||||
}
|
||||
impl GUID {
|
||||
pub const fn from_u128(uuid: u128) -> Self {
|
||||
Self {
|
||||
data1: (uuid >> 96) as u32,
|
||||
data2: (uuid >> 80 & 0xffff) as u16,
|
||||
data3: (uuid >> 64 & 0xffff) as u16,
|
||||
data4: (uuid as u64).to_be_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type HANDLE = *mut core::ffi::c_void;
|
||||
pub type HINSTANCE = *mut core::ffi::c_void;
|
||||
pub type HMODULE = *mut core::ffi::c_void;
|
||||
pub type HRESULT = i32;
|
||||
pub type LOAD_LIBRARY_FLAGS = u32;
|
||||
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: LOAD_LIBRARY_FLAGS = 4096u32;
|
||||
pub type PCSTR = *const u8;
|
||||
pub type RPC_STATUS = i32;
|
||||
pub const RPC_S_UUID_LOCAL_ONLY: RPC_STATUS = 1824i32;
|
||||
pub type HSTRING = *mut core::ffi::c_void;
|
||||
@@ -0,0 +1,5 @@
|
||||
pub trait CanInto<T>: Sized {
|
||||
const QUERY: bool = false;
|
||||
}
|
||||
|
||||
impl<T> CanInto<T> for T where T: Clone {}
|
||||
@@ -0,0 +1,246 @@
|
||||
#[inline]
|
||||
pub unsafe fn RoGetAgileReference<P2>(
|
||||
options: AgileReferenceOptions,
|
||||
riid: *const windows_core::GUID,
|
||||
punk: P2,
|
||||
) -> windows_core::Result<IAgileReference>
|
||||
where
|
||||
P2: windows_core::Param<windows_core::IUnknown>,
|
||||
{
|
||||
windows_core::link!("combase.dll" "system" fn RoGetAgileReference(options : AgileReferenceOptions, riid : *const windows_core::GUID, punk : * mut core::ffi::c_void, ppagilereference : *mut * mut core::ffi::c_void) -> windows_core::HRESULT);
|
||||
unsafe {
|
||||
let mut result__ = core::mem::zeroed();
|
||||
RoGetAgileReference(options, riid, punk.param().abi(), &mut result__)
|
||||
.and_then(|| windows_core::Type::from_abi(result__))
|
||||
}
|
||||
}
|
||||
pub const AGILEREFERENCE_DEFAULT: AgileReferenceOptions = AgileReferenceOptions(0i32);
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct AgileReferenceOptions(pub i32);
|
||||
pub const CO_E_NOTINITIALIZED: windows_core::HRESULT = windows_core::HRESULT(0x800401F0_u32 as _);
|
||||
pub const E_INVALIDARG: windows_core::HRESULT = windows_core::HRESULT(0x80070057_u32 as _);
|
||||
pub const E_NOINTERFACE: windows_core::HRESULT = windows_core::HRESULT(0x80004002_u32 as _);
|
||||
pub const E_POINTER: windows_core::HRESULT = windows_core::HRESULT(0x80004003_u32 as _);
|
||||
windows_core::imp::define_interface!(
|
||||
IAgileObject,
|
||||
IAgileObject_Vtbl,
|
||||
0x94ea2b94_e9cc_49e0_c0ff_ee64ca8f5b90
|
||||
);
|
||||
windows_core::imp::interface_hierarchy!(IAgileObject, windows_core::IUnknown);
|
||||
#[repr(C)]
|
||||
#[doc(hidden)]
|
||||
pub struct IAgileObject_Vtbl {
|
||||
pub base__: windows_core::IUnknown_Vtbl,
|
||||
}
|
||||
pub trait IAgileObject_Impl: windows_core::IUnknownImpl {}
|
||||
impl IAgileObject_Vtbl {
|
||||
pub const fn new<Identity: IAgileObject_Impl, const OFFSET: isize>() -> Self {
|
||||
Self {
|
||||
base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(),
|
||||
}
|
||||
}
|
||||
pub fn matches(iid: &windows_core::GUID) -> bool {
|
||||
iid == &<IAgileObject as windows_core::Interface>::IID
|
||||
}
|
||||
}
|
||||
impl windows_core::RuntimeName for IAgileObject {}
|
||||
windows_core::imp::define_interface!(
|
||||
IAgileReference,
|
||||
IAgileReference_Vtbl,
|
||||
0xc03f6a43_65a4_9818_987e_e0b810d2a6f2
|
||||
);
|
||||
windows_core::imp::interface_hierarchy!(IAgileReference, windows_core::IUnknown);
|
||||
impl IAgileReference {
|
||||
pub unsafe fn Resolve<T>(&self) -> windows_core::Result<T>
|
||||
where
|
||||
T: windows_core::Interface,
|
||||
{
|
||||
let mut result__ = core::ptr::null_mut();
|
||||
unsafe {
|
||||
(windows_core::Interface::vtable(self).Resolve)(
|
||||
windows_core::Interface::as_raw(self),
|
||||
&T::IID,
|
||||
&mut result__,
|
||||
)
|
||||
.and_then(|| windows_core::Type::from_abi(result__))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[doc(hidden)]
|
||||
pub struct IAgileReference_Vtbl {
|
||||
pub base__: windows_core::IUnknown_Vtbl,
|
||||
pub Resolve: unsafe extern "system" fn(
|
||||
*mut core::ffi::c_void,
|
||||
*const windows_core::GUID,
|
||||
*mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT,
|
||||
}
|
||||
pub trait IAgileReference_Impl: windows_core::IUnknownImpl {
|
||||
fn Resolve(
|
||||
&self,
|
||||
riid: *const windows_core::GUID,
|
||||
ppvobjectreference: *mut *mut core::ffi::c_void,
|
||||
) -> windows_core::Result<()>;
|
||||
}
|
||||
impl IAgileReference_Vtbl {
|
||||
pub const fn new<Identity: IAgileReference_Impl, const OFFSET: isize>() -> Self {
|
||||
unsafe extern "system" fn Resolve<Identity: IAgileReference_Impl, const OFFSET: isize>(
|
||||
this: *mut core::ffi::c_void,
|
||||
riid: *const windows_core::GUID,
|
||||
ppvobjectreference: *mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT {
|
||||
unsafe {
|
||||
let this: &Identity =
|
||||
&*((this as *const *const ()).offset(OFFSET) as *const Identity);
|
||||
IAgileReference_Impl::Resolve(
|
||||
this,
|
||||
core::mem::transmute_copy(&riid),
|
||||
core::mem::transmute_copy(&ppvobjectreference),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
Self {
|
||||
base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(),
|
||||
Resolve: Resolve::<Identity, OFFSET>,
|
||||
}
|
||||
}
|
||||
pub fn matches(iid: &windows_core::GUID) -> bool {
|
||||
iid == &<IAgileReference as windows_core::Interface>::IID
|
||||
}
|
||||
}
|
||||
impl windows_core::RuntimeName for IAgileReference {}
|
||||
windows_core::imp::define_interface!(
|
||||
IWeakReference,
|
||||
IWeakReference_Vtbl,
|
||||
0x00000037_0000_0000_c000_000000000046
|
||||
);
|
||||
windows_core::imp::interface_hierarchy!(IWeakReference, windows_core::IUnknown);
|
||||
impl IWeakReference {
|
||||
pub unsafe fn Resolve<T>(&self) -> windows_core::Result<T>
|
||||
where
|
||||
T: windows_core::Interface,
|
||||
{
|
||||
let mut result__ = core::ptr::null_mut();
|
||||
unsafe {
|
||||
(windows_core::Interface::vtable(self).Resolve)(
|
||||
windows_core::Interface::as_raw(self),
|
||||
&T::IID,
|
||||
&mut result__,
|
||||
)
|
||||
.and_then(|| windows_core::Type::from_abi(result__))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[doc(hidden)]
|
||||
pub struct IWeakReference_Vtbl {
|
||||
pub base__: windows_core::IUnknown_Vtbl,
|
||||
pub Resolve: unsafe extern "system" fn(
|
||||
*mut core::ffi::c_void,
|
||||
*const windows_core::GUID,
|
||||
*mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT,
|
||||
}
|
||||
pub trait IWeakReference_Impl: windows_core::IUnknownImpl {
|
||||
fn Resolve(
|
||||
&self,
|
||||
riid: *const windows_core::GUID,
|
||||
objectreference: *mut *mut core::ffi::c_void,
|
||||
) -> windows_core::Result<()>;
|
||||
}
|
||||
impl IWeakReference_Vtbl {
|
||||
pub const fn new<Identity: IWeakReference_Impl, const OFFSET: isize>() -> Self {
|
||||
unsafe extern "system" fn Resolve<Identity: IWeakReference_Impl, const OFFSET: isize>(
|
||||
this: *mut core::ffi::c_void,
|
||||
riid: *const windows_core::GUID,
|
||||
objectreference: *mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT {
|
||||
unsafe {
|
||||
let this: &Identity =
|
||||
&*((this as *const *const ()).offset(OFFSET) as *const Identity);
|
||||
IWeakReference_Impl::Resolve(
|
||||
this,
|
||||
core::mem::transmute_copy(&riid),
|
||||
core::mem::transmute_copy(&objectreference),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
Self {
|
||||
base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(),
|
||||
Resolve: Resolve::<Identity, OFFSET>,
|
||||
}
|
||||
}
|
||||
pub fn matches(iid: &windows_core::GUID) -> bool {
|
||||
iid == &<IWeakReference as windows_core::Interface>::IID
|
||||
}
|
||||
}
|
||||
impl windows_core::RuntimeName for IWeakReference {}
|
||||
windows_core::imp::define_interface!(
|
||||
IWeakReferenceSource,
|
||||
IWeakReferenceSource_Vtbl,
|
||||
0x00000038_0000_0000_c000_000000000046
|
||||
);
|
||||
windows_core::imp::interface_hierarchy!(IWeakReferenceSource, windows_core::IUnknown);
|
||||
impl IWeakReferenceSource {
|
||||
pub unsafe fn GetWeakReference(&self) -> windows_core::Result<IWeakReference> {
|
||||
unsafe {
|
||||
let mut result__ = core::mem::zeroed();
|
||||
(windows_core::Interface::vtable(self).GetWeakReference)(
|
||||
windows_core::Interface::as_raw(self),
|
||||
&mut result__,
|
||||
)
|
||||
.and_then(|| windows_core::Type::from_abi(result__))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[doc(hidden)]
|
||||
pub struct IWeakReferenceSource_Vtbl {
|
||||
pub base__: windows_core::IUnknown_Vtbl,
|
||||
pub GetWeakReference: unsafe extern "system" fn(
|
||||
*mut core::ffi::c_void,
|
||||
*mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT,
|
||||
}
|
||||
pub trait IWeakReferenceSource_Impl: windows_core::IUnknownImpl {
|
||||
fn GetWeakReference(&self) -> windows_core::Result<IWeakReference>;
|
||||
}
|
||||
impl IWeakReferenceSource_Vtbl {
|
||||
pub const fn new<Identity: IWeakReferenceSource_Impl, const OFFSET: isize>() -> Self {
|
||||
unsafe extern "system" fn GetWeakReference<
|
||||
Identity: IWeakReferenceSource_Impl,
|
||||
const OFFSET: isize,
|
||||
>(
|
||||
this: *mut core::ffi::c_void,
|
||||
weakreference: *mut *mut core::ffi::c_void,
|
||||
) -> windows_core::HRESULT {
|
||||
unsafe {
|
||||
let this: &Identity =
|
||||
&*((this as *const *const ()).offset(OFFSET) as *const Identity);
|
||||
match IWeakReferenceSource_Impl::GetWeakReference(this) {
|
||||
Ok(ok__) => {
|
||||
weakreference.write(core::mem::transmute(ok__));
|
||||
windows_core::HRESULT(0)
|
||||
}
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(),
|
||||
GetWeakReference: GetWeakReference::<Identity, OFFSET>,
|
||||
}
|
||||
}
|
||||
pub fn matches(iid: &windows_core::GUID) -> bool {
|
||||
iid == &<IWeakReferenceSource as windows_core::Interface>::IID
|
||||
}
|
||||
}
|
||||
impl windows_core::RuntimeName for IWeakReferenceSource {}
|
||||
pub const JSCRIPT_E_CANTEXECUTE: windows_core::HRESULT = windows_core::HRESULT(0x89020001_u32 as _);
|
||||
pub const REGDB_E_CLASSNOTREG: windows_core::HRESULT = windows_core::HRESULT(0x80040154_u32 as _);
|
||||
pub const RPC_E_DISCONNECTED: windows_core::HRESULT = windows_core::HRESULT(0x80010108_u32 as _);
|
||||
pub const S_OK: windows_core::HRESULT = windows_core::HRESULT(0x0_u32 as _);
|
||||
@@ -0,0 +1,214 @@
|
||||
use super::*;
|
||||
use crate::Interface;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{forget, transmute, transmute_copy};
|
||||
use core::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
pub struct FactoryCache<C, I> {
|
||||
shared: AtomicPtr<c_void>,
|
||||
_c: PhantomData<C>,
|
||||
_i: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<C, I> FactoryCache<C, I> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
shared: AtomicPtr::new(null_mut()),
|
||||
_c: PhantomData,
|
||||
_i: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, I> Default for FactoryCache<C, I> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: crate::RuntimeName, I: Interface> FactoryCache<C, I> {
|
||||
pub fn call<R, F: FnOnce(&I) -> crate::Result<R>>(&self, callback: F) -> crate::Result<R> {
|
||||
loop {
|
||||
// Attempt to load a previously cached factory pointer.
|
||||
let ptr = self.shared.load(Ordering::Relaxed);
|
||||
|
||||
// If a pointer is found, the cache is primed and we're good to go.
|
||||
if !ptr.is_null() {
|
||||
return callback(unsafe { transmute::<&*mut c_void, &I>(&ptr) });
|
||||
}
|
||||
|
||||
// Otherwise, we load the factory the usual way.
|
||||
let factory = load_factory::<C, I>()?;
|
||||
|
||||
// If the factory is agile, we can safely cache it.
|
||||
if factory.cast::<IAgileObject>().is_ok() {
|
||||
if self
|
||||
.shared
|
||||
.compare_exchange_weak(
|
||||
null_mut(),
|
||||
factory.as_raw(),
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
forget(factory);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, for non-agile factories we simply use the factory
|
||||
// and discard after use as it is not safe to cache.
|
||||
return callback(&factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 load_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 {
|
||||
let mut get_com_factory = || {
|
||||
crate::HRESULT(RoGetActivationFactory(
|
||||
transmute_copy(&name),
|
||||
&I::IID as *const _ as _,
|
||||
&mut factory as *mut _ as *mut _,
|
||||
))
|
||||
};
|
||||
let mut code = get_com_factory();
|
||||
|
||||
// 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 {
|
||||
let mut cookie = core::ptr::null_mut();
|
||||
CoIncrementMTAUsage(&mut cookie);
|
||||
|
||||
// Now try a second time to get the activation factory via the OS.
|
||||
code = get_com_factory();
|
||||
}
|
||||
|
||||
code
|
||||
};
|
||||
|
||||
// If this succeeded then return the resulting factory interface.
|
||||
if let Some(factory) = factory {
|
||||
return Ok(factory);
|
||||
}
|
||||
|
||||
// Reg-free activation should only be attempted if the class is not registered.
|
||||
// It should not be attempted if the class is registered but fails to activate.
|
||||
if code == REGDB_E_CLASSNOTREG {
|
||||
// Now attempt to find the factory's implementation heuristically.
|
||||
if let Some(i) = search_path(C::NAME, |library| unsafe {
|
||||
get_activation_factory(library, &name)
|
||||
}) {
|
||||
return i.cast();
|
||||
}
|
||||
}
|
||||
|
||||
Err(crate::Error::from_hresult(code))
|
||||
}
|
||||
|
||||
// Remove the suffix until a match is found appending `.dll\0` at the end
|
||||
///
|
||||
/// For example, if the class name is
|
||||
/// "A.B.TypeName" then the attempted load order will be:
|
||||
/// 1. A.B.dll
|
||||
/// 2. A.dll
|
||||
fn search_path<F, R>(mut path: &str, mut callback: F) -> Option<R>
|
||||
where
|
||||
F: FnMut(crate::PCSTR) -> crate::Result<R>,
|
||||
{
|
||||
let suffix = b".dll\0";
|
||||
let mut library = alloc::vec![0; path.len() + suffix.len()];
|
||||
while let Some(pos) = path.rfind('.') {
|
||||
path = &path[..pos];
|
||||
library.truncate(path.len() + suffix.len());
|
||||
library[..path.len()].copy_from_slice(path.as_bytes());
|
||||
library[path.len()..].copy_from_slice(suffix);
|
||||
|
||||
if let Ok(r) = callback(crate::PCSTR::from_raw(library.as_ptr())) {
|
||||
return Some(r);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn get_activation_factory(
|
||||
library: crate::PCSTR,
|
||||
name: &crate::HSTRING,
|
||||
) -> crate::Result<IGenericFactory> {
|
||||
unsafe {
|
||||
let function =
|
||||
delay_load::<DllGetActivationFactory>(library, crate::s!("DllGetActivationFactory"))
|
||||
.ok_or_else(crate::Error::from_thread)?;
|
||||
let mut abi = null_mut();
|
||||
function(transmute_copy(name), &mut abi).and_then(|| crate::Type::from_abi(abi))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn delay_load<T>(library: crate::PCSTR, function: crate::PCSTR) -> Option<T> {
|
||||
unsafe {
|
||||
let library = LoadLibraryExA(
|
||||
library.0,
|
||||
core::ptr::null_mut(),
|
||||
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
|
||||
);
|
||||
|
||||
if library.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let address = GetProcAddress(library, function.0);
|
||||
|
||||
if address.is_some() {
|
||||
return Some(core::mem::transmute_copy(&address));
|
||||
}
|
||||
|
||||
FreeLibrary(library);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
type DllGetActivationFactory =
|
||||
extern "system" fn(name: *mut c_void, factory: *mut *mut c_void) -> crate::HRESULT;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn dll_search() {
|
||||
let path = "A.B.TypeName";
|
||||
|
||||
// Test library successfully found
|
||||
let mut results = Vec::new();
|
||||
let end_result = search_path(path, |library| {
|
||||
results.push(unsafe { library.to_string().unwrap() });
|
||||
if unsafe { library.as_bytes() } == &b"A.dll"[..] {
|
||||
Ok(42)
|
||||
} else {
|
||||
Err(crate::Error::empty())
|
||||
}
|
||||
});
|
||||
assert!(matches!(end_result, Some(42)));
|
||||
assert_eq!(results, vec!["A.B.dll", "A.dll"]);
|
||||
|
||||
// Test library never successfully found
|
||||
let mut results = Vec::new();
|
||||
let end_result = search_path(path, |library| {
|
||||
results.push(unsafe { library.to_string().unwrap() });
|
||||
crate::Result::<()>::Err(crate::Error::empty())
|
||||
});
|
||||
assert!(end_result.is_none());
|
||||
assert_eq!(results, vec!["A.B.dll", "A.dll"]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use crate::Interface;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::{transmute_copy, zeroed};
|
||||
|
||||
// A streamlined version of the IActivationFactory interface used by WinRT class factories used internally by the windows crate
|
||||
// to simplify code generation. Components should implement the `IActivationFactory` interface published by the windows crate.
|
||||
super::define_interface!(
|
||||
IGenericFactory,
|
||||
IGenericFactory_Vtbl,
|
||||
0x00000035_0000_0000_c000_000000000046
|
||||
);
|
||||
super::interface_hierarchy!(IGenericFactory, crate::IUnknown, crate::IInspectable);
|
||||
|
||||
impl IGenericFactory {
|
||||
pub fn ActivateInstance<I: Interface>(&self) -> crate::Result<I> {
|
||||
unsafe {
|
||||
let mut result__ = zeroed();
|
||||
(Interface::vtable(self).ActivateInstance)(
|
||||
transmute_copy(self),
|
||||
&mut result__ as *mut _ as *mut _,
|
||||
)
|
||||
.and_then(|| crate::Type::from_abi(result__))
|
||||
.and_then(|interface: crate::IInspectable| interface.cast())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IGenericFactory_Vtbl {
|
||||
pub base__: crate::IInspectable_Vtbl,
|
||||
pub ActivateInstance:
|
||||
unsafe extern "system" fn(this: *mut c_void, instance: *mut *mut c_void) -> crate::HRESULT,
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
use super::*;
|
||||
use crate::{IUnknown, IUnknown_Vtbl, Interface, GUID, HRESULT};
|
||||
use core::ffi::c_void;
|
||||
use core::mem::{transmute, transmute_copy};
|
||||
use core::ptr::null_mut;
|
||||
|
||||
windows_link::link!("ole32.dll" "system" fn CoCreateFreeThreadedMarshaler(punkouter: *mut c_void, ppunkmarshal: *mut *mut c_void) -> HRESULT);
|
||||
|
||||
pub unsafe fn marshaler(outer: IUnknown, result: *mut *mut c_void) -> HRESULT {
|
||||
unsafe {
|
||||
let mut marshaler_raw = null_mut();
|
||||
_ = CoCreateFreeThreadedMarshaler(null_mut(), &mut marshaler_raw);
|
||||
assert!(!marshaler_raw.is_null(), "allocation failed");
|
||||
let marshaler: IUnknown = transmute(marshaler_raw);
|
||||
|
||||
_ = (marshaler.vtable().QueryInterface)(
|
||||
transmute_copy(&marshaler),
|
||||
&IMarshal::IID,
|
||||
&mut marshaler_raw,
|
||||
);
|
||||
|
||||
debug_assert!(!marshaler_raw.is_null());
|
||||
let marshaler: IMarshal = transmute(marshaler_raw);
|
||||
|
||||
let marshaler = Marshaler {
|
||||
vtable: &Marshaler::VTABLE,
|
||||
outer,
|
||||
marshaler,
|
||||
count: RefCount::new(1),
|
||||
};
|
||||
|
||||
debug_assert!(!result.is_null());
|
||||
*result = transmute::<Box<_>, *mut c_void>(Box::new(marshaler));
|
||||
S_OK
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Marshaler {
|
||||
vtable: *const IMarshal_Vtbl,
|
||||
outer: IUnknown,
|
||||
marshaler: IMarshal,
|
||||
count: RefCount,
|
||||
}
|
||||
|
||||
impl Marshaler {
|
||||
const VTABLE: IMarshal_Vtbl = IMarshal_Vtbl {
|
||||
base__: IUnknown_Vtbl {
|
||||
QueryInterface: Self::QueryInterface,
|
||||
AddRef: Self::AddRef,
|
||||
Release: Self::Release,
|
||||
},
|
||||
GetUnmarshalClass: Self::GetUnmarshalClass,
|
||||
GetMarshalSizeMax: Self::GetMarshalSizeMax,
|
||||
MarshalInterface: Self::MarshalInterface,
|
||||
UnmarshalInterface: Self::UnmarshalInterface,
|
||||
ReleaseMarshalData: Self::ReleaseMarshalData,
|
||||
DisconnectObject: Self::DisconnectObject,
|
||||
};
|
||||
|
||||
unsafe extern "system" fn QueryInterface(
|
||||
this: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
if iid.is_null() || interface.is_null() {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
if *iid == IMarshal::IID {
|
||||
*interface = &mut (*this).vtable as *mut _ as _;
|
||||
(*this).count.add_ref();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
((*this).outer.vtable().QueryInterface)(transmute_copy(&(*this).outer), iid, interface)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn AddRef(this: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
(*this).count.add_ref()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn Release(this: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
let remaining = (*this).count.release();
|
||||
|
||||
if remaining == 0 {
|
||||
let _ = Box::from_raw(this);
|
||||
}
|
||||
|
||||
remaining
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn GetUnmarshalClass(
|
||||
this: *mut c_void,
|
||||
riid: *const GUID,
|
||||
pv: *const c_void,
|
||||
dwdestcontext: u32,
|
||||
pvdestcontext: *const c_void,
|
||||
mshlflags: u32,
|
||||
pcid: *mut GUID,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().GetUnmarshalClass)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
riid,
|
||||
pv,
|
||||
dwdestcontext,
|
||||
pvdestcontext,
|
||||
mshlflags,
|
||||
pcid,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn GetMarshalSizeMax(
|
||||
this: *mut c_void,
|
||||
riid: *const GUID,
|
||||
pv: *const c_void,
|
||||
dwdestcontext: u32,
|
||||
pvdestcontext: *const c_void,
|
||||
mshlflags: u32,
|
||||
psize: *mut u32,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().GetMarshalSizeMax)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
riid,
|
||||
pv,
|
||||
dwdestcontext,
|
||||
pvdestcontext,
|
||||
mshlflags,
|
||||
psize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn MarshalInterface(
|
||||
this: *mut c_void,
|
||||
pstm: *mut c_void,
|
||||
riid: *const GUID,
|
||||
pv: *const c_void,
|
||||
dwdestcontext: u32,
|
||||
pvdestcontext: *const c_void,
|
||||
mshlflags: u32,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().MarshalInterface)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
pstm,
|
||||
riid,
|
||||
pv,
|
||||
dwdestcontext,
|
||||
pvdestcontext,
|
||||
mshlflags,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn UnmarshalInterface(
|
||||
this: *mut c_void,
|
||||
pstm: *mut c_void,
|
||||
riid: *const GUID,
|
||||
ppv: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().UnmarshalInterface)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
pstm,
|
||||
riid,
|
||||
ppv,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn ReleaseMarshalData(this: *mut c_void, pstm: *mut c_void) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().ReleaseMarshalData)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
pstm,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn DisconnectObject(this: *mut c_void, dwreserved: u32) -> HRESULT {
|
||||
unsafe {
|
||||
let this = this as *mut *mut c_void as *mut Self;
|
||||
|
||||
((*this).marshaler.vtable().DisconnectObject)(
|
||||
transmute_copy(&(*this).marshaler),
|
||||
dwreserved,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct IMarshal(IUnknown);
|
||||
|
||||
unsafe impl Interface for IMarshal {
|
||||
type Vtable = IMarshal_Vtbl;
|
||||
const IID: GUID = GUID::from_u128(0x00000003_0000_0000_c000_000000000046);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct IMarshal_Vtbl {
|
||||
base__: IUnknown_Vtbl,
|
||||
|
||||
GetUnmarshalClass: unsafe extern "system" fn(
|
||||
*mut c_void,
|
||||
*const GUID,
|
||||
*const c_void,
|
||||
u32,
|
||||
*const c_void,
|
||||
u32,
|
||||
*mut GUID,
|
||||
) -> HRESULT,
|
||||
|
||||
GetMarshalSizeMax: unsafe extern "system" fn(
|
||||
*mut c_void,
|
||||
*const GUID,
|
||||
*const c_void,
|
||||
u32,
|
||||
*const c_void,
|
||||
u32,
|
||||
*mut u32,
|
||||
) -> HRESULT,
|
||||
|
||||
MarshalInterface: unsafe extern "system" fn(
|
||||
*mut c_void,
|
||||
*mut c_void,
|
||||
*const GUID,
|
||||
*const c_void,
|
||||
u32,
|
||||
*const c_void,
|
||||
u32,
|
||||
) -> HRESULT,
|
||||
|
||||
UnmarshalInterface: unsafe extern "system" fn(
|
||||
*mut c_void,
|
||||
*mut c_void,
|
||||
*const GUID,
|
||||
*mut *mut c_void,
|
||||
) -> HRESULT,
|
||||
|
||||
ReleaseMarshalData: unsafe extern "system" fn(*mut c_void, *mut c_void) -> HRESULT,
|
||||
DisconnectObject: unsafe extern "system" fn(*mut c_void, u32) -> HRESULT,
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#[cfg(windows)]
|
||||
include!("windows.rs");
|
||||
|
||||
mod bindings;
|
||||
mod can_into;
|
||||
mod com_bindings;
|
||||
mod ref_count;
|
||||
mod sha1;
|
||||
mod weak_ref_count;
|
||||
|
||||
pub(crate) use bindings::*;
|
||||
pub use can_into::*;
|
||||
pub use com_bindings::*;
|
||||
pub use ref_count::*;
|
||||
pub use sha1::*;
|
||||
pub use weak_ref_count::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! interface_hierarchy {
|
||||
($child:ident, $parent:ty) => {
|
||||
impl ::windows_core::imp::CanInto<$parent> for $child {}
|
||||
impl ::core::convert::From<&$child> for &$parent {
|
||||
fn from(value: &$child) -> Self {
|
||||
unsafe { ::core::mem::transmute(value) }
|
||||
}
|
||||
}
|
||||
impl ::core::convert::From<$child> for $parent {
|
||||
fn from(value: $child) -> Self {
|
||||
unsafe { ::core::mem::transmute(value) }
|
||||
}
|
||||
}
|
||||
};
|
||||
($child:ident, $first:ty, $($rest:ty),+) => {
|
||||
$crate::imp::interface_hierarchy!($child, $first);
|
||||
$crate::imp::interface_hierarchy!($child, $($rest),+);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use interface_hierarchy;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! required_hierarchy {
|
||||
($child:ident, $parent:ty) => {
|
||||
impl ::windows_core::imp::CanInto<$parent> for $child { const QUERY: bool = true; }
|
||||
};
|
||||
($child:ident, $first:ty, $($rest:ty),+) => {
|
||||
$crate::imp::required_hierarchy!($child, $first);
|
||||
$crate::imp::required_hierarchy!($child, $($rest),+);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use required_hierarchy;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! define_interface {
|
||||
($name:ident, $vtbl:ident, $iid:literal) => {
|
||||
#[repr(transparent)]
|
||||
#[derive(::core::cmp::PartialEq, ::core::cmp::Eq, ::core::clone::Clone)]
|
||||
pub struct $name(::windows_core::IUnknown);
|
||||
unsafe impl ::windows_core::Interface for $name {
|
||||
type Vtable = $vtbl;
|
||||
const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128($iid);
|
||||
}
|
||||
impl ::core::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_tuple(stringify!($name))
|
||||
.field(&::windows_core::Interface::as_raw(self))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
($name:ident, $vtbl:ident) => {
|
||||
#[repr(transparent)]
|
||||
#[derive(::core::cmp::PartialEq, ::core::cmp::Eq, ::core::clone::Clone)]
|
||||
pub struct $name(::core::ptr::NonNull<::core::ffi::c_void>);
|
||||
unsafe impl ::windows_core::Interface for $name {
|
||||
type Vtable = $vtbl;
|
||||
const IID: ::windows_core::GUID = ::windows_core::GUID::zeroed();
|
||||
const UNKNOWN: bool = false;
|
||||
}
|
||||
impl ::core::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_tuple(stringify!($name)).field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use define_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use alloc::boxed::Box;
|
||||
@@ -0,0 +1,33 @@
|
||||
use core::sync::atomic::{fence, AtomicI32, Ordering};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Default)]
|
||||
pub struct RefCount(pub(crate) AtomicI32);
|
||||
|
||||
impl RefCount {
|
||||
/// Creates a new `RefCount` with an initial value of `1`.
|
||||
pub const fn new(count: u32) -> Self {
|
||||
Self(AtomicI32::new(count as i32))
|
||||
}
|
||||
|
||||
/// Increments the reference count, returning the new value.
|
||||
pub fn add_ref(&self) -> u32 {
|
||||
(self.0.fetch_add(1, Ordering::Relaxed) + 1) as u32
|
||||
}
|
||||
|
||||
/// Decrements the reference count, returning the new value.
|
||||
///
|
||||
/// This operation inserts an `Acquire` fence when the reference count reaches zero.
|
||||
/// This prevents reordering before the object is destroyed.
|
||||
pub fn release(&self) -> u32 {
|
||||
let remaining = self.0.fetch_sub(1, Ordering::Release) - 1;
|
||||
|
||||
match remaining.cmp(&0) {
|
||||
core::cmp::Ordering::Equal => fence(Ordering::Acquire),
|
||||
core::cmp::Ordering::Less => panic!("Object has been over-released."),
|
||||
core::cmp::Ordering::Greater => {}
|
||||
}
|
||||
|
||||
remaining as u32
|
||||
}
|
||||
}
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
pub const fn sha1(data: &ConstBuffer) -> Digest {
|
||||
let state: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
|
||||
let len: u64 = 0;
|
||||
let blocks = Blocks {
|
||||
len: 0,
|
||||
data: [0; 64],
|
||||
};
|
||||
let (blocks, len, state) = process_blocks(blocks, data, len, state);
|
||||
digest(state, len, blocks)
|
||||
}
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct ConstBuffer {
|
||||
data: [u8; BUFFER_SIZE],
|
||||
head: usize,
|
||||
}
|
||||
|
||||
impl Default for ConstBuffer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstBuffer {
|
||||
pub const fn for_class<C: crate::RuntimeName, I: crate::RuntimeType>() -> Self {
|
||||
Self::new()
|
||||
.push_slice(b"rc(")
|
||||
.push_slice(C::NAME.as_bytes())
|
||||
.push(b';')
|
||||
.push_other(I::SIGNATURE)
|
||||
.push(b')')
|
||||
}
|
||||
|
||||
pub const fn for_interface<T: crate::Interface>() -> Self {
|
||||
Self::new().push_guid(&T::IID)
|
||||
}
|
||||
|
||||
pub const fn from_slice(slice: &[u8]) -> Self {
|
||||
let s = Self::new();
|
||||
s.push_slice(slice)
|
||||
}
|
||||
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
data: [0; BUFFER_SIZE],
|
||||
head: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn push_slice(self, slice: &[u8]) -> Self {
|
||||
self.push_amount(slice, slice.len())
|
||||
}
|
||||
|
||||
const fn get(&self, index: usize) -> u8 {
|
||||
self.data[index]
|
||||
}
|
||||
|
||||
const fn len(&self) -> usize {
|
||||
self.head
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
&self.data[..self.head]
|
||||
}
|
||||
|
||||
pub const fn push_other(self, other: Self) -> Self {
|
||||
self.push_amount(&other.data, other.len())
|
||||
}
|
||||
|
||||
const fn push(mut self, value: u8) -> Self {
|
||||
self.data[self.head] = value;
|
||||
self.head += 1;
|
||||
self
|
||||
}
|
||||
|
||||
const fn push_hex_u8(self, value: u8) -> Self {
|
||||
const fn digit(mut value: u8) -> u8 {
|
||||
value &= 0xF;
|
||||
|
||||
if value < 10 {
|
||||
b'0' + value
|
||||
} else {
|
||||
b'a' + (value - 10)
|
||||
}
|
||||
}
|
||||
|
||||
self.push(digit(value >> 4)).push(digit(value))
|
||||
}
|
||||
|
||||
const fn push_hex_u16(self, value: u16) -> Self {
|
||||
self.push_hex_u8((value >> 8) as u8)
|
||||
.push_hex_u8((value & 0xFF) as u8)
|
||||
}
|
||||
|
||||
const fn push_hex_u32(self, value: u32) -> Self {
|
||||
self.push_hex_u16((value >> 16) as u16)
|
||||
.push_hex_u16((value & 0xFFFF) as u16)
|
||||
}
|
||||
|
||||
const fn push_amount(mut self, slice: &[u8], amount: usize) -> Self {
|
||||
let mut i = 0;
|
||||
while i < amount {
|
||||
self.data[self.head + i] = slice[i];
|
||||
i += 1;
|
||||
}
|
||||
self.head += i;
|
||||
self
|
||||
}
|
||||
|
||||
const fn push_guid(self, guid: &crate::GUID) -> Self {
|
||||
self.push(b'{')
|
||||
.push_hex_u32(guid.data1)
|
||||
.push(b'-')
|
||||
.push_hex_u16(guid.data2)
|
||||
.push(b'-')
|
||||
.push_hex_u16(guid.data3)
|
||||
.push(b'-')
|
||||
.push_hex_u16(((guid.data4[0] as u16) << 8) | guid.data4[1] as u16)
|
||||
.push(b'-')
|
||||
.push_hex_u16(((guid.data4[2] as u16) << 8) | guid.data4[3] as u16)
|
||||
.push_hex_u16(((guid.data4[4] as u16) << 8) | guid.data4[5] as u16)
|
||||
.push_hex_u16(((guid.data4[6] as u16) << 8) | guid.data4[7] as u16)
|
||||
.push(b'}')
|
||||
}
|
||||
}
|
||||
|
||||
struct Blocks {
|
||||
len: u32,
|
||||
data: [u8; 64],
|
||||
}
|
||||
|
||||
const fn process_blocks(
|
||||
mut blocks: Blocks,
|
||||
data: &ConstBuffer,
|
||||
mut len: u64,
|
||||
mut state: [u32; 5],
|
||||
) -> (Blocks, u64, [u32; 5]) {
|
||||
const fn as_block(input: &ConstBuffer, offset: usize) -> [u32; 16] {
|
||||
let mut result = [0u32; 16];
|
||||
|
||||
let mut i = 0;
|
||||
while i != 16 {
|
||||
let off = offset + (i * 4);
|
||||
result[i] = (input.get(off + 3) as u32)
|
||||
| ((input.get(off + 2) as u32) << 8)
|
||||
| ((input.get(off + 1) as u32) << 16)
|
||||
| ((input.get(off) as u32) << 24);
|
||||
i += 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
const fn clone_from_slice_64(
|
||||
mut data: [u8; 64],
|
||||
slice: &[u8],
|
||||
offset: usize,
|
||||
num_elems: usize,
|
||||
) -> [u8; 64] {
|
||||
let mut i = 0;
|
||||
while i < num_elems {
|
||||
data[i] = slice[offset + i];
|
||||
i += 1;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < data.len() {
|
||||
if data.len() - i >= 64 {
|
||||
let chunk_block = as_block(data, i);
|
||||
len += 64;
|
||||
state = process_state(state, chunk_block);
|
||||
i += 64;
|
||||
} else {
|
||||
let num_elems = data.len() - i;
|
||||
blocks.data = clone_from_slice_64(blocks.data, &data.data, i, num_elems);
|
||||
blocks.len = num_elems as u32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(blocks, len, state)
|
||||
}
|
||||
|
||||
const fn process_state(mut state: [u32; 5], block: [u32; 16]) -> [u32; 5] {
|
||||
let a = state[0];
|
||||
let b = state[1];
|
||||
let c = state[2];
|
||||
let d = state[3];
|
||||
let e = state[4];
|
||||
let (block, b, e) = r0(block, a, b, c, d, e, 0);
|
||||
let (block, a, d) = r0(block, e, a, b, c, d, 1);
|
||||
let (block, e, c) = r0(block, d, e, a, b, c, 2);
|
||||
let (block, d, b) = r0(block, c, d, e, a, b, 3);
|
||||
let (block, c, a) = r0(block, b, c, d, e, a, 4);
|
||||
let (block, b, e) = r0(block, a, b, c, d, e, 5);
|
||||
let (block, a, d) = r0(block, e, a, b, c, d, 6);
|
||||
let (block, e, c) = r0(block, d, e, a, b, c, 7);
|
||||
let (block, d, b) = r0(block, c, d, e, a, b, 8);
|
||||
let (block, c, a) = r0(block, b, c, d, e, a, 9);
|
||||
let (block, b, e) = r0(block, a, b, c, d, e, 10);
|
||||
let (block, a, d) = r0(block, e, a, b, c, d, 11);
|
||||
let (block, e, c) = r0(block, d, e, a, b, c, 12);
|
||||
let (block, d, b) = r0(block, c, d, e, a, b, 13);
|
||||
let (block, c, a) = r0(block, b, c, d, e, a, 14);
|
||||
let (block, b, e) = r0(block, a, b, c, d, e, 15);
|
||||
let (block, a, d) = r1(block, e, a, b, c, d, 0);
|
||||
let (block, e, c) = r1(block, d, e, a, b, c, 1);
|
||||
let (block, d, b) = r1(block, c, d, e, a, b, 2);
|
||||
let (block, c, a) = r1(block, b, c, d, e, a, 3);
|
||||
let (block, b, e) = r2(block, a, b, c, d, e, 4);
|
||||
let (block, a, d) = r2(block, e, a, b, c, d, 5);
|
||||
let (block, e, c) = r2(block, d, e, a, b, c, 6);
|
||||
let (block, d, b) = r2(block, c, d, e, a, b, 7);
|
||||
let (block, c, a) = r2(block, b, c, d, e, a, 8);
|
||||
let (block, b, e) = r2(block, a, b, c, d, e, 9);
|
||||
let (block, a, d) = r2(block, e, a, b, c, d, 10);
|
||||
let (block, e, c) = r2(block, d, e, a, b, c, 11);
|
||||
let (block, d, b) = r2(block, c, d, e, a, b, 12);
|
||||
let (block, c, a) = r2(block, b, c, d, e, a, 13);
|
||||
let (block, b, e) = r2(block, a, b, c, d, e, 14);
|
||||
let (block, a, d) = r2(block, e, a, b, c, d, 15);
|
||||
let (block, e, c) = r2(block, d, e, a, b, c, 0);
|
||||
let (block, d, b) = r2(block, c, d, e, a, b, 1);
|
||||
let (block, c, a) = r2(block, b, c, d, e, a, 2);
|
||||
let (block, b, e) = r2(block, a, b, c, d, e, 3);
|
||||
let (block, a, d) = r2(block, e, a, b, c, d, 4);
|
||||
let (block, e, c) = r2(block, d, e, a, b, c, 5);
|
||||
let (block, d, b) = r2(block, c, d, e, a, b, 6);
|
||||
let (block, c, a) = r2(block, b, c, d, e, a, 7);
|
||||
let (block, b, e) = r3(block, a, b, c, d, e, 8);
|
||||
let (block, a, d) = r3(block, e, a, b, c, d, 9);
|
||||
let (block, e, c) = r3(block, d, e, a, b, c, 10);
|
||||
let (block, d, b) = r3(block, c, d, e, a, b, 11);
|
||||
let (block, c, a) = r3(block, b, c, d, e, a, 12);
|
||||
let (block, b, e) = r3(block, a, b, c, d, e, 13);
|
||||
let (block, a, d) = r3(block, e, a, b, c, d, 14);
|
||||
let (block, e, c) = r3(block, d, e, a, b, c, 15);
|
||||
let (block, d, b) = r3(block, c, d, e, a, b, 0);
|
||||
let (block, c, a) = r3(block, b, c, d, e, a, 1);
|
||||
let (block, b, e) = r3(block, a, b, c, d, e, 2);
|
||||
let (block, a, d) = r3(block, e, a, b, c, d, 3);
|
||||
let (block, e, c) = r3(block, d, e, a, b, c, 4);
|
||||
let (block, d, b) = r3(block, c, d, e, a, b, 5);
|
||||
let (block, c, a) = r3(block, b, c, d, e, a, 6);
|
||||
let (block, b, e) = r3(block, a, b, c, d, e, 7);
|
||||
let (block, a, d) = r3(block, e, a, b, c, d, 8);
|
||||
let (block, e, c) = r3(block, d, e, a, b, c, 9);
|
||||
let (block, d, b) = r3(block, c, d, e, a, b, 10);
|
||||
let (block, c, a) = r3(block, b, c, d, e, a, 11);
|
||||
let (block, b, e) = r4(block, a, b, c, d, e, 12);
|
||||
let (block, a, d) = r4(block, e, a, b, c, d, 13);
|
||||
let (block, e, c) = r4(block, d, e, a, b, c, 14);
|
||||
let (block, d, b) = r4(block, c, d, e, a, b, 15);
|
||||
let (block, c, a) = r4(block, b, c, d, e, a, 0);
|
||||
let (block, b, e) = r4(block, a, b, c, d, e, 1);
|
||||
let (block, a, d) = r4(block, e, a, b, c, d, 2);
|
||||
let (block, e, c) = r4(block, d, e, a, b, c, 3);
|
||||
let (block, d, b) = r4(block, c, d, e, a, b, 4);
|
||||
let (block, c, a) = r4(block, b, c, d, e, a, 5);
|
||||
let (block, b, e) = r4(block, a, b, c, d, e, 6);
|
||||
let (block, a, d) = r4(block, e, a, b, c, d, 7);
|
||||
let (block, e, c) = r4(block, d, e, a, b, c, 8);
|
||||
let (block, d, b) = r4(block, c, d, e, a, b, 9);
|
||||
let (block, c, a) = r4(block, b, c, d, e, a, 10);
|
||||
let (block, b, e) = r4(block, a, b, c, d, e, 11);
|
||||
let (block, a, d) = r4(block, e, a, b, c, d, 12);
|
||||
let (block, e, c) = r4(block, d, e, a, b, c, 13);
|
||||
let (block, d, b) = r4(block, c, d, e, a, b, 14);
|
||||
let (_, c, a) = r4(block, b, c, d, e, a, 15);
|
||||
|
||||
state[0] = state[0].wrapping_add(a);
|
||||
state[1] = state[1].wrapping_add(b);
|
||||
state[2] = state[2].wrapping_add(c);
|
||||
state[3] = state[3].wrapping_add(d);
|
||||
state[4] = state[4].wrapping_add(e);
|
||||
state
|
||||
}
|
||||
|
||||
const fn digest(mut state: [u32; 5], len: u64, blocks: Blocks) -> Digest {
|
||||
const fn clone_from_slice_128(
|
||||
mut data: [u8; 128],
|
||||
slice: &[u8],
|
||||
offset: usize,
|
||||
num_elems: usize,
|
||||
) -> [u8; 128] {
|
||||
let mut i = 0;
|
||||
while i < num_elems {
|
||||
data[i] = slice[offset + i];
|
||||
i += 1;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
const fn clone_slice_128(mut data: [u8; 128], slice: &[u8], _offset: usize) -> [u8; 128] {
|
||||
let mut i = 0;
|
||||
while i < slice.len() {
|
||||
data[_offset + i] = slice[i];
|
||||
i += 1;
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
const fn as_block(input: &[u8], offset: usize) -> [u32; 16] {
|
||||
let mut result = [0u32; 16];
|
||||
|
||||
let mut i = 0;
|
||||
while i != 16 {
|
||||
let off = offset + (i * 4);
|
||||
result[i] = (input[off + 3] as u32)
|
||||
| ((input[off + 2] as u32) << 8)
|
||||
| ((input[off + 1] as u32) << 16)
|
||||
| ((input[off] as u32) << 24);
|
||||
i += 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
let bits = (len + (blocks.len as u64)) * 8;
|
||||
let extra = [
|
||||
(bits >> 56) as u8,
|
||||
(bits >> 48) as u8,
|
||||
(bits >> 40) as u8,
|
||||
(bits >> 32) as u8,
|
||||
(bits >> 24) as u8,
|
||||
(bits >> 16) as u8,
|
||||
(bits >> 8) as u8,
|
||||
bits as u8,
|
||||
];
|
||||
let mut last = [0; 128];
|
||||
let blocklen = blocks.len as usize;
|
||||
last = clone_from_slice_128(last, &blocks.data, 0, blocklen);
|
||||
last[blocklen] = 0x80;
|
||||
|
||||
if blocklen < 56 {
|
||||
last = clone_slice_128(last, &extra, 56);
|
||||
state = process_state(state, as_block(&last, 0));
|
||||
} else {
|
||||
last = clone_slice_128(last, &extra, 120);
|
||||
state = process_state(state, as_block(&last, 0));
|
||||
state = process_state(state, as_block(&last, 64));
|
||||
}
|
||||
Digest { data: state }
|
||||
}
|
||||
|
||||
const fn rol(value: u32, bits: usize) -> u32 {
|
||||
(value << bits) | (value >> (32 - bits))
|
||||
}
|
||||
|
||||
const fn blk(block: &[u32], i: usize) -> u32 {
|
||||
let value = block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i];
|
||||
rol(value, 1)
|
||||
}
|
||||
|
||||
const fn r0(
|
||||
block: [u32; 16],
|
||||
v: u32,
|
||||
mut w: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
mut z: u32,
|
||||
i: usize,
|
||||
) -> ([u32; 16], u32, u32) {
|
||||
let n = ((w & (x ^ y)) ^ y)
|
||||
.wrapping_add(block[i])
|
||||
.wrapping_add(0x5a82_7999)
|
||||
.wrapping_add(rol(v, 5));
|
||||
z = z.wrapping_add(n);
|
||||
w = rol(w, 30);
|
||||
(block, w, z)
|
||||
}
|
||||
|
||||
const fn r1(
|
||||
mut block: [u32; 16],
|
||||
v: u32,
|
||||
mut w: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
mut z: u32,
|
||||
i: usize,
|
||||
) -> ([u32; 16], u32, u32) {
|
||||
block[i] = blk(&block, i);
|
||||
let n = ((w & (x ^ y)) ^ y)
|
||||
.wrapping_add(block[i])
|
||||
.wrapping_add(0x5a82_7999)
|
||||
.wrapping_add(rol(v, 5));
|
||||
z = z.wrapping_add(n);
|
||||
w = rol(w, 30);
|
||||
(block, w, z)
|
||||
}
|
||||
|
||||
const fn r2(
|
||||
mut block: [u32; 16],
|
||||
v: u32,
|
||||
mut w: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
mut z: u32,
|
||||
i: usize,
|
||||
) -> ([u32; 16], u32, u32) {
|
||||
block[i] = blk(&block, i);
|
||||
let n = (w ^ x ^ y)
|
||||
.wrapping_add(block[i])
|
||||
.wrapping_add(0x6ed9_eba1)
|
||||
.wrapping_add(rol(v, 5));
|
||||
z = z.wrapping_add(n);
|
||||
w = rol(w, 30);
|
||||
(block, w, z)
|
||||
}
|
||||
|
||||
const fn r3(
|
||||
mut block: [u32; 16],
|
||||
v: u32,
|
||||
mut w: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
mut z: u32,
|
||||
i: usize,
|
||||
) -> ([u32; 16], u32, u32) {
|
||||
block[i] = blk(&block, i);
|
||||
let n = (((w | x) & y) | (w & x))
|
||||
.wrapping_add(block[i])
|
||||
.wrapping_add(0x8f1b_bcdc)
|
||||
.wrapping_add(rol(v, 5));
|
||||
z = z.wrapping_add(n);
|
||||
w = rol(w, 30);
|
||||
(block, w, z)
|
||||
}
|
||||
|
||||
const fn r4(
|
||||
mut block: [u32; 16],
|
||||
v: u32,
|
||||
mut w: u32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
mut z: u32,
|
||||
i: usize,
|
||||
) -> ([u32; 16], u32, u32) {
|
||||
block[i] = blk(&block, i);
|
||||
let n = (w ^ x ^ y)
|
||||
.wrapping_add(block[i])
|
||||
.wrapping_add(0xca62_c1d6)
|
||||
.wrapping_add(rol(v, 5));
|
||||
z = z.wrapping_add(n);
|
||||
w = rol(w, 30);
|
||||
(block, w, z)
|
||||
}
|
||||
|
||||
pub struct Digest {
|
||||
data: [u32; 5],
|
||||
}
|
||||
|
||||
impl Digest {
|
||||
pub const fn bytes(&self) -> [u8; 20] {
|
||||
[
|
||||
(self.data[0] >> 24) as u8,
|
||||
(self.data[0] >> 16) as u8,
|
||||
(self.data[0] >> 8) as u8,
|
||||
self.data[0] as u8,
|
||||
(self.data[1] >> 24) as u8,
|
||||
(self.data[1] >> 16) as u8,
|
||||
(self.data[1] >> 8) as u8,
|
||||
self.data[1] as u8,
|
||||
(self.data[2] >> 24) as u8,
|
||||
(self.data[2] >> 16) as u8,
|
||||
(self.data[2] >> 8) as u8,
|
||||
self.data[2] as u8,
|
||||
(self.data[3] >> 24) as u8,
|
||||
(self.data[3] >> 16) as u8,
|
||||
(self.data[3] >> 8) as u8,
|
||||
self.data[3] as u8,
|
||||
(self.data[4] >> 24) as u8,
|
||||
(self.data[4] >> 16) as u8,
|
||||
(self.data[4] >> 8) as u8,
|
||||
self.data[4] as u8,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Digest {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
for i in self.data.iter() {
|
||||
write!(f, "{i:08x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
use super::*;
|
||||
use crate::{IUnknown, IUnknown_Vtbl, Interface, GUID, HRESULT};
|
||||
use core::ffi::c_void;
|
||||
use core::mem::{transmute, transmute_copy};
|
||||
use core::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicIsize, Ordering};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Default)]
|
||||
pub struct WeakRefCount(AtomicIsize);
|
||||
|
||||
impl WeakRefCount {
|
||||
pub const fn new() -> Self {
|
||||
Self(AtomicIsize::new(1))
|
||||
}
|
||||
|
||||
pub fn add_ref(&self) -> u32 {
|
||||
self.0
|
||||
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |count_or_pointer| {
|
||||
bool::then_some(!is_weak_ref(count_or_pointer), count_or_pointer + 1)
|
||||
})
|
||||
.map(|u| u as u32 + 1)
|
||||
.unwrap_or_else(|pointer| unsafe { TearOff::decode(pointer).strong_count.add_ref() })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_one(&self) -> bool {
|
||||
self.0.load(Ordering::Acquire) == 1
|
||||
}
|
||||
|
||||
pub fn release(&self) -> u32 {
|
||||
self.0
|
||||
.fetch_update(Ordering::Release, Ordering::Relaxed, |count_or_pointer| {
|
||||
bool::then_some(!is_weak_ref(count_or_pointer), count_or_pointer - 1)
|
||||
})
|
||||
.map(|u| u as u32 - 1)
|
||||
.unwrap_or_else(|pointer| unsafe {
|
||||
let tear_off = TearOff::decode(pointer);
|
||||
let remaining = tear_off.strong_count.release();
|
||||
|
||||
// If this is the last strong reference, we can release the weak reference implied by the strong reference.
|
||||
// There may still be weak references, so the WeakRelease is called to handle such possibilities.
|
||||
if remaining == 0 {
|
||||
TearOff::WeakRelease(&mut tear_off.weak_vtable as *mut _ as _);
|
||||
}
|
||||
|
||||
remaining
|
||||
})
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
pub unsafe fn query(&self, iid: &GUID, object: *mut c_void) -> *mut c_void {
|
||||
unsafe {
|
||||
if iid != &IWeakReferenceSource::IID {
|
||||
return null_mut();
|
||||
}
|
||||
|
||||
let mut count_or_pointer = self.0.load(Ordering::Relaxed);
|
||||
|
||||
if is_weak_ref(count_or_pointer) {
|
||||
return TearOff::from_encoding(count_or_pointer);
|
||||
}
|
||||
|
||||
let tear_off = TearOff::new(object, count_or_pointer as u32);
|
||||
let tear_off_ptr: *mut c_void = transmute_copy(&tear_off);
|
||||
let encoding: usize = ((tear_off_ptr as usize) >> 1) | (1 << (usize::BITS - 1));
|
||||
|
||||
loop {
|
||||
match self.0.compare_exchange_weak(
|
||||
count_or_pointer,
|
||||
encoding as isize,
|
||||
Ordering::AcqRel,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
let result: *mut c_void = transmute(tear_off);
|
||||
TearOff::from_strong_ptr(result).strong_count.add_ref();
|
||||
return result;
|
||||
}
|
||||
Err(pointer) => count_or_pointer = pointer,
|
||||
}
|
||||
|
||||
if is_weak_ref(count_or_pointer) {
|
||||
return TearOff::from_encoding(count_or_pointer);
|
||||
}
|
||||
|
||||
TearOff::from_strong_ptr(tear_off_ptr)
|
||||
.strong_count
|
||||
.0
|
||||
.store(count_or_pointer as i32, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_weak_ref(value: isize) -> bool {
|
||||
value < 0
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TearOff {
|
||||
strong_vtable: *const IWeakReferenceSource_Vtbl,
|
||||
weak_vtable: *const IWeakReference_Vtbl,
|
||||
object: *mut c_void,
|
||||
strong_count: RefCount,
|
||||
weak_count: RefCount,
|
||||
}
|
||||
|
||||
impl TearOff {
|
||||
#[expect(clippy::new_ret_no_self)]
|
||||
unsafe fn new(object: *mut c_void, strong_count: u32) -> IWeakReferenceSource {
|
||||
unsafe {
|
||||
transmute(Box::new(Self {
|
||||
strong_vtable: &Self::STRONG_VTABLE,
|
||||
weak_vtable: &Self::WEAK_VTABLE,
|
||||
object,
|
||||
strong_count: RefCount::new(strong_count),
|
||||
weak_count: RefCount::new(1),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn from_encoding(encoding: isize) -> *mut c_void {
|
||||
unsafe {
|
||||
let tear_off = Self::decode(encoding);
|
||||
tear_off.strong_count.add_ref();
|
||||
tear_off as *mut _ as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
const STRONG_VTABLE: IWeakReferenceSource_Vtbl = IWeakReferenceSource_Vtbl {
|
||||
base__: IUnknown_Vtbl {
|
||||
QueryInterface: Self::StrongQueryInterface,
|
||||
AddRef: Self::StrongAddRef,
|
||||
Release: Self::StrongRelease,
|
||||
},
|
||||
GetWeakReference: Self::StrongDowngrade,
|
||||
};
|
||||
|
||||
const WEAK_VTABLE: IWeakReference_Vtbl = IWeakReference_Vtbl {
|
||||
base__: IUnknown_Vtbl {
|
||||
QueryInterface: Self::WeakQueryInterface,
|
||||
AddRef: Self::WeakAddRef,
|
||||
Release: Self::WeakRelease,
|
||||
},
|
||||
Resolve: Self::WeakUpgrade,
|
||||
};
|
||||
|
||||
unsafe fn from_strong_ptr<'a>(this: *mut c_void) -> &'a mut Self {
|
||||
unsafe { &mut *(this as *mut *mut c_void as *mut Self) }
|
||||
}
|
||||
|
||||
unsafe fn from_weak_ptr<'a>(this: *mut c_void) -> &'a mut Self {
|
||||
unsafe { &mut *((this as *mut *mut c_void).sub(1) as *mut Self) }
|
||||
}
|
||||
|
||||
unsafe fn decode<'a>(value: isize) -> &'a mut Self {
|
||||
unsafe { &mut *((value << 1) as *mut Self) as &mut Self }
|
||||
}
|
||||
|
||||
unsafe fn query_interface(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT {
|
||||
unsafe {
|
||||
((*(*(self.object as *mut *mut IUnknown_Vtbl))).QueryInterface)(
|
||||
self.object,
|
||||
iid,
|
||||
interface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn StrongQueryInterface(
|
||||
ptr: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = Self::from_strong_ptr(ptr);
|
||||
|
||||
if iid.is_null() || interface.is_null() {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
// Only directly respond to queries for the the tear-off's strong interface. This is
|
||||
// effectively a self-query.
|
||||
if *iid == IWeakReferenceSource::IID {
|
||||
*interface = ptr;
|
||||
this.strong_count.add_ref();
|
||||
return HRESULT(0);
|
||||
}
|
||||
|
||||
// As the tear-off is sharing the identity of the object, simply delegate any remaining
|
||||
// queries to the object.
|
||||
this.query_interface(iid, interface)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn WeakQueryInterface(
|
||||
ptr: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = Self::from_weak_ptr(ptr);
|
||||
|
||||
if iid.is_null() || interface.is_null() {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
// While the weak vtable is packed into the same allocation as the strong vtable and
|
||||
// tear-off, it represents a distinct COM identity and thus does not share or delegate to
|
||||
// the object.
|
||||
|
||||
*interface = if *iid == IWeakReference::IID
|
||||
|| *iid == IUnknown::IID
|
||||
|| *iid == IAgileObject::IID
|
||||
{
|
||||
ptr
|
||||
} else {
|
||||
#[cfg(windows)]
|
||||
if *iid == IMarshal::IID {
|
||||
this.weak_count.add_ref();
|
||||
return marshaler(transmute::<*mut c_void, IUnknown>(ptr), interface);
|
||||
}
|
||||
|
||||
null_mut()
|
||||
};
|
||||
|
||||
if (*interface).is_null() {
|
||||
E_NOINTERFACE
|
||||
} else {
|
||||
this.weak_count.add_ref();
|
||||
HRESULT(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn StrongAddRef(ptr: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = Self::from_strong_ptr(ptr);
|
||||
|
||||
// Implement `AddRef` directly as we own the strong reference.
|
||||
this.strong_count.add_ref()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn WeakAddRef(ptr: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = Self::from_weak_ptr(ptr);
|
||||
|
||||
// Implement `AddRef` directly as we own the weak reference.
|
||||
this.weak_count.add_ref()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn StrongRelease(ptr: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = Self::from_strong_ptr(ptr);
|
||||
|
||||
// Forward strong `Release` to the object so that it can destroy itself. It will then
|
||||
// decrement its weak reference and allow the tear-off to be released as needed.
|
||||
((*(*(this.object as *mut *mut IUnknown_Vtbl))).Release)(this.object)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn WeakRelease(ptr: *mut c_void) -> u32 {
|
||||
unsafe {
|
||||
let this = Self::from_weak_ptr(ptr);
|
||||
|
||||
// Implement `Release` directly as we own the weak reference.
|
||||
let remaining = this.weak_count.release();
|
||||
|
||||
// If there are no remaining references, it means that the object has already been
|
||||
// destroyed. Go ahead and destroy the tear-off.
|
||||
if remaining == 0 {
|
||||
let _ = Box::from_raw(this);
|
||||
}
|
||||
|
||||
remaining
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn StrongDowngrade(
|
||||
ptr: *mut c_void,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = Self::from_strong_ptr(ptr);
|
||||
|
||||
// The strong vtable hands out a reference to the weak vtable. This is always safe and
|
||||
// straightforward since a strong reference guarantees there is at least one weak
|
||||
// reference.
|
||||
*interface = &mut this.weak_vtable as *mut _ as _;
|
||||
this.weak_count.add_ref();
|
||||
HRESULT(0)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn WeakUpgrade(
|
||||
ptr: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = Self::from_weak_ptr(ptr);
|
||||
|
||||
this.strong_count
|
||||
.0
|
||||
.fetch_update(Ordering::Acquire, Ordering::Relaxed, |count| {
|
||||
// Attempt to acquire a strong reference count to stabilize the object for the duration
|
||||
// of the `QueryInterface` call.
|
||||
bool::then_some(count != 0, count + 1)
|
||||
})
|
||||
.map(|_| {
|
||||
// Let the object respond to the upgrade query.
|
||||
let result = this.query_interface(iid, interface);
|
||||
// Decrement the temporary reference account used to stabilize the object.
|
||||
this.strong_count.0.fetch_sub(1, Ordering::Relaxed);
|
||||
// Return the result of the query.
|
||||
result
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
*interface = null_mut();
|
||||
HRESULT(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
mod factory_cache;
|
||||
pub use factory_cache::*;
|
||||
|
||||
mod generic_factory;
|
||||
pub use generic_factory::*;
|
||||
|
||||
mod marshaler;
|
||||
pub use marshaler::*;
|
||||
|
||||
mod array_proxy;
|
||||
pub use array_proxy::*;
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
use super::*;
|
||||
use core::ffi::c_void;
|
||||
use core::ptr::null_mut;
|
||||
|
||||
/// Parent interface for all WinRT interfaces.
|
||||
///
|
||||
/// A WinRT object that may be used as a polymorphic stand-in for any WinRT class, interface, or boxed value.
|
||||
/// [`IInspectable`] represents the
|
||||
/// [IInspectable](https://docs.microsoft.com/en-us/windows/win32/api/inspectable/nn-inspectable-iinspectable)
|
||||
/// interface.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct IInspectable(pub IUnknown);
|
||||
|
||||
interface_hierarchy!(IInspectable, IUnknown);
|
||||
|
||||
impl IInspectable {
|
||||
/// Returns the canonical type name for the underlying object.
|
||||
#[cfg(windows)]
|
||||
pub fn GetRuntimeClassName(&self) -> Result<HSTRING> {
|
||||
unsafe {
|
||||
let mut abi = null_mut();
|
||||
(self.vtable().GetRuntimeClassName)(core::mem::transmute_copy(self), &mut abi).ok()?;
|
||||
Ok(core::mem::transmute::<*mut c_void, HSTRING>(abi))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the trust level of the current object.
|
||||
pub fn GetTrustLevel(&self) -> Result<i32> {
|
||||
unsafe {
|
||||
let mut value = 0;
|
||||
(self.vtable().GetTrustLevel)(core::mem::transmute_copy(self), &mut value).ok()?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[repr(C)]
|
||||
pub struct IInspectable_Vtbl {
|
||||
pub base: IUnknown_Vtbl,
|
||||
pub GetIids: unsafe extern "system" fn(
|
||||
this: *mut c_void,
|
||||
count: *mut u32,
|
||||
values: *mut *mut GUID,
|
||||
) -> HRESULT,
|
||||
pub GetRuntimeClassName:
|
||||
unsafe extern "system" fn(this: *mut c_void, value: *mut *mut c_void) -> HRESULT,
|
||||
pub GetTrustLevel: unsafe extern "system" fn(this: *mut c_void, value: *mut i32) -> HRESULT,
|
||||
}
|
||||
|
||||
unsafe impl Interface for IInspectable {
|
||||
type Vtable = IInspectable_Vtbl;
|
||||
const IID: GUID = GUID::from_u128(0xaf86e2e0_b12d_4c6a_9c5a_d7aa65101e90);
|
||||
}
|
||||
|
||||
impl RuntimeType for IInspectable {
|
||||
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"cinterface(IInspectable)");
|
||||
}
|
||||
|
||||
impl RuntimeName for IInspectable {}
|
||||
|
||||
impl IInspectable_Vtbl {
|
||||
pub const fn new<Identity: IUnknownImpl, Name: RuntimeName, const OFFSET: isize>() -> Self {
|
||||
unsafe extern "system" fn GetIids(
|
||||
_: *mut c_void,
|
||||
count: *mut u32,
|
||||
values: *mut *mut GUID,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
if count.is_null() || values.is_null() {
|
||||
return imp::E_POINTER;
|
||||
}
|
||||
// Note: even if we end up implementing this in future, it still doesn't need a this pointer
|
||||
// since the data to be returned is type- not instance-specific so can be shared for all
|
||||
// interfaces.
|
||||
*count = 0;
|
||||
*values = null_mut();
|
||||
HRESULT(0)
|
||||
}
|
||||
}
|
||||
unsafe extern "system" fn GetRuntimeClassName<T: RuntimeName>(
|
||||
_: *mut c_void,
|
||||
value: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
if value.is_null() {
|
||||
return imp::E_POINTER;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
*value = core::mem::transmute::<HSTRING, *mut c_void>(T::NAME.into());
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
*value = core::ptr::null_mut();
|
||||
}
|
||||
|
||||
HRESULT(0)
|
||||
}
|
||||
}
|
||||
unsafe extern "system" fn GetTrustLevel<T: IUnknownImpl, const OFFSET: isize>(
|
||||
this: *mut c_void,
|
||||
value: *mut i32,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
if value.is_null() {
|
||||
return imp::E_POINTER;
|
||||
}
|
||||
let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T;
|
||||
(*this).GetTrustLevel(value)
|
||||
}
|
||||
}
|
||||
Self {
|
||||
base: IUnknown_Vtbl::new::<Identity, OFFSET>(),
|
||||
GetIids,
|
||||
GetRuntimeClassName: GetRuntimeClassName::<Name>,
|
||||
GetTrustLevel: GetTrustLevel::<Identity, OFFSET>,
|
||||
}
|
||||
}
|
||||
}
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
use super::*;
|
||||
use core::any::Any;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{forget, transmute_copy, MaybeUninit};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
/// Provides low-level access to an interface vtable.
|
||||
///
|
||||
/// This trait is automatically implemented by the generated bindings and should not be
|
||||
/// implemented manually.
|
||||
///
|
||||
/// # Safety
|
||||
pub unsafe trait Interface: Sized + Clone {
|
||||
#[doc(hidden)]
|
||||
type Vtable;
|
||||
|
||||
/// The `GUID` associated with the interface.
|
||||
const IID: GUID;
|
||||
|
||||
#[doc(hidden)]
|
||||
const UNKNOWN: bool = true;
|
||||
|
||||
/// A reference to the interface's vtable
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
fn vtable(&self) -> &Self::Vtable {
|
||||
// SAFETY: the implementor of the trait guarantees that `Self` is castable to its vtable
|
||||
unsafe { self.assume_vtable::<Self>() }
|
||||
}
|
||||
|
||||
/// Cast this interface as a reference to the supplied interfaces `Vtable`
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is safe if `T` is an equivalent interface to `Self` or a super interface.
|
||||
/// In other words, `T::Vtable` must be equivalent to the beginning of `Self::Vtable`.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
unsafe fn assume_vtable<T: Interface>(&self) -> &T::Vtable {
|
||||
unsafe { &**(self.as_raw() as *mut *mut T::Vtable) }
|
||||
}
|
||||
|
||||
/// Returns the raw COM interface pointer. The resulting pointer continues to be owned by the `Interface` implementation.
|
||||
#[inline(always)]
|
||||
fn as_raw(&self) -> *mut c_void {
|
||||
// SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation
|
||||
unsafe { transmute_copy(self) }
|
||||
}
|
||||
|
||||
/// Returns the raw COM interface pointer and releases ownership. It the caller's responsibility to release the COM interface pointer.
|
||||
#[inline(always)]
|
||||
fn into_raw(self) -> *mut c_void {
|
||||
// SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation
|
||||
let raw = self.as_raw();
|
||||
forget(self);
|
||||
raw
|
||||
}
|
||||
|
||||
/// Creates an `Interface` by taking ownership of the `raw` COM interface pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `raw` pointer must be owned by the caller and represent a valid COM interface pointer. In other words,
|
||||
/// it must point to a vtable beginning with the `IUnknown` function pointers and match the vtable of `Interface`.
|
||||
unsafe fn from_raw(raw: *mut c_void) -> Self {
|
||||
unsafe { transmute_copy(&raw) }
|
||||
}
|
||||
|
||||
/// Creates an `Interface` that is valid so long as the `raw` COM interface pointer is valid.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `raw` pointer must be a valid COM interface pointer. In other words, it must point to a vtable
|
||||
/// beginning with the `IUnknown` function pointers and match the vtable of `Interface`.
|
||||
#[inline(always)]
|
||||
unsafe fn from_raw_borrowed(raw: &*mut c_void) -> Option<&Self> {
|
||||
unsafe {
|
||||
if raw.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(transmute_copy(&raw))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to cast the current interface to another interface using `QueryInterface`.
|
||||
///
|
||||
/// The name `cast` is preferred to `query` because there is a WinRT method named query but not one
|
||||
/// named cast.
|
||||
#[inline(always)]
|
||||
fn cast<T: Interface>(&self) -> Result<T> {
|
||||
// SAFETY: `result` is valid for writing an interface pointer and it is safe
|
||||
// to cast the `result` pointer as `T` on success because we are using the `IID` tied
|
||||
// to `T` which the implementor of `Interface` has guaranteed is correct
|
||||
unsafe {
|
||||
// If query() returns a failure code then we propagate that failure code to the caller.
|
||||
// In that case, we ignore the contents of 'result' (which will _not_ be dropped,
|
||||
// because MaybeUninit intentionally does not drop its contents).
|
||||
//
|
||||
// This guards against implementations of COM interfaces which may store non-null values
|
||||
// in 'result' but still return E_NOINTERFACE.
|
||||
let mut result = MaybeUninit::<Option<T>>::zeroed();
|
||||
self.query(&T::IID, result.as_mut_ptr() as _).ok()?;
|
||||
|
||||
// If we get here, then query() has succeeded, but we still need to double-check
|
||||
// that the output pointer is non-null.
|
||||
if let Some(obj) = result.assume_init() {
|
||||
Ok(obj)
|
||||
} else {
|
||||
Err(imp::E_POINTER.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This casts the given COM interface to [`&dyn Any`].
|
||||
///
|
||||
/// Applications should generally _not_ call this method directly. Instead, use the
|
||||
/// [`Interface::cast_object_ref`] or [`Interface::cast_object`] methods.
|
||||
///
|
||||
/// `T` must be a type that has been annotated with `#[implement]`; this is checked at
|
||||
/// compile-time by the generic constraints of this method. However, note that the
|
||||
/// returned `&dyn Any` refers to the _outer_ implementation object that was generated by
|
||||
/// `#[implement]`, i.e. the `MyApp_Impl` type, not the inner `MyApp` type.
|
||||
///
|
||||
/// If the given object is not a Rust object, or is a Rust object but not `T`, or is a Rust
|
||||
/// object that contains non-static lifetimes, then this function will return `Err(E_NOINTERFACE)`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// **IMPORTANT!!** This uses a non-standard protocol for QueryInterface! The `DYNAMIC_CAST_IID`
|
||||
/// IID identifies this protocol, but there is no `IDynamicCast` interface. Instead, objects
|
||||
/// that recognize `DYNAMIC_CAST_IID` simply store their `&dyn Any` directly at the interface
|
||||
/// pointer that was passed to `QueryInterface. This means that the returned value has a
|
||||
/// size that is twice as large (`size_of::<&dyn Any>() == 2 * size_of::<*const c_void>()`).
|
||||
///
|
||||
/// This means that callers that use this protocol cannot simply pass `&mut ptr` for
|
||||
/// an ordinary single-pointer-sized pointer. Only this method understands this protocol.
|
||||
///
|
||||
/// Another part of this protocol is that the implementation of `QueryInterface` _does not_
|
||||
/// AddRef the object. The caller must guarantee the liveness of the COM object. In Rust,
|
||||
/// this means tying the lifetime of the IUnknown* that we used for the QueryInterface
|
||||
/// call to the lifetime of the returned `&dyn Any` value.
|
||||
///
|
||||
/// This method preserves type safety and relies on these invariants:
|
||||
///
|
||||
/// * All `QueryInterface` implementations that recognize `DYNAMIC_CAST_IID` are generated by
|
||||
/// the `#[implement]` macro and respect the rules described here.
|
||||
#[inline(always)]
|
||||
fn cast_to_any<T>(&self) -> Result<&dyn Any>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
T::Outer: Any + 'static + IUnknownImpl<Impl = T>,
|
||||
{
|
||||
unsafe {
|
||||
let mut any_ref_arg: MaybeUninit<&dyn Any> = MaybeUninit::zeroed();
|
||||
self.query(
|
||||
&DYNAMIC_CAST_IID,
|
||||
any_ref_arg.as_mut_ptr() as *mut *mut c_void,
|
||||
)
|
||||
.ok()?;
|
||||
Ok(any_ref_arg.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given COM interface refers to an implementation of `T`.
|
||||
///
|
||||
/// `T` must be a type that has been annotated with `#[implement]`; this is checked at
|
||||
/// compile-time by the generic constraints of this method.
|
||||
///
|
||||
/// If the given object is not a Rust object, or is a Rust object but not `T`, or is a Rust
|
||||
/// object that contains non-static lifetimes, then this function will return `false`.
|
||||
#[inline(always)]
|
||||
fn is_object<T>(&self) -> bool
|
||||
where
|
||||
T: ComObjectInner,
|
||||
T::Outer: Any + 'static + IUnknownImpl<Impl = T>,
|
||||
{
|
||||
if let Ok(any) = self.cast_to_any::<T>() {
|
||||
any.is::<T::Outer>()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// This casts the given COM interface to [`&dyn Any`]. It returns a reference to the "outer"
|
||||
/// object, e.g. `&MyApp_Impl`, not the inner `&MyApp` object.
|
||||
///
|
||||
/// `T` must be a type that has been annotated with `#[implement]`; this is checked at
|
||||
/// compile-time by the generic constraints of this method. However, note that the
|
||||
/// returned `&dyn Any` refers to the _outer_ implementation object that was generated by
|
||||
/// `#[implement]`, i.e. the `MyApp_Impl` type, not the inner `MyApp` type.
|
||||
///
|
||||
/// If the given object is not a Rust object, or is a Rust object but not `T`, or is a Rust
|
||||
/// object that contains non-static lifetimes, then this function will return `Err(E_NOINTERFACE)`.
|
||||
///
|
||||
/// The returned value is borrowed. If you need an owned (counted) reference, then use
|
||||
/// [`Interface::cast_object`].
|
||||
#[inline(always)]
|
||||
fn cast_object_ref<T>(&self) -> Result<&T::Outer>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
T::Outer: Any + 'static + IUnknownImpl<Impl = T>,
|
||||
{
|
||||
let any: &dyn Any = self.cast_to_any::<T>()?;
|
||||
if let Some(outer) = any.downcast_ref::<T::Outer>() {
|
||||
Ok(outer)
|
||||
} else {
|
||||
Err(imp::E_NOINTERFACE.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// This casts the given COM interface to [`&dyn Any`]. It returns a reference to the "outer"
|
||||
/// object, e.g. `MyApp_Impl`, not the inner `MyApp` object.
|
||||
///
|
||||
/// `T` must be a type that has been annotated with `#[implement]`; this is checked at
|
||||
/// compile-time by the generic constraints of this method. However, note that the
|
||||
/// returned `&dyn Any` refers to the _outer_ implementation object that was generated by
|
||||
/// `#[implement]`, i.e. the `MyApp_Impl` type, not the inner `MyApp` type.
|
||||
///
|
||||
/// If the given object is not a Rust object, or is a Rust object but not `T`, or is a Rust
|
||||
/// object that contains non-static lifetimes, then this function will return `Err(E_NOINTERFACE)`.
|
||||
///
|
||||
/// The returned value is an owned (counted) reference; this function calls `AddRef` on the
|
||||
/// underlying COM object. If you do not need an owned reference, then you can use the
|
||||
/// [`Interface::cast_object_ref`] method instead, and avoid the cost of `AddRef` / `Release`.
|
||||
#[inline(always)]
|
||||
fn cast_object<T>(&self) -> Result<ComObject<T>>
|
||||
where
|
||||
T: ComObjectInner,
|
||||
T::Outer: Any + 'static + IUnknownImpl<Impl = T>,
|
||||
{
|
||||
let object_ref = self.cast_object_ref::<T>()?;
|
||||
Ok(object_ref.to_object())
|
||||
}
|
||||
|
||||
/// Attempts to create a [`Weak`] reference to this object.
|
||||
fn downgrade(&self) -> Result<Weak<Self>> {
|
||||
self.cast::<imp::IWeakReferenceSource>()
|
||||
.map(|source| Weak::downgrade(&source))
|
||||
}
|
||||
|
||||
/// Call `QueryInterface` on this interface
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `interface` must be a non-null, valid pointer for writing an interface pointer.
|
||||
#[inline(always)]
|
||||
unsafe fn query(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT {
|
||||
unsafe {
|
||||
if Self::UNKNOWN {
|
||||
(self.assume_vtable::<IUnknown>().QueryInterface)(self.as_raw(), iid, interface)
|
||||
} else {
|
||||
panic!("Non-COM interfaces cannot be queried.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an `InterfaceRef` for this reference. The `InterfaceRef` tracks lifetimes statically,
|
||||
/// and eliminates the need for dynamic reference count adjustments (AddRef/Release).
|
||||
fn to_ref(&self) -> InterfaceRef<'_, Self> {
|
||||
InterfaceRef::from_interface(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// This has the same memory representation as `IFoo`, but represents a borrowed interface pointer.
|
||||
///
|
||||
/// This type has no `Drop` impl; it does not AddRef/Release the given interface. However, because
|
||||
/// it has a lifetime parameter, it always represents a non-null pointer to an interface.
|
||||
#[repr(transparent)]
|
||||
pub struct InterfaceRef<'a, I>(NonNull<c_void>, PhantomData<&'a I>);
|
||||
|
||||
impl<I> Copy for InterfaceRef<'_, I> {}
|
||||
|
||||
impl<I> Clone for InterfaceRef<'_, I> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: core::fmt::Debug + Interface> core::fmt::Debug for InterfaceRef<'_, I> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
<I as core::fmt::Debug>::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interface> InterfaceRef<'_, I> {
|
||||
/// Creates an `InterfaceRef` from a raw pointer. _This is extremely dangerous, since there
|
||||
/// is no lifetime tracking at all!_
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must guarantee that the `'a` lifetime parameter is bound by context to a correct
|
||||
/// lifetime.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_raw(ptr: NonNull<c_void>) -> Self {
|
||||
Self(ptr, PhantomData)
|
||||
}
|
||||
|
||||
/// Creates an `InterfaceRef` from an interface reference. This safely associates the lifetime
|
||||
/// of the interface reference with the `'a` parameter of `InterfaceRef`. This allows for
|
||||
/// lifetime checking _without_ calling AddRef/Release on the underlying lifetime, which can
|
||||
/// improve efficiency.
|
||||
#[inline(always)]
|
||||
pub fn from_interface(interface: &I) -> Self {
|
||||
unsafe {
|
||||
// SAFETY: new_unchecked() should be valid because Interface::as_raw should always
|
||||
// return a non-null pointer.
|
||||
Self(NonNull::new_unchecked(interface.as_raw()), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls AddRef on the underlying COM interface and returns an "owned" (counted) reference.
|
||||
#[inline(always)]
|
||||
pub fn to_owned(self) -> I {
|
||||
(*self).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'i: 'a, I: Interface> From<&'i I> for InterfaceRef<'a, I> {
|
||||
#[inline(always)]
|
||||
fn from(interface: &'a I) -> Self {
|
||||
InterfaceRef::from_interface(interface)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interface> core::ops::Deref for InterfaceRef<'_, I> {
|
||||
type Target = I;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &I {
|
||||
unsafe { core::mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This IID identifies a special protocol, used by [`Interface::cast_to_any`]. This is _not_
|
||||
/// an ordinary COM interface; it uses special lifetime rules and a larger interface pointer.
|
||||
/// See the comments on [`Interface::cast_to_any`].
|
||||
#[doc(hidden)]
|
||||
pub const DYNAMIC_CAST_IID: GUID = GUID::from_u128(0xae49d5cb_143f_431c_874c_2729336e4eca);
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#![doc = include_str!("../readme.md")]
|
||||
#![doc(html_no_source)]
|
||||
#![debugger_visualizer(natvis_file = "../windows-core.natvis")]
|
||||
#![cfg_attr(all(not(feature = "std")), no_std)]
|
||||
#![expect(
|
||||
non_snake_case,
|
||||
non_camel_case_types,
|
||||
dead_code,
|
||||
clippy::missing_transmute_annotations,
|
||||
clippy::upper_case_acronyms
|
||||
)]
|
||||
|
||||
#[cfg(windows)]
|
||||
include!("windows.rs");
|
||||
|
||||
extern crate self as windows_core;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod imp;
|
||||
|
||||
mod as_impl;
|
||||
mod com_object;
|
||||
mod guid;
|
||||
mod inspectable;
|
||||
mod interface;
|
||||
mod out_param;
|
||||
mod out_ref;
|
||||
mod param;
|
||||
mod param_value;
|
||||
mod r#ref;
|
||||
mod runtime_name;
|
||||
mod runtime_type;
|
||||
mod scoped_interface;
|
||||
mod r#type;
|
||||
mod unknown;
|
||||
mod weak;
|
||||
|
||||
pub use as_impl::*;
|
||||
pub use com_object::*;
|
||||
pub use guid::*;
|
||||
pub use inspectable::*;
|
||||
pub use interface::*;
|
||||
pub use out_param::*;
|
||||
pub use out_ref::*;
|
||||
pub use param::*;
|
||||
pub use param_value::*;
|
||||
pub use r#ref::*;
|
||||
pub use r#type::*;
|
||||
pub use runtime_name::*;
|
||||
pub use runtime_type::*;
|
||||
pub use scoped_interface::*;
|
||||
pub use unknown::*;
|
||||
pub use weak::*;
|
||||
pub use windows_implement::implement;
|
||||
pub use windows_interface::interface;
|
||||
pub use windows_link::link;
|
||||
pub use windows_result::*;
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
use super::*;
|
||||
use core::mem::{take, transmute_copy, zeroed};
|
||||
|
||||
/// Provides automatic parameter conversion in cases where the Windows API expects implicit conversion support.
|
||||
///
|
||||
/// This is a mutable version of [Param] meant to support out parameters.
|
||||
/// There is no need to implement this trait. Blanket implementations are provided for all applicable Windows types.
|
||||
pub trait OutParam<T: TypeKind, C = <T as TypeKind>::TypeKind>: Sized
|
||||
where
|
||||
T: Type<T>,
|
||||
{
|
||||
#[doc(hidden)]
|
||||
unsafe fn borrow_mut(&self) -> OutRef<'_, T>;
|
||||
}
|
||||
|
||||
impl<T> OutParam<T, CloneType> for &mut T
|
||||
where
|
||||
T: TypeKind<TypeKind = CloneType> + Clone + Default,
|
||||
{
|
||||
unsafe fn borrow_mut(&self) -> OutRef<'_, T> {
|
||||
unsafe {
|
||||
let this: &mut T = transmute_copy(self);
|
||||
take(this);
|
||||
transmute_copy(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutParam<T, CopyType> for &mut T
|
||||
where
|
||||
T: TypeKind<TypeKind = CopyType> + Clone + Default,
|
||||
{
|
||||
unsafe fn borrow_mut(&self) -> OutRef<'_, T> {
|
||||
unsafe { transmute_copy(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutParam<T, InterfaceType> for &mut Option<T>
|
||||
where
|
||||
T: TypeKind<TypeKind = InterfaceType> + Clone,
|
||||
{
|
||||
unsafe fn borrow_mut(&self) -> OutRef<'_, T> {
|
||||
unsafe {
|
||||
let this: &mut Option<T> = transmute_copy(self);
|
||||
take(this);
|
||||
transmute_copy(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OutParam<T> for Option<&mut T>
|
||||
where
|
||||
T: Type<T>,
|
||||
{
|
||||
unsafe fn borrow_mut(&self) -> OutRef<'_, T> {
|
||||
unsafe {
|
||||
match self {
|
||||
Some(this) => transmute_copy(this),
|
||||
None => zeroed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
use super::*;
|
||||
|
||||
/// A borrowed type with the same memory layout as the type itself that can be used to construct ABI-compatible function signatures.
|
||||
///
|
||||
/// This is a mutable version of [Ref] meant to support out parameters.
|
||||
#[repr(transparent)]
|
||||
pub struct OutRef<'a, T: Type<T>>(*mut T::Abi, core::marker::PhantomData<&'a T>);
|
||||
|
||||
impl<T: Type<T>> OutRef<'_, T> {
|
||||
/// Returns `true` if the argument is null.
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.0.is_null()
|
||||
}
|
||||
|
||||
/// Overwrites a memory location with the given value without reading or dropping the old value.
|
||||
pub fn write(self, value: T::Default) -> Result<()> {
|
||||
if self.0.is_null() {
|
||||
Err(Error::from_hresult(imp::E_POINTER))
|
||||
} else {
|
||||
unsafe { *self.0 = core::mem::transmute_copy(&value) }
|
||||
core::mem::forget(value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type<T>> From<&'a mut T::Default> for OutRef<'a, T> {
|
||||
fn from(from: &'a mut T::Default) -> Self {
|
||||
unsafe { core::mem::transmute(from) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Default for OutRef<'_, T> {
|
||||
fn default() -> Self {
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
use super::*;
|
||||
use core::mem::transmute_copy;
|
||||
use core::mem::zeroed;
|
||||
|
||||
/// Provides automatic parameter conversion in cases where the Windows API expects implicit conversion support.
|
||||
///
|
||||
/// There is no need to implement this trait. Blanket implementations are provided for all applicable Windows types.
|
||||
pub trait Param<T: TypeKind, C = <T as TypeKind>::TypeKind>: Sized
|
||||
where
|
||||
T: Type<T>,
|
||||
{
|
||||
#[doc(hidden)]
|
||||
unsafe fn param(self) -> ParamValue<T>;
|
||||
}
|
||||
|
||||
impl<T> Param<T> for Option<&T>
|
||||
where
|
||||
T: Type<T>,
|
||||
{
|
||||
unsafe fn param(self) -> ParamValue<T> {
|
||||
unsafe {
|
||||
ParamValue::Borrowed(match self {
|
||||
Some(item) => transmute_copy(item),
|
||||
None => zeroed(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Param<T> for InterfaceRef<'_, T>
|
||||
where
|
||||
T: Type<T>,
|
||||
{
|
||||
unsafe fn param(self) -> ParamValue<T> {
|
||||
unsafe { ParamValue::Borrowed(transmute_copy(&self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Param<T, InterfaceType> for &U
|
||||
where
|
||||
T: TypeKind<TypeKind = InterfaceType> + Clone,
|
||||
T: Interface,
|
||||
U: Interface,
|
||||
U: imp::CanInto<T>,
|
||||
{
|
||||
unsafe fn param(self) -> ParamValue<T> {
|
||||
unsafe {
|
||||
if U::QUERY {
|
||||
self.cast()
|
||||
.map_or(ParamValue::Borrowed(zeroed()), |ok| ParamValue::Owned(ok))
|
||||
} else {
|
||||
ParamValue::Borrowed(transmute_copy(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Param<T, CloneType> for &T
|
||||
where
|
||||
T: TypeKind<TypeKind = CloneType> + Clone,
|
||||
{
|
||||
unsafe fn param(self) -> ParamValue<T> {
|
||||
unsafe { ParamValue::Borrowed(transmute_copy(self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Param<T, CopyType> for U
|
||||
where
|
||||
T: TypeKind<TypeKind = CopyType> + Clone,
|
||||
U: TypeKind<TypeKind = CopyType> + Clone,
|
||||
U: imp::CanInto<T>,
|
||||
{
|
||||
unsafe fn param(self) -> ParamValue<T> {
|
||||
unsafe { ParamValue::Owned(transmute_copy(&self)) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use super::*;
|
||||
use core::mem::transmute_copy;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub enum ParamValue<T: Type<T>> {
|
||||
Owned(T),
|
||||
Borrowed(T::Abi),
|
||||
}
|
||||
|
||||
impl<T: Type<T>> ParamValue<T> {
|
||||
// TODO: replace with `borrow` in windows-bindgen
|
||||
pub fn abi(&self) -> T::Abi {
|
||||
unsafe {
|
||||
match self {
|
||||
Self::Owned(item) => transmute_copy(item),
|
||||
Self::Borrowed(borrowed) => transmute_copy(borrowed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> Ref<'_, T> {
|
||||
unsafe { transmute_copy(&self.abi()) }
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
use super::*;
|
||||
use core::mem::transmute;
|
||||
|
||||
/// A borrowed type with the same memory layout as the type itself that can be used to construct ABI-compatible function signatures.
|
||||
#[repr(transparent)]
|
||||
pub struct Ref<'a, T: Type<T>>(T::Abi, core::marker::PhantomData<&'a T>);
|
||||
|
||||
impl<T: Type<T>> Ref<'_, T> {
|
||||
/// Returns `true` if the argument is null.
|
||||
pub fn is_null(&self) -> bool {
|
||||
T::is_null(&self.0)
|
||||
}
|
||||
|
||||
/// Converts the argument to a [`Result<&T>`] reference.
|
||||
pub fn ok(&self) -> Result<&T> {
|
||||
self.as_ref()
|
||||
.ok_or_else(|| Error::from_hresult(imp::E_POINTER))
|
||||
}
|
||||
|
||||
/// Converts the argument to a [`Option<&T>`] reference.
|
||||
pub fn as_ref(&self) -> Option<&T> {
|
||||
if self.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe { Some(self.assume_init_ref()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the argument to a `&T` reference.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the argument is null.
|
||||
#[track_caller]
|
||||
pub fn unwrap(&self) -> &T {
|
||||
self.as_ref().expect("called `Ref::unwrap` on a null value")
|
||||
}
|
||||
|
||||
/// Converts the argument to an [`Option<T>`] by cloning the reference.
|
||||
pub fn cloned(&self) -> Option<T> {
|
||||
self.as_ref().cloned()
|
||||
}
|
||||
|
||||
unsafe fn assume_init_ref(&self) -> &T {
|
||||
unsafe { T::assume_init_ref(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> Default for Ref<'_, T> {
|
||||
fn default() -> Self {
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type<T>> core::ops::Deref for Ref<'_, T> {
|
||||
type Target = T::Default;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { transmute(&self.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type<T, InterfaceType>> From<&'a Option<T>> for Ref<'a, T>
|
||||
where
|
||||
T: TypeKind<TypeKind = InterfaceType>,
|
||||
{
|
||||
fn from(from: &'a Option<T>) -> Self {
|
||||
unsafe { core::mem::transmute_copy(from) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type<T, InterfaceType>> From<Option<&'a T>> for Ref<'a, T>
|
||||
where
|
||||
T: TypeKind<TypeKind = InterfaceType>,
|
||||
{
|
||||
fn from(from: Option<&'a T>) -> Self {
|
||||
if let Some(from) = from {
|
||||
unsafe { core::mem::transmute_copy(from) }
|
||||
} else {
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Type<T>> From<&'a T> for Ref<'a, T> {
|
||||
fn from(from: &'a T) -> Self {
|
||||
unsafe { core::mem::transmute_copy(from) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#[doc(hidden)]
|
||||
pub trait RuntimeName {
|
||||
// TODO: needs to use ConstBuffer like RuntimeType to allow generic interfaces to have names for GetRuntimeClassName
|
||||
const NAME: &'static str = "";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use super::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait RuntimeType: Type<Self> {
|
||||
const SIGNATURE: imp::ConstBuffer;
|
||||
}
|
||||
|
||||
macro_rules! primitives {
|
||||
($(($t:ty, $s:literal)),+) => {
|
||||
$(
|
||||
impl RuntimeType for $t {
|
||||
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice($s);
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
primitives! {
|
||||
(bool, b"b1"),
|
||||
(i8, b"i1"),
|
||||
(u8, b"u1"),
|
||||
(i16, b"i2"),
|
||||
(u16, b"u2"),
|
||||
(i32, b"i4"),
|
||||
(u32, b"u4"),
|
||||
(i64, b"i8"),
|
||||
(u64, b"u8"),
|
||||
(f32, b"f4"),
|
||||
(f64, b"f8")
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use super::*;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[repr(C)]
|
||||
pub struct ScopedHeap {
|
||||
pub vtable: *const c_void,
|
||||
pub this: *const c_void,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ScopedInterface<'a, T: Interface> {
|
||||
interface: T,
|
||||
lifetime: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<T: Interface> ScopedInterface<'_, T> {
|
||||
pub fn new(interface: T) -> Self {
|
||||
Self {
|
||||
interface,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Interface> core::ops::Deref for ScopedInterface<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.interface
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Interface> Drop for ScopedInterface<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(self.interface.as_raw() as *const _ as *mut ScopedHeap);
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
use super::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait TypeKind {
|
||||
type TypeKind;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct InterfaceType;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct CloneType;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct CopyType;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Type<T: TypeKind, C = <T as TypeKind>::TypeKind>: TypeKind + Sized + Clone {
|
||||
type Abi;
|
||||
type Default;
|
||||
|
||||
fn is_null(abi: &Self::Abi) -> bool;
|
||||
unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self;
|
||||
unsafe fn from_abi(abi: Self::Abi) -> Result<Self>;
|
||||
fn from_default(default: &Self::Default) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl<T> Type<T, InterfaceType> for T
|
||||
where
|
||||
T: TypeKind<TypeKind = InterfaceType> + Clone,
|
||||
{
|
||||
type Abi = *mut core::ffi::c_void;
|
||||
type Default = Option<Self>;
|
||||
|
||||
fn is_null(abi: &Self::Abi) -> bool {
|
||||
abi.is_null()
|
||||
}
|
||||
|
||||
unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
|
||||
unsafe { core::mem::transmute::<&*mut core::ffi::c_void, &T>(abi) }
|
||||
}
|
||||
|
||||
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
|
||||
unsafe {
|
||||
if !abi.is_null() {
|
||||
Ok(core::mem::transmute_copy(&abi))
|
||||
} else {
|
||||
Err(Error::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_default(default: &Self::Default) -> Result<Self> {
|
||||
default.as_ref().cloned().ok_or(Error::empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Type<T, CloneType> for T
|
||||
where
|
||||
T: TypeKind<TypeKind = CloneType> + Clone,
|
||||
{
|
||||
type Abi = core::mem::MaybeUninit<Self>;
|
||||
type Default = Self;
|
||||
|
||||
fn is_null(_: &Self::Abi) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
|
||||
unsafe { abi.assume_init_ref() }
|
||||
}
|
||||
|
||||
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
|
||||
unsafe { Ok(abi.assume_init()) }
|
||||
}
|
||||
|
||||
fn from_default(default: &Self::Default) -> Result<Self> {
|
||||
Ok(default.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Type<T, CopyType> for T
|
||||
where
|
||||
T: TypeKind<TypeKind = CopyType> + Clone,
|
||||
{
|
||||
type Abi = Self;
|
||||
type Default = Self;
|
||||
|
||||
fn is_null(_: &Self::Abi) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self {
|
||||
abi
|
||||
}
|
||||
|
||||
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
|
||||
Ok(abi)
|
||||
}
|
||||
|
||||
fn from_default(default: &Self) -> Result<Self> {
|
||||
Ok(default.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Interface> TypeKind for T {
|
||||
type TypeKind = InterfaceType;
|
||||
}
|
||||
|
||||
impl<T> TypeKind for *mut T {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
macro_rules! primitives {
|
||||
($($t:ty),+) => {
|
||||
$(
|
||||
impl TypeKind for $t {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
primitives!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize);
|
||||
|
||||
#[doc(hidden)]
|
||||
pub type AbiType<T> = <T as Type<T>>::Abi;
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
use super::*;
|
||||
use core::ffi::c_void;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
/// Base interface for all COM interfaces.
|
||||
///
|
||||
/// All COM interfaces (and thus WinRT classes and interfaces) implement
|
||||
/// [IUnknown](https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown)
|
||||
/// under the hood to provide reference-counted lifetime management as well as the ability
|
||||
/// to query for additional interfaces that the object may implement.
|
||||
#[repr(transparent)]
|
||||
pub struct IUnknown(NonNull<c_void>);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[repr(C)]
|
||||
pub struct IUnknown_Vtbl {
|
||||
pub QueryInterface: unsafe extern "system" fn(
|
||||
this: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT,
|
||||
pub AddRef: unsafe extern "system" fn(this: *mut c_void) -> u32,
|
||||
pub Release: unsafe extern "system" fn(this: *mut c_void) -> u32,
|
||||
}
|
||||
|
||||
unsafe impl Interface for IUnknown {
|
||||
type Vtable = IUnknown_Vtbl;
|
||||
const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046);
|
||||
}
|
||||
|
||||
impl Clone for IUnknown {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
(self.vtable().AddRef)(core::mem::transmute_copy(self));
|
||||
}
|
||||
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IUnknown {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.vtable().Release)(core::mem::transmute_copy(self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for IUnknown {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// First we test for ordinary pointer equality. If two COM interface pointers have the
|
||||
// same pointer value, then they are the same object. This can save us a lot of time,
|
||||
// since calling QueryInterface is much more expensive than a single pointer comparison.
|
||||
//
|
||||
// However, interface pointers may have different values and yet point to the same object.
|
||||
// Since COM objects may implement multiple interfaces, COM identity can only
|
||||
// be determined by querying for `IUnknown` explicitly and then comparing the
|
||||
// pointer values. This works since `QueryInterface` is required to return
|
||||
// the same pointer value for queries for `IUnknown`.
|
||||
core::ptr::eq(self.as_raw(), other.as_raw())
|
||||
|| self.cast::<Self>().unwrap().0 == other.cast::<Self>().unwrap().0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for IUnknown {}
|
||||
|
||||
impl core::fmt::Debug for IUnknown {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_tuple("IUnknown").field(&self.as_raw()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The `#[implement]` macro generates implementations of this trait for the types
|
||||
/// that it generates, e.g. `MyApp_Impl`,
|
||||
///
|
||||
/// `ComObject` uses this trait to interact with boxed COM objects.
|
||||
#[doc(hidden)]
|
||||
pub trait IUnknownImpl {
|
||||
/// The contained user type, e.g. `MyApp`. Also known as the "inner" type.
|
||||
type Impl;
|
||||
|
||||
/// Get a reference to the backing implementation.
|
||||
fn get_impl(&self) -> &Self::Impl;
|
||||
|
||||
/// Get a mutable reference to the contained (inner) object.
|
||||
fn get_impl_mut(&mut self) -> &mut Self::Impl;
|
||||
|
||||
/// Consumes the box and returns the contained (inner) object. This is the opposite of `new_box`.
|
||||
fn into_inner(self) -> Self::Impl;
|
||||
|
||||
/// The classic `QueryInterface` method from COM.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is safe to call as long as the interface pointer is non-null and valid for writes
|
||||
/// of an interface pointer.
|
||||
unsafe fn QueryInterface(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT;
|
||||
|
||||
/// Increments the reference count of the interface
|
||||
fn AddRef(&self) -> u32;
|
||||
|
||||
/// Decrements the reference count causing the interface's memory to be freed when the count is 0
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should only be called when the interface pointer is no longer used as calling `Release`
|
||||
/// on a non-aliased interface pointer and then using that interface pointer may result in use after free.
|
||||
///
|
||||
/// This function takes `*mut Self` because the object may be freed by the time this method returns.
|
||||
/// Taking `&self` would violate Rust's rules on reference lifetime.
|
||||
unsafe fn Release(self_: *mut Self) -> u32;
|
||||
|
||||
/// Returns `true` if the reference count of the box is equal to 1.
|
||||
fn is_reference_count_one(&self) -> bool;
|
||||
|
||||
/// Gets the trust level of the current object.
|
||||
unsafe fn GetTrustLevel(&self, value: *mut i32) -> HRESULT;
|
||||
|
||||
/// Gets a borrowed reference to an interface that is implemented by this ComObject.
|
||||
///
|
||||
/// The returned reference does not have an additional reference count.
|
||||
/// You can AddRef it by calling to_owned().
|
||||
#[inline(always)]
|
||||
fn as_interface<I: Interface>(&self) -> InterfaceRef<'_, I>
|
||||
where
|
||||
Self: ComObjectInterface<I>,
|
||||
{
|
||||
<Self as ComObjectInterface<I>>::as_interface_ref(self)
|
||||
}
|
||||
|
||||
/// Gets an owned (counted) reference to an interface that is implemented by this ComObject.
|
||||
#[inline(always)]
|
||||
fn to_interface<I: Interface>(&self) -> I
|
||||
where
|
||||
Self: ComObjectInterface<I>,
|
||||
{
|
||||
<Self as ComObjectInterface<I>>::as_interface_ref(self).to_owned()
|
||||
}
|
||||
|
||||
/// Creates a new owned reference to this object.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function can only be safely called by `<Foo>_Impl` objects that are embedded in a
|
||||
/// `ComObject`. Since we only allow safe Rust code to access these objects using a `ComObject`
|
||||
/// or a `&<Foo>_Impl` that points within a `ComObject`, this is safe.
|
||||
fn to_object(&self) -> ComObject<Self::Impl>
|
||||
where
|
||||
Self::Impl: ComObjectInner<Outer = Self>;
|
||||
}
|
||||
|
||||
impl IUnknown_Vtbl {
|
||||
pub const fn new<T: IUnknownImpl, const OFFSET: isize>() -> Self {
|
||||
unsafe extern "system" fn QueryInterface<T: IUnknownImpl, const OFFSET: isize>(
|
||||
this: *mut c_void,
|
||||
iid: *const GUID,
|
||||
interface: *mut *mut c_void,
|
||||
) -> HRESULT {
|
||||
unsafe {
|
||||
let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T;
|
||||
(*this).QueryInterface(iid, interface)
|
||||
}
|
||||
}
|
||||
unsafe extern "system" fn AddRef<T: IUnknownImpl, const OFFSET: isize>(
|
||||
this: *mut c_void,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T;
|
||||
(*this).AddRef()
|
||||
}
|
||||
}
|
||||
unsafe extern "system" fn Release<T: IUnknownImpl, const OFFSET: isize>(
|
||||
this: *mut c_void,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T;
|
||||
T::Release(this)
|
||||
}
|
||||
}
|
||||
Self {
|
||||
QueryInterface: QueryInterface::<T, OFFSET>,
|
||||
AddRef: AddRef::<T, OFFSET>,
|
||||
Release: Release::<T, OFFSET>,
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
use super::*;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// `Weak` holds a non-owning reference to an object.
|
||||
#[derive(Clone, PartialEq, Eq, Default)]
|
||||
pub struct Weak<I: Interface>(Option<imp::IWeakReference>, PhantomData<I>);
|
||||
|
||||
impl<I: Interface> Weak<I> {
|
||||
/// Creates a new `Weak` object without any backing object.
|
||||
pub const fn new() -> Self {
|
||||
Self(None, PhantomData)
|
||||
}
|
||||
|
||||
/// Attempts to upgrade the weak reference to a strong reference.
|
||||
pub fn upgrade(&self) -> Option<I> {
|
||||
self.0
|
||||
.as_ref()
|
||||
.and_then(|inner| unsafe { inner.Resolve().ok() })
|
||||
}
|
||||
|
||||
pub(crate) fn downgrade(source: &imp::IWeakReferenceSource) -> Self {
|
||||
let reference = unsafe { source.GetWeakReference().ok() };
|
||||
Self(reference, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<I: Interface> Send for Weak<I> {}
|
||||
unsafe impl<I: Interface> Sync for Weak<I> {}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
mod agile_reference;
|
||||
pub use agile_reference::*;
|
||||
|
||||
mod array;
|
||||
pub use array::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod event;
|
||||
#[cfg(feature = "std")]
|
||||
pub use event::*;
|
||||
|
||||
mod handles;
|
||||
pub use handles::*;
|
||||
|
||||
pub use windows_strings::*;
|
||||
|
||||
/// 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: RuntimeName, I: Interface>() -> Result<I> {
|
||||
imp::load_factory::<C, I>()
|
||||
}
|
||||
|
||||
impl Param<PCWSTR> for &BSTR {
|
||||
unsafe fn param(self) -> ParamValue<PCWSTR> {
|
||||
ParamValue::Owned(PCWSTR(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Param<PCWSTR> for &HSTRING {
|
||||
unsafe fn param(self) -> ParamValue<PCWSTR> {
|
||||
ParamValue::Owned(PCWSTR(self.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Param<PCWSTR> for PWSTR {
|
||||
unsafe fn param(self) -> ParamValue<PCWSTR> {
|
||||
ParamValue::Owned(PCWSTR(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Param<PCSTR> for PSTR {
|
||||
unsafe fn param(self) -> ParamValue<PCSTR> {
|
||||
ParamValue::Owned(PCSTR(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeType for HSTRING {
|
||||
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"string");
|
||||
}
|
||||
|
||||
impl TypeKind for PWSTR {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
impl TypeKind for PSTR {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
impl TypeKind for PCWSTR {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
impl TypeKind for PCSTR {
|
||||
type TypeKind = CopyType;
|
||||
}
|
||||
|
||||
impl TypeKind for HSTRING {
|
||||
type TypeKind = CloneType;
|
||||
}
|
||||
|
||||
impl TypeKind for BSTR {
|
||||
type TypeKind = CloneType;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="windows_core::array::Array<*>">
|
||||
<DisplayString>{{ len={len} }}</DisplayString>
|
||||
|
||||
<Expand>
|
||||
<Item Name="[len]">len</Item>
|
||||
<ArrayItems>
|
||||
<Size>len</Size>
|
||||
<ValuePointer>data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="windows_core::imp::ref_count::RefCount">
|
||||
<DisplayString>{__0}</DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
Reference in new issue
Block a user