Bug 1910150: Update windows-rs to 0.58. r=glandium,supply-chain-reviewers

Update the `windows` Microsoft Windows binding crate to 0.58.

- Update `taskcluster/kinds/fetch/toolchains.yml` to request version
  0.58 of the `windows` crate.
- Update `build/rust/windows/Cargo.toml` to present itself as 0.58.
- Vendor the following new crates into `third_party/rust`:
    - windows-core
    - windows-implement
    - windows-interface
    - windows-result
    - windows-strings
- Update `supply-chain/imports.lock` as necessary.

Differential Revision: https://phabricator.services.mozilla.com/D218694
This commit is contained in:
Jim Blandy
2024-08-07 16:00:47 +00:00
parent e1b04239fc
commit 4d7ca2a448
88 changed files with 7972 additions and 3196 deletions

51
Cargo.lock generated
View File

@@ -6903,7 +6903,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.52.0"
version = "0.58.0"
dependencies = [
"mozbuild",
"windows-core",
@@ -6912,10 +6912,55 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]

View File

@@ -11,10 +11,16 @@
[package]
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
name = "windows"
version = "0.52.0"
version = "0.58.0"
authors = ["Microsoft"]
build = false
exclude = ["features.json"]
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust for Windows"
documentation = "https://microsoft.github.io/windows-docs-rs/"
readme = "readme.md"
@@ -30,24 +36,22 @@ rustdoc-args = [
]
targets = []
[lib]
name = "windows"
path = "src/lib.rs"
test = false
doctest = false
[dependencies.windows-core]
version = "0.52.0"
[dependencies.windows-implement]
version = "0.52.0"
optional = true
[dependencies.windows-interface]
version = "0.52.0"
optional = true
version = "0.58.0"
[dependencies.windows-targets]
version = "0.52.0"
version = "0.52.6"
[features]
AI = []
AI = ["Foundation"]
AI_MachineLearning = ["AI"]
ApplicationModel = []
ApplicationModel = ["Foundation"]
ApplicationModel_Activation = ["ApplicationModel"]
ApplicationModel_AppExtensions = ["ApplicationModel"]
ApplicationModel_AppService = ["ApplicationModel"]
@@ -75,6 +79,7 @@ ApplicationModel_ExtendedExecution = ["ApplicationModel"]
ApplicationModel_ExtendedExecution_Foreground = ["ApplicationModel_ExtendedExecution"]
ApplicationModel_Holographic = ["ApplicationModel"]
ApplicationModel_LockScreen = ["ApplicationModel"]
ApplicationModel_PackageExtensions = ["ApplicationModel"]
ApplicationModel_Payments = ["ApplicationModel"]
ApplicationModel_Payments_Provider = ["ApplicationModel_Payments"]
ApplicationModel_Preview = ["ApplicationModel"]
@@ -86,10 +91,6 @@ ApplicationModel_Resources_Core = ["ApplicationModel_Resources"]
ApplicationModel_Resources_Management = ["ApplicationModel_Resources"]
ApplicationModel_Search = ["ApplicationModel"]
ApplicationModel_Search_Core = ["ApplicationModel_Search"]
ApplicationModel_Store = ["ApplicationModel"]
ApplicationModel_Store_LicenseManagement = ["ApplicationModel_Store"]
ApplicationModel_Store_Preview = ["ApplicationModel_Store"]
ApplicationModel_Store_Preview_InstallControl = ["ApplicationModel_Store_Preview"]
ApplicationModel_UserActivities = ["ApplicationModel"]
ApplicationModel_UserActivities_Core = ["ApplicationModel_UserActivities"]
ApplicationModel_UserDataAccounts = ["ApplicationModel"]
@@ -100,7 +101,7 @@ ApplicationModel_UserDataTasks_DataProvider = ["ApplicationModel_UserDataTasks"]
ApplicationModel_VoiceCommands = ["ApplicationModel"]
ApplicationModel_Wallet = ["ApplicationModel"]
ApplicationModel_Wallet_System = ["ApplicationModel_Wallet"]
Data = []
Data = ["Foundation"]
Data_Html = ["Data"]
Data_Json = ["Data"]
Data_Pdf = ["Data"]
@@ -108,7 +109,7 @@ Data_Text = ["Data"]
Data_Xml = ["Data"]
Data_Xml_Dom = ["Data_Xml"]
Data_Xml_Xsl = ["Data_Xml"]
Devices = []
Devices = ["Foundation"]
Devices_Adc = ["Devices"]
Devices_Adc_Provider = ["Devices_Adc"]
Devices_Background = ["Devices"]
@@ -157,14 +158,14 @@ Devices_Usb = ["Devices"]
Devices_WiFi = ["Devices"]
Devices_WiFiDirect = ["Devices"]
Devices_WiFiDirect_Services = ["Devices_WiFiDirect"]
Embedded = []
Embedded = ["Foundation"]
Embedded_DeviceLockdown = ["Embedded"]
Foundation = []
Foundation_Collections = ["Foundation"]
Foundation_Diagnostics = ["Foundation"]
Foundation_Metadata = ["Foundation"]
Foundation_Numerics = ["Foundation"]
Gaming = []
Gaming = ["Foundation"]
Gaming_Input = ["Gaming"]
Gaming_Input_Custom = ["Gaming_Input"]
Gaming_Input_ForceFeedback = ["Gaming_Input"]
@@ -174,13 +175,13 @@ Gaming_Preview_GamesEnumeration = ["Gaming_Preview"]
Gaming_UI = ["Gaming"]
Gaming_XboxLive = ["Gaming"]
Gaming_XboxLive_Storage = ["Gaming_XboxLive"]
Globalization = []
Globalization = ["Foundation"]
Globalization_Collation = ["Globalization"]
Globalization_DateTimeFormatting = ["Globalization"]
Globalization_Fonts = ["Globalization"]
Globalization_NumberFormatting = ["Globalization"]
Globalization_PhoneNumberFormatting = ["Globalization"]
Graphics = []
Graphics = ["Foundation"]
Graphics_Capture = ["Graphics"]
Graphics_DirectX = ["Graphics"]
Graphics_DirectX_Direct3D11 = ["Graphics_DirectX"]
@@ -195,14 +196,15 @@ Graphics_Printing_OptionDetails = ["Graphics_Printing"]
Graphics_Printing_PrintSupport = ["Graphics_Printing"]
Graphics_Printing_PrintTicket = ["Graphics_Printing"]
Graphics_Printing_Workflow = ["Graphics_Printing"]
Management = []
Management = ["Foundation"]
Management_Core = ["Management"]
Management_Deployment = ["Management"]
Management_Deployment_Preview = ["Management_Deployment"]
Management_Policies = ["Management"]
Management_Setup = ["Management"]
Management_Update = ["Management"]
Management_Workplace = ["Management"]
Media = []
Media = ["Foundation"]
Media_AppBroadcasting = ["Media"]
Media_AppRecording = ["Media"]
Media_Audio = ["Media"]
@@ -236,7 +238,7 @@ Media_SpeechSynthesis = ["Media"]
Media_Streaming = ["Media"]
Media_Streaming_Adaptive = ["Media_Streaming"]
Media_Transcoding = ["Media"]
Networking = []
Networking = ["Foundation"]
Networking_BackgroundTransfer = ["Networking"]
Networking_Connectivity = ["Networking"]
Networking_NetworkOperators = ["Networking"]
@@ -247,14 +249,14 @@ Networking_ServiceDiscovery_Dnssd = ["Networking_ServiceDiscovery"]
Networking_Sockets = ["Networking"]
Networking_Vpn = ["Networking"]
Networking_XboxLive = ["Networking"]
Perception = []
Perception = ["Foundation"]
Perception_Automation = ["Perception"]
Perception_Automation_Core = ["Perception_Automation"]
Perception_People = ["Perception"]
Perception_Spatial = ["Perception"]
Perception_Spatial_Preview = ["Perception_Spatial"]
Perception_Spatial_Surfaces = ["Perception_Spatial"]
Phone = []
Phone = ["Foundation"]
Phone_ApplicationModel = ["Phone"]
Phone_Devices = ["Phone"]
Phone_Devices_Notification = ["Phone_Devices"]
@@ -278,7 +280,7 @@ Phone_System_UserProfile_GameServices = ["Phone_System_UserProfile"]
Phone_System_UserProfile_GameServices_Core = ["Phone_System_UserProfile_GameServices"]
Phone_UI = ["Phone"]
Phone_UI_Input = ["Phone_UI"]
Security = []
Security = ["Foundation"]
Security_Authentication = ["Security"]
Security_Authentication_Identity = ["Security_Authentication"]
Security_Authentication_Identity_Core = ["Security_Authentication_Identity"]
@@ -298,14 +300,14 @@ Security_DataProtection = ["Security"]
Security_EnterpriseData = ["Security"]
Security_ExchangeActiveSyncProvisioning = ["Security"]
Security_Isolation = ["Security"]
Services = []
Services = ["Foundation"]
Services_Maps = ["Services"]
Services_Maps_Guidance = ["Services_Maps"]
Services_Maps_LocalSearch = ["Services_Maps"]
Services_Maps_OfflineMaps = ["Services_Maps"]
Services_Store = ["Services"]
Services_TargetedContent = ["Services"]
Storage = []
Storage = ["Foundation"]
Storage_AccessCache = ["Storage"]
Storage_BulkAccess = ["Storage"]
Storage_Compression = ["Storage"]
@@ -315,7 +317,7 @@ Storage_Pickers_Provider = ["Storage_Pickers"]
Storage_Provider = ["Storage"]
Storage_Search = ["Storage"]
Storage_Streams = ["Storage"]
System = []
System = ["Foundation"]
System_Diagnostics = ["System"]
System_Diagnostics_DevicePortal = ["System_Diagnostics"]
System_Diagnostics_Telemetry = ["System_Diagnostics"]
@@ -329,12 +331,13 @@ System_Profile = ["System"]
System_Profile_SystemManufacturers = ["System_Profile"]
System_RemoteDesktop = ["System"]
System_RemoteDesktop_Input = ["System_RemoteDesktop"]
System_RemoteDesktop_Provider = ["System_RemoteDesktop"]
System_RemoteSystems = ["System"]
System_Threading = ["System"]
System_Threading_Core = ["System_Threading"]
System_Update = ["System"]
System_UserProfile = ["System"]
UI = []
UI = ["Foundation"]
UI_Accessibility = ["UI"]
UI_ApplicationSettings = ["UI"]
UI_Composition = ["UI"]
@@ -358,6 +361,7 @@ UI_Input_Preview_Injection = ["UI_Input_Preview"]
UI_Input_Spatial = ["UI_Input"]
UI_Notifications = ["UI"]
UI_Notifications_Management = ["UI_Notifications"]
UI_Notifications_Preview = ["UI_Notifications"]
UI_Popups = ["UI"]
UI_Shell = ["UI"]
UI_StartScreen = ["UI"]
@@ -371,21 +375,28 @@ UI_WebUI = ["UI"]
UI_WebUI_Core = ["UI_WebUI"]
UI_WindowManagement = ["UI"]
UI_WindowManagement_Preview = ["UI_WindowManagement"]
Wdk = []
Wdk = ["Win32_Foundation"]
Wdk_Devices = ["Wdk"]
Wdk_Devices_Bluetooth = ["Wdk_Devices"]
Wdk_Devices_HumanInterfaceDevice = ["Wdk_Devices"]
Wdk_Foundation = ["Wdk"]
Wdk_Graphics = ["Wdk"]
Wdk_Graphics_Direct3D = ["Wdk_Graphics"]
Wdk_NetworkManagement = ["Wdk"]
Wdk_NetworkManagement_Ndis = ["Wdk_NetworkManagement"]
Wdk_NetworkManagement_WindowsFilteringPlatform = ["Wdk_NetworkManagement"]
Wdk_Storage = ["Wdk"]
Wdk_Storage_FileSystem = ["Wdk_Storage"]
Wdk_Storage_FileSystem_Minifilters = ["Wdk_Storage_FileSystem"]
Wdk_System = ["Wdk"]
Wdk_System_IO = ["Wdk_System"]
Wdk_System_Memory = ["Wdk_System"]
Wdk_System_OfflineRegistry = ["Wdk_System"]
Wdk_System_Registry = ["Wdk_System"]
Wdk_System_SystemInformation = ["Wdk_System"]
Wdk_System_SystemServices = ["Wdk_System"]
Wdk_System_Threading = ["Wdk_System"]
Web = []
Web = ["Foundation"]
Web_AtomPub = ["Web"]
Web_Http = ["Web"]
Web_Http_Diagnostics = ["Web_Http"]
@@ -394,7 +405,7 @@ Web_Http_Headers = ["Web_Http"]
Web_Syndication = ["Web"]
Web_UI = ["Web"]
Web_UI_Interop = ["Web_UI"]
Win32 = []
Win32 = ["Win32_Foundation"]
Win32_AI = ["Win32"]
Win32_AI_MachineLearning = ["Win32_AI"]
Win32_AI_MachineLearning_DirectML = ["Win32_AI_MachineLearning"]
@@ -606,6 +617,7 @@ Win32_System_Diagnostics_Debug_Extensions = ["Win32_System_Diagnostics_Debug"]
Win32_System_Diagnostics_Etw = ["Win32_System_Diagnostics"]
Win32_System_Diagnostics_ProcessSnapshotting = ["Win32_System_Diagnostics"]
Win32_System_Diagnostics_ToolHelp = ["Win32_System_Diagnostics"]
Win32_System_Diagnostics_TraceLogging = ["Win32_System_Diagnostics"]
Win32_System_DistributedTransactionCoordinator = ["Win32_System"]
Win32_System_Environment = ["Win32_System"]
Win32_System_ErrorReporting = ["Win32_System"]
@@ -723,14 +735,23 @@ Win32_UI_WindowsAndMessaging = ["Win32_UI"]
Win32_UI_Wpf = ["Win32_UI"]
Win32_Web = ["Win32"]
Win32_Web_InternetExplorer = ["Win32_Web"]
default = []
default = ["std"]
deprecated = []
docs = []
implement = [
"windows-implement",
"windows-interface",
"windows-core/implement",
]
implement = []
std = ["windows-core/std"]
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]
[dependencies.mozbuild]
version = "0.1"

View File

@@ -809,6 +809,34 @@ user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-implement]]
version = "0.58.0"
when = "2024-07-03"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-interface]]
version = "0.58.0"
when = "2024-07-03"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-result]]
version = "0.2.0"
when = "2024-07-03"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-strings]]
version = "0.1.0"
when = "2024-07-03"
user-id = 64539
user-login = "kennykerr"
user-name = "Kenny Kerr"
[[publisher.windows-sys]]
version = "0.52.0"
when = "2023-11-15"

View File

@@ -652,9 +652,9 @@ windows-rs:
description: Source of the windows rust crate
fetch:
type: static-url
url: https://crates.io/api/v1/crates/windows/0.52.0/download
sha256: e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be
size: 11843715
url: https://crates.io/api/v1/crates/windows/0.58.0/download
sha256: dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6
size: 9744521
artifact-name: windows-rs.tar.zst
strip-components: 1
add-prefix: windows-rs/

View File

@@ -1 +1 @@
{"files":{"Cargo.toml":"483877d1a465dc6a4c190fdc206eb83293e6102d548dcf0f2e2093fa075694f5","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"3bcfa95b17c7748ff6139e7482ebad2d24645ac3606e4b4f88318c72122d1386","src/agile_reference.rs":"801b3bbe3674e2665bdcb12a08f06f7782be60a14c3a033289f32aa35db1fce1","src/array.rs":"c9975db941876ce329ed851b5e6ad989a660f9da0f91e8dece7333fda2274e01","src/as_impl.rs":"018954b44fe4c16d62a959f5578c5a26b968d0f9a3f156f1bac10492b934d949","src/com_interface.rs":"afc47510e80e829b5ebb2a801840104725dfcea3c5920a424b089948a14f8d15","src/error.rs":"c9115facaaa7369e91e3d959e9278770d727e52bb0108df099ae5c90feb52fdf","src/event.rs":"cc3502add9db70efb2627ddc5c709853a56ccbfee37923d67b56ed338b9d8f44","src/guid.rs":"dd82af578fef2519173614600a15ada073d5e7c973fd2e951eb5abef9b1553d0","src/hresult.rs":"00155798c154614c4c9a0a2da20814dddb537a1e21c88bc3dff65598fdf7ee03","src/imp/bindings.rs":"805fe0012f5bbf639f6d66cfdbe21f4f855c4ec3eee2610f457dbf3b80defad7","src/imp/com_bindings.rs":"3f88e91dd17daf657c9e29e82628fc84fa02b942bca1ae5ca682e4a2a89ccb6e","src/imp/delay_load.rs":"26492b2ef34f4a3ac35f41050a9088ee15603caf0a59c6e74fdf423bfcb96829","src/imp/factory_cache.rs":"fcec72750d06a262878720a13f82b7365075c6b038b28426532b233aae8baa84","src/imp/generic_factory.rs":"cbf8b251e2758d98bf6bbd7c73b20a2e31e60e0d006b2febc51e247c039fab85","src/imp/heap.rs":"646e31462f37f96a9d6928997c2bbacc333eabb3989e9999e89ff95e9565e4ba","src/imp/mod.rs":"503ab66c7911e5ad6dfd2311f81674798c84e41d3c023b4868f80ad3a3593b8f","src/imp/ref_count.rs":"ded51b50923eb1d542432d216b56604b6cef564f664740a3ad8290ad8d88b8e6","src/imp/sha1.rs":"0ff2ba4d1f77b5ec3859567fec1a3d75175a844914c5f7ec068c8581dc2b7f4a","src/imp/waiter.rs":"575cfdbe9befc22b1e8fa91e15f303d830b23673d07a5361992229e0440081b2","src/imp/weak_ref_count.rs":"5d58f4bdcb4b6fbb55be672b7bd885307c9cac17ee4f212c899a54e76a039ded","src/inspectable.rs":"32c4881d567d90254556a4185cd0dd5b984c932183db7c8b4c51bcbac53e41ac","src/interface.rs":"4a4588085d5401585aabc62a131d2601134f4fb80ba2199fe066e6df6c84ead3","src/lib.rs":"9673f8d82af0325a5924b9afe07af39e7ea82731c3caca126e39cd9596dd4e63","src/param.rs":"cb62f173048e84f6355662223d34be482449393fc049fc45980d1bf087dc8e77","src/runtime_name.rs":"b330db95b0ce32c963d6b43a0b152cd96e136e5b38b92c02223fb53b30f83ecf","src/runtime_type.rs":"cf3697b442b2bc77e1a8981128eb5f4c282603a01f7081399f1e11587670d0bc","src/scoped_interface.rs":"c11be14a0c95c3c9b055741e4b9c353d6bb6efe88075ceb05fd606b6ac34a2a6","src/strings/bstr.rs":"569ff0cf6157cb869ec14f773261fe95ee1d065cade7b04d3ee44f0dfbdec149","src/strings/hstring.rs":"ea3913393e655b6c48270df04fbb82eb9fc4b1d3f1d673bd04872a33d160f38d","src/strings/literals.rs":"5b998ebd3a750a7f42c2013b354a58f6c289f5f433f154a9ac34b47e23ec9429","src/strings/mod.rs":"4aaf6d2efe2396e541bd5d9ddff859d3374bfda4914b67daf645ee3b48cc3852","src/strings/pcstr.rs":"3c29064d22e632dc9f30c8d6933e41d5c755d2c82462c6570318bebe12a5c1a3","src/strings/pcwstr.rs":"86a36da73bdcd12af9a6e1be09470443b42017a0613c432ac3e559cfdc7f3ff2","src/strings/pstr.rs":"151d29125932d874aefcda01975913f87e8561b3d240e84c9db39e891b78a484","src/strings/pwstr.rs":"de99eb08f1f3c351854d1ed1f978460669b53ae39dc118e788a9f66a562d2c7c","src/type.rs":"fc263c265f799c7d5b326cebbbc20a8f96ee438b1cc444d9925caab22db8e488","src/unknown.rs":"5a1888c35ad9db383216bbe1c15dde3ea316d40ba2783392067060706f7ec812","src/weak.rs":"b1d1740d65ad0b9f296342afbac0c6994eb2fa0ea7dba4882b64b246e309d9da","windows.natvis":"f9bd327a207011f85debdd3d67828668f71b56e7e429a7172dab1286738ca238"},"package":"33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"}
{"files":{"Cargo.toml":"6a0eed9b6df5e3fbdd4fb636fdf0778f0182dfe81fee1427de89088c31dfb299","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"a3280bab4a34e02f616e9c55459182ed93df0cf06ae6d69743140746366679d5","src/agile_reference.rs":"aeca8437136c09a51ef45cface5ae8e6720f67d23681faa12a84b14a79cfeed2","src/array.rs":"50afdb247feb5e0b575a9cfeda2359719fe0db7bd4b12c687d699f3821d15eee","src/as_impl.rs":"b9d36d6634de33928cc78b8e0824483c5b6561ca3872d5a9b267fb196db3d07e","src/com_object.rs":"c02e31da9d2d8efd7074bf3e44afe3279ca727c6ec3b840ae4d71a8c98bc55e6","src/event.rs":"d0e528b73744e31a3c46e2a69b441ba83970e3576cd5099c66792328f8dca34a","src/guid.rs":"27f3e764fb5787cf8dbf2e284e643e1555a3bd1ae5d0d70b76a9193cd051792f","src/handles.rs":"c5f53785605cc14059c4ad0272e310b435ed2f48405e8e4052fb1d5e40ee4d78","src/imp/bindings.rs":"949bd0444b102df0b63fcbc1451171c1e13400044303fd6aa50a00b1f191c198","src/imp/can_into.rs":"d06228653f20c07dcfd8a4004cd6be7a19a8da0a79226d42653ab2f3bf290816","src/imp/com_bindings.rs":"b0e5f877f7e2db25dd680ae5c75197e9ff4cb0efb2d14083f17866a20aa5440c","src/imp/factory_cache.rs":"64f00976c5e6392de7a2c4d161f58874c7dfdbecab2c983d20d16fd1acb26826","src/imp/generic_factory.rs":"340e2a75a5c4ec36bc7ea442c7211b3be4f8d3a537fda99316f9ce15bde02555","src/imp/mod.rs":"e16decb788da9edf33b7706c1ae21c2c610f0b96b4b1b505fd4c57a6e0a11193","src/imp/ref_count.rs":"57ed3694b12509b05a140c84eab931b69d4c93c446b2842aed708a9ee6a0c862","src/imp/sha1.rs":"bf8707becd6f32e13d23da0b5ecad906858f42af9626b130a3a88bf8b4f0975e","src/imp/waiter.rs":"eae739b667fcdf290cdeb533bd1254d423e1165fa697cba7a522caa5ca66f89b","src/imp/weak_ref_count.rs":"8a69cfce27500ccc0b511de3183c0d19ad260fbffc48a8bf05275b9fc02c7118","src/imp/windows.rs":"6bf14613157d1cb1d0a376fc20fb9ee5dc2d40a64535c528bff526c4e0a895b7","src/inspectable.rs":"d6767bb4796f4e2fd1c03a2cf9fddd97d7b395ca4d56dd6a443333ce92a7865b","src/interface.rs":"c3bcbdcef95f91742215f29d57ee9920fae7923914eb635054e81ad34bd06a12","src/lib.rs":"cb46d0844bcf4ae7a2d3477e206d1623c861e68b3ec0fa8821c87df27eba36e5","src/out_param.rs":"d91dd1a88201c2dbb395d371ba42270bc19191861758496fccba30ad5f8d52e9","src/out_ref.rs":"e12010828943df8c4aa40c9b53316a96f14c208fdb0e860b5206ddc5eedce1d2","src/param.rs":"88f2de2f038e3f8776872c8f9db456a9f012329800b75e001624cdf9671cf97c","src/param_value.rs":"b1d29ec7589cc7eebba1faf01aaae39f9d401dff13d2a6a5ec382f94b1becce0","src/ref.rs":"9cdceb34ad726190459bef5ec8d2a030b0dbf164b5cd969d215a3d09622897f6","src/runtime_name.rs":"b330db95b0ce32c963d6b43a0b152cd96e136e5b38b92c02223fb53b30f83ecf","src/runtime_type.rs":"4f504a1ee3525003d4376d259c5e195a08617bd5aff1789fd9027d51b354eb63","src/scoped_interface.rs":"57d9dcd0284659e2bf465c08d6c857c2c985e90f0316e5677ebd2a656edb6737","src/type.rs":"caf4d12aa96f3204148c33f6853b7f31699fde13b2b4ada3ba2a01faab89647a","src/unknown.rs":"9b6117025d4d18600ef682aa0bacb904c020b0b6bfe6c1ab6d236c716a3abea1","src/variant.rs":"2708f9ba47369879b222f19c8bc29705d03140b254d0c09ab7557a0f71dd006e","src/weak.rs":"0363f44452b6d363431f5b4d83c83be74d6c5eb419a3d5d9a3eb35b611d3c2a6","src/windows.rs":"6cd563c982c98c18f98753b210df17c71a728532392b88d11a1fc8cef9d01397"},"package":"6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"}

View File

@@ -11,10 +11,15 @@
[package]
edition = "2021"
rust-version = "1.56"
rust-version = "1.70"
name = "windows-core"
version = "0.52.0"
version = "0.58.0"
authors = ["Microsoft"]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust for Windows"
readme = "readme.md"
categories = ["os::windows-apis"]
@@ -25,9 +30,37 @@ repository = "https://github.com/microsoft/windows-rs"
default-target = "x86_64-pc-windows-msvc"
targets = []
[lib]
name = "windows_core"
path = "src/lib.rs"
[dependencies.windows-implement]
version = "0.58.0"
[dependencies.windows-interface]
version = "0.58.0"
[dependencies.windows-result]
version = "0.2.0"
[dependencies.windows-strings]
version = "0.1.0"
[dependencies.windows-targets]
version = "0.52.0"
version = "0.52.6"
[features]
default = []
implement = []
default = ["std"]
std = []
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]

View File

@@ -3,14 +3,14 @@
The [windows](https://crates.io/crates/windows) and [windows-sys](https://crates.io/crates/windows-sys) crates let you call any Windows API past, present, and future using code generated on the fly directly from the [metadata describing the API](https://github.com/microsoft/windows-rs/tree/master/crates/libs/bindgen/default) and right into your Rust package where you can call them as if they were just another Rust module. The Rust language projection follows in the tradition established by [C++/WinRT](https://github.com/microsoft/cppwinrt) of building language projections for Windows using standard languages and compilers, providing a natural and idiomatic way for Rust developers to call Windows APIs.
* [Getting started](https://kennykerr.ca/rust-getting-started/)
* [Samples](https://github.com/microsoft/windows-rs/tree/0.52.0/crates/samples) <!-- link to samples for upcoming release -->
* [Samples](https://github.com/microsoft/windows-rs/tree/0.58.0/crates/samples)
* [Releases](https://github.com/microsoft/windows-rs/releases)
Start by adding the following to your Cargo.toml file:
```toml
[dependencies.windows]
version = "0.52"
version = "0.58"
features = [
"Data_Xml_Dom",
"Win32_Foundation",
@@ -81,8 +81,8 @@ fn main() {
WaitForSingleObject(event, 0);
CloseHandle(event);
MessageBoxA(0, s!("Ansi"), s!("Caption"), MB_OK);
MessageBoxW(0, w!("Wide"), w!("Caption"), MB_OK);
MessageBoxA(0 as _, s!("Ansi"), s!("Caption"), MB_OK);
MessageBoxW(0 as _, w!("Wide"), w!("Caption"), MB_OK);
}
}
```

View File

@@ -1,15 +1,26 @@
use super::*;
use std::marker::PhantomData;
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>(crate::imp::IAgileReference, PhantomData<T>);
pub struct AgileReference<T>(imp::IAgileReference, PhantomData<T>);
impl<T: ComInterface> AgileReference<T> {
impl<T: Interface> AgileReference<T> {
/// Creates an agile reference to the object.
pub fn new(object: &T) -> Result<Self> {
unsafe { crate::imp::RoGetAgileReference(crate::imp::AGILEREFERENCE_DEFAULT, &T::IID, object.as_unknown()).map(|reference| Self(reference, Default::default())) }
// 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.
@@ -18,11 +29,11 @@ impl<T: ComInterface> AgileReference<T> {
}
}
unsafe impl<T: ComInterface> Send for AgileReference<T> {}
unsafe impl<T: ComInterface> Sync for AgileReference<T> {}
unsafe impl<T: Interface> Send for AgileReference<T> {}
unsafe impl<T: Interface> Sync for AgileReference<T> {}
impl<T> std::fmt::Debug for AgileReference<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<T> core::fmt::Debug for AgileReference<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "AgileReference({:?})", &self.0)
}
}

View File

@@ -8,7 +8,10 @@ pub struct Array<T: Type<T>> {
impl<T: Type<T>> Default for Array<T> {
fn default() -> Self {
Array { data: std::ptr::null_mut(), len: 0 }
Array {
data: core::ptr::null_mut(),
len: 0,
}
}
}
@@ -20,14 +23,16 @@ impl<T: Type<T>> Array<T> {
/// Creates an array of the given length with default values.
pub fn with_len(len: usize) -> Self {
assert!(len < std::u32::MAX as usize);
let bytes_amount = len.checked_mul(std::mem::size_of::<T>()).expect("Attempted to allocate too large an Array");
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 { crate::imp::CoTaskMemAlloc(bytes_amount) as *mut T::Default };
let data = unsafe { imp::CoTaskMemAlloc(bytes_amount) as *mut T::Default };
assert!(!data.is_null(), "Could not successfully allocate for Array");
@@ -36,7 +41,7 @@ impl<T: Type<T>> Array<T> {
// bytes making the entire array zero initialized. We have assured
// above that the data ptr is not null.
unsafe {
std::ptr::write_bytes(data, 0, len);
core::ptr::write_bytes(data, 0, len);
}
let len = len as u32;
@@ -81,22 +86,22 @@ impl<T: Type<T>> Array<T> {
return;
}
let mut data = std::ptr::null_mut();
let mut data = core::ptr::null_mut();
let mut len = 0;
std::mem::swap(&mut data, &mut self.data);
std::mem::swap(&mut len, &mut self.len);
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`
std::ptr::drop_in_place(std::slice::from_raw_parts_mut(data, len as usize));
core::ptr::drop_in_place(core::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
crate::imp::CoTaskMemFree(data as _);
imp::CoTaskMemFree(data as _);
}
}
@@ -115,12 +120,12 @@ impl<T: Type<T>> Array<T> {
/// 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);
std::mem::forget(self);
core::mem::forget(self);
abi
}
}
impl<T: Type<T>> std::ops::Deref for Array<T> {
impl<T: Type<T>> core::ops::Deref for Array<T> {
type Target = [T::Default];
fn deref(&self) -> &[T::Default] {
@@ -129,18 +134,18 @@ impl<T: Type<T>> std::ops::Deref for Array<T> {
}
// SAFETY: data must not be null if the array is not empty
unsafe { std::slice::from_raw_parts(self.data, self.len as usize) }
unsafe { core::slice::from_raw_parts(self.data, self.len as usize) }
}
}
impl<T: Type<T>> std::ops::DerefMut for Array<T> {
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 { std::slice::from_raw_parts_mut(self.data, self.len as usize) }
unsafe { core::slice::from_raw_parts_mut(self.data, self.len as usize) }
}
}
@@ -154,12 +159,16 @@ impl<T: Type<T>> Drop for Array<T> {
pub struct ArrayProxy<T: Type<T>> {
data: *mut *mut T::Default,
len: *mut u32,
temp: std::mem::ManuallyDrop<Array<T>>,
temp: core::mem::ManuallyDrop<Array<T>>,
}
impl<T: Type<T>> ArrayProxy<T> {
pub fn from_raw_parts(data: *mut *mut T::Default, len: *mut u32) -> Self {
Self { data, len, temp: std::mem::ManuallyDrop::new(Array::new()) }
Self {
data,
len,
temp: core::mem::ManuallyDrop::new(Array::new()),
}
}
pub fn as_array(&mut self) -> &mut Array<T> {

View File

@@ -6,5 +6,15 @@ pub trait AsImpl<T> {
///
/// The caller needs to ensure that `self` is actually implemented by the
/// implementation `T`.
unsafe fn as_impl(&self) -> &T;
unsafe fn as_impl(&self) -> &T {
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>;
}

View File

@@ -1,50 +0,0 @@
use super::*;
/// Provides low-level access to a COM interface.
///
/// This trait is automatically implemented by the generated bindings and should not be
/// implemented manually.
///
/// # Safety
///
/// It is only safe to implement this trait if the implementing type is a valid COM interface pointer meaning
/// the following criteria are met:
/// * its in-memory representation is equal to `NonNull<NonNull<Self::VTable>>`
/// * the vtable is correctly structured beginning with the `IUnknown` function pointers
/// * the vtable must be the correct vtable for the supplied IID
pub unsafe trait ComInterface: Interface + Clone {
/// A unique identifier representing this interface.
const IID: GUID;
// Casts the `ComInterface` to a `IUnknown`.
fn as_unknown(&self) -> &IUnknown {
// SAFETY: it is always safe to treat a `ComInterface` as an `IUnknown`.
unsafe { std::mem::transmute(self) }
}
/// 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.
fn cast<T: ComInterface>(&self) -> Result<T> {
let mut result = None;
// 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 `ComInterface` has guaranteed is correct
unsafe { self.query(&T::IID, &mut result as *mut _ as _).and_some(result) }
}
/// Attempts to create a [`Weak`] reference to this object.
fn downgrade(&self) -> Result<Weak<Self>> {
self.cast::<crate::imp::IWeakReferenceSource>().and_then(|source| Weak::downgrade(&source))
}
/// Call `QueryInterface` on this interface
///
/// # Safety
///
/// `interface` must be a non-null, valid pointer for writing an interface pointer.
unsafe fn query(&self, iid: *const GUID, interface: *mut *mut std::ffi::c_void) -> HRESULT {
(self.assume_vtable::<IUnknown>().QueryInterface)(self.as_raw(), iid, interface)
}
}

View File

@@ -0,0 +1,332 @@
use crate::imp::Box;
use crate::{AsImpl, 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>
where
IUnknown: From<T> + AsImpl<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) -> ComObject<T> {
ComObject::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: &ComObject<T>) -> 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()
}
}

View File

@@ -1,159 +0,0 @@
use super::*;
/// An error object consists of both an error code as well as detailed error information for debugging.
#[derive(Clone, PartialEq, Eq)]
pub struct Error {
pub(crate) code: HRESULT,
pub(crate) info: Option<crate::imp::IRestrictedErrorInfo>,
}
impl Error {
/// An error object without any failure information.
pub const OK: Self = Self { code: HRESULT(0), info: None };
/// This creates a new WinRT error object, capturing the stack and other information about the
/// point of failure.
pub fn new(code: HRESULT, message: HSTRING) -> Self {
unsafe {
if let Some(function) = crate::imp::delay_load::<RoOriginateError>(s!("combase.dll"), s!("RoOriginateError")) {
function(code, std::mem::transmute_copy(&message));
}
let info = GetErrorInfo().and_then(|e| e.cast()).ok();
Self { code, info }
}
}
pub fn from_win32() -> Self {
unsafe { Self { code: HRESULT::from_win32(crate::imp::GetLastError()), info: None } }
}
/// The error code describing the error.
pub const fn code(&self) -> HRESULT {
self.code
}
/// The error information describing the error.
pub const fn info(&self) -> &Option<crate::imp::IRestrictedErrorInfo> {
&self.info
}
/// The error message describing the error.
pub fn message(&self) -> HSTRING {
// First attempt to retrieve the restricted error information.
if let Some(info) = &self.info {
let mut fallback = BSTR::default();
let mut message = BSTR::default();
let mut code = HRESULT(0);
unsafe {
let _ = info.GetErrorDetails(&mut fallback, &mut code, &mut message, &mut BSTR::default());
}
if self.code == code {
let message = if !message.is_empty() { message } else { fallback };
return HSTRING::from_wide(crate::imp::wide_trim_end(message.as_wide())).unwrap_or_default();
}
}
self.code.message()
}
}
impl std::convert::From<Error> for HRESULT {
fn from(error: Error) -> Self {
let code = error.code;
let info: Option<crate::imp::IErrorInfo> = error.info.and_then(|info| info.cast().ok());
unsafe {
let _ = crate::imp::SetErrorInfo(0, info.as_ref());
}
code
}
}
impl std::convert::From<Error> for std::io::Error {
fn from(from: Error) -> Self {
Self::from_raw_os_error(from.code.0)
}
}
impl std::convert::From<std::string::FromUtf16Error> for Error {
fn from(_: std::string::FromUtf16Error) -> Self {
Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None }
}
}
impl std::convert::From<std::string::FromUtf8Error> for Error {
fn from(_: std::string::FromUtf8Error) -> Self {
Self { code: HRESULT::from_win32(crate::imp::ERROR_NO_UNICODE_TRANSLATION), info: None }
}
}
impl std::convert::From<std::num::TryFromIntError> for Error {
fn from(_: std::num::TryFromIntError) -> Self {
Self { code: HRESULT(crate::imp::E_INVALIDARG), info: None }
}
}
// Unfortunately this is needed to make types line up. The Rust type system does
// not know the `Infallible` can never be constructed. This code needs to be here
// to satesify the type checker but it will never be run. Once `!` is stabilizied
// this can be removed.
impl std::convert::From<std::convert::Infallible> for Error {
fn from(_: std::convert::Infallible) -> Self {
unreachable!()
}
}
impl std::convert::From<HRESULT> for Error {
fn from(code: HRESULT) -> Self {
let info: Option<crate::imp::IRestrictedErrorInfo> = GetErrorInfo().and_then(|e| e.cast()).ok();
if let Some(info) = info {
// If it does (and therefore running on a recent version of Windows)
// then capture_propagation_context adds a breadcrumb to the error
// info to make debugging easier.
if let Ok(capture) = info.cast::<crate::imp::ILanguageExceptionErrorInfo2>() {
unsafe {
let _ = capture.CapturePropagationContext(None);
}
}
return Self { code, info: Some(info) };
}
if let Ok(info) = GetErrorInfo() {
let message = unsafe { info.GetDescription().unwrap_or_default() };
Self::new(code, HSTRING::from_wide(message.as_wide()).unwrap_or_default())
} else {
Self { code, info: None }
}
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = fmt.debug_struct("Error");
debug.field("code", &self.code).field("message", &self.message()).finish()
}
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = self.message();
if message.is_empty() {
std::write!(fmt, "{}", self.code())
} else {
std::write!(fmt, "{} ({})", self.message(), self.code())
}
}
}
impl std::error::Error for Error {}
type RoOriginateError = extern "system" fn(code: HRESULT, message: *mut std::ffi::c_void) -> i32;
fn GetErrorInfo() -> Result<crate::imp::IErrorInfo> {
unsafe { crate::imp::GetErrorInfo(0) }
}

View File

@@ -1,26 +1,34 @@
use super::*;
use std::sync::*;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{size_of, transmute_copy};
use core::ptr::null_mut;
use std::sync::Mutex;
/// 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: ComInterface> {
pub struct Event<T: Interface> {
swap: Mutex<()>,
change: Mutex<()>,
delegates: Array<T>,
}
impl<T: ComInterface> Default for Event<T> {
impl<T: Interface> Default for Event<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: ComInterface> Event<T> {
impl<T: Interface> Event<T> {
/// Creates a new, empty `Event<T>`.
pub fn new() -> Self {
Self { delegates: Array::new(), swap: Mutex::default(), change: Mutex::default() }
Self {
delegates: Array::new(),
swap: Mutex::default(),
change: Mutex::default(),
}
}
/// Registers a delegate with the event object.
@@ -99,7 +107,10 @@ impl<T: ComInterface> Event<T> {
for delegate in lock_free_calls.as_slice() {
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(), crate::imp::RPC_E_DISCONNECTED | crate::imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE) {
if matches!(
error.code(),
imp::RPC_E_DISCONNECTED | imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE
) {
self.remove(delegate.to_token())?;
}
}
@@ -109,33 +120,41 @@ impl<T: ComInterface> Event<T> {
}
/// A thread-safe reference-counted array of delegates.
struct Array<T: ComInterface> {
struct Array<T: Interface> {
buffer: *mut Buffer<T>,
len: usize,
_phantom: std::marker::PhantomData<T>,
_phantom: PhantomData<T>,
}
impl<T: ComInterface> Default for Array<T> {
impl<T: Interface> Default for Array<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: ComInterface> Array<T> {
impl<T: Interface> Array<T> {
/// Creates a new, empty `Array<T>` with no capacity.
fn new() -> Self {
Self { buffer: std::ptr::null_mut(), len: 0, _phantom: std::marker::PhantomData }
Self {
buffer: null_mut(),
len: 0,
_phantom: PhantomData,
}
}
/// Creates a new, empty `Array<T>` with the specified capacity.
fn with_capacity(capacity: usize) -> Result<Self> {
Ok(Self { buffer: Buffer::new(capacity)?, len: 0, _phantom: std::marker::PhantomData })
Ok(Self {
buffer: Buffer::new(capacity)?,
len: 0,
_phantom: PhantomData,
})
}
/// Swaps the contents of two `Array<T>` objects.
fn swap(&mut self, mut other: Self) -> Self {
unsafe { std::ptr::swap(&mut self.buffer, &mut other.buffer) };
std::mem::swap(&mut self.len, &mut other.len);
unsafe { core::ptr::swap(&mut self.buffer, &mut other.buffer) };
core::mem::swap(&mut self.len, &mut other.len);
other
}
@@ -152,7 +171,7 @@ impl<T: ComInterface> Array<T> {
/// Appends a delegate to the back of the array.
fn push(&mut self, delegate: Delegate<T>) {
unsafe {
std::ptr::write((*self.buffer).as_mut_ptr().add(self.len), delegate);
(*self.buffer).as_mut_ptr().add(self.len).write(delegate);
self.len += 1;
}
}
@@ -162,7 +181,7 @@ impl<T: ComInterface> Array<T> {
if self.is_empty() {
&[]
} else {
unsafe { std::slice::from_raw_parts((*self.buffer).as_ptr(), self.len) }
unsafe { core::slice::from_raw_parts((*self.buffer).as_ptr(), self.len) }
}
}
@@ -171,26 +190,30 @@ impl<T: ComInterface> Array<T> {
if self.is_empty() {
&mut []
} else {
unsafe { std::slice::from_raw_parts_mut((*self.buffer).as_mut_ptr(), self.len) }
unsafe { core::slice::from_raw_parts_mut((*self.buffer).as_mut_ptr(), self.len) }
}
}
}
impl<T: ComInterface> Clone for Array<T> {
impl<T: Interface> Clone for Array<T> {
fn clone(&self) -> Self {
if !self.is_empty() {
unsafe { (*self.buffer).0.add_ref() };
}
Self { buffer: self.buffer, len: self.len, _phantom: std::marker::PhantomData }
Self {
buffer: self.buffer,
len: self.len,
_phantom: PhantomData,
}
}
}
impl<T: ComInterface> Drop for Array<T> {
impl<T: Interface> Drop for Array<T> {
fn drop(&mut self) {
unsafe {
if !self.is_empty() && (*self.buffer).0.release() == 0 {
std::ptr::drop_in_place(self.as_mut_slice());
crate::imp::heap_free(self.buffer as _)
core::ptr::drop_in_place(self.as_mut_slice());
heap_free(self.buffer as _)
}
}
}
@@ -198,18 +221,19 @@ impl<T: ComInterface> Drop for Array<T> {
/// A reference-counted buffer.
#[repr(C)]
struct Buffer<T>(crate::imp::RefCount, std::marker::PhantomData<T>);
#[repr(align(8))]
struct Buffer<T>(imp::RefCount, PhantomData<T>);
impl<T: ComInterface> Buffer<T> {
impl<T: Interface> Buffer<T> {
/// Creates a new `Buffer` with the specified size in bytes.
fn new(len: usize) -> Result<*mut Self> {
if len == 0 {
Ok(std::ptr::null_mut())
Ok(null_mut())
} else {
let alloc_size = std::mem::size_of::<Self>() + len * std::mem::size_of::<Delegate<T>>();
let header = crate::imp::heap_alloc(alloc_size)? as *mut Self;
let alloc_size = size_of::<Self>() + len * size_of::<Delegate<T>>();
let header = heap_alloc(alloc_size)? as *mut Self;
unsafe {
header.write(Self(crate::imp::RefCount::new(1), std::marker::PhantomData));
header.write(Self(imp::RefCount::new(1), PhantomData));
}
Ok(header)
}
@@ -234,10 +258,10 @@ enum Delegate<T> {
Indirect(AgileReference<T>),
}
impl<T: ComInterface> Delegate<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::<crate::imp::IAgileObject>().is_ok() {
if delegate.cast::<imp::IAgileObject>().is_ok() {
Ok(Self::Direct(delegate.clone()))
} else {
Ok(Self::Indirect(AgileReference::new(delegate)?))
@@ -248,8 +272,8 @@ impl<T: ComInterface> Delegate<T> {
fn to_token(&self) -> i64 {
unsafe {
match self {
Self::Direct(delegate) => crate::imp::EncodePointer(std::mem::transmute_copy(delegate)) as i64,
Self::Indirect(delegate) => crate::imp::EncodePointer(std::mem::transmute_copy(delegate)) as i64,
Self::Direct(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64,
Self::Indirect(delegate) => imp::EncodePointer(transmute_copy(delegate)) as i64,
}
}
}
@@ -262,3 +286,30 @@ impl<T: ComInterface> Delegate<T> {
}
}
}
/// Allocate memory of size `bytes` using `malloc` - the `Event` implementation does not
/// need to use any particular allocator so `HeapAlloc` need not be used.
fn heap_alloc(bytes: usize) -> crate::Result<*mut c_void> {
let ptr: *mut c_void = unsafe {
extern "C" {
fn malloc(bytes: usize) -> *mut c_void;
}
malloc(bytes)
};
if ptr.is_null() {
Err(Error::from_hresult(imp::E_OUTOFMEMORY))
} else {
Ok(ptr)
}
}
/// Free memory allocated by `heap_alloc`.
unsafe fn heap_free(ptr: *mut c_void) {
extern "C" {
fn free(ptr: *mut c_void);
}
free(ptr);
}

View File

@@ -7,9 +7,16 @@ use super::*;
#[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],
}
@@ -21,27 +28,48 @@ impl GUID {
/// 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] }
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 }
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() }
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
((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 = 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);
@@ -53,7 +81,14 @@ impl GUID {
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]])
Self::from_values(
first,
second,
third,
[
fourth, bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
],
)
}
}
@@ -65,22 +100,44 @@ impl TypeKind for GUID {
type TypeKind = CopyType;
}
impl std::fmt::Debug for GUID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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 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 std::convert::From<&str> for GUID {
impl From<&str> for GUID {
fn from(value: &str) -> Self {
assert!(value.len() == 36, "Invalid GUID string");
let mut bytes = value.bytes();
let a = ((bytes.next_u32() * 16 + bytes.next_u32()) << 24) + ((bytes.next_u32() * 16 + bytes.next_u32()) << 16) + ((bytes.next_u32() * 16 + bytes.next_u32()) << 8) + bytes.next_u32() * 16 + bytes.next_u32();
let a = ((bytes.next_u32() * 16 + bytes.next_u32()) << 24)
+ ((bytes.next_u32() * 16 + bytes.next_u32()) << 16)
+ ((bytes.next_u32() * 16 + bytes.next_u32()) << 8)
+ bytes.next_u32() * 16
+ bytes.next_u32();
assert!(bytes.next().unwrap() == b'-', "Invalid GUID string");
let b = ((bytes.next_u16() * 16 + (bytes.next_u16())) << 8) + bytes.next_u16() * 16 + bytes.next_u16();
let b = ((bytes.next_u16() * 16 + (bytes.next_u16())) << 8)
+ bytes.next_u16() * 16
+ bytes.next_u16();
assert!(bytes.next().unwrap() == b'-', "Invalid GUID string");
let c = ((bytes.next_u16() * 16 + bytes.next_u16()) << 8) + bytes.next_u16() * 16 + bytes.next_u16();
let c = ((bytes.next_u16() * 16 + bytes.next_u16()) << 8)
+ bytes.next_u16() * 16
+ bytes.next_u16();
assert!(bytes.next().unwrap() == b'-', "Invalid GUID string");
let d = bytes.next_u8() * 16 + bytes.next_u8();
let e = bytes.next_u8() * 16 + bytes.next_u8();
@@ -97,13 +154,13 @@ impl std::convert::From<&str> for GUID {
}
}
impl std::convert::From<u128> for GUID {
impl From<u128> for GUID {
fn from(value: u128) -> Self {
Self::from_u128(value)
}
}
impl std::convert::From<GUID> for u128 {
impl From<GUID> for u128 {
fn from(value: GUID) -> Self {
value.to_u128()
}
@@ -115,7 +172,7 @@ trait HexReader {
fn next_u32(&mut self) -> u32;
}
impl HexReader for std::str::Bytes<'_> {
impl HexReader for core::str::Bytes<'_> {
fn next_u8(&mut self) -> u8 {
let value = self.next().unwrap();
match value {

View File

@@ -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
}
}

View File

@@ -1,140 +0,0 @@
use super::*;
/// An error code value returned by most COM functions.
#[repr(transparent)]
#[derive(Copy, Clone, Default, Eq, PartialEq)]
#[must_use]
#[allow(non_camel_case_types)]
pub struct HRESULT(pub i32);
impl HRESULT {
/// Returns [`true`] if `self` is a success code.
#[inline]
pub const fn is_ok(self) -> bool {
self.0 >= 0
}
/// Returns [`true`] if `self` is a failure code.
#[inline]
pub const fn is_err(self) -> bool {
!self.is_ok()
}
/// Asserts that `self` is a success code.
///
/// This will invoke the [`panic!`] macro if `self` is a failure code and display
/// the [`HRESULT`] value for diagnostics.
#[inline]
#[track_caller]
pub fn unwrap(self) {
assert!(self.is_ok(), "HRESULT 0x{:X}", self.0);
}
/// Converts the [`HRESULT`] to [`Result<()>`][Result<_>].
#[inline]
pub fn ok(self) -> Result<()> {
if self.is_ok() {
Ok(())
} else {
Err(Error::from(self))
}
}
/// Returns the [`Option`] as a [`Result`] if the option is a [`Some`] value, returning
/// a suitable error if not.
pub fn and_some<T: Interface>(self, some: Option<T>) -> Result<T> {
if self.is_ok() {
if let Some(result) = some {
Ok(result)
} else {
Err(Error::OK)
}
} else {
Err(Error::from(self))
}
}
/// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`]
/// converted to [`Result<T>`].
#[inline]
pub fn and_then<F, T>(self, op: F) -> Result<T>
where
F: FnOnce() -> T,
{
self.ok()?;
Ok(op())
}
/// If the [`Result`] is [`Ok`] converts the `T::Abi` into `T`.
///
/// # Safety
///
/// Safe to call if
/// * `abi` is initialized if `self` is `Ok`
/// * `abi` can be safely transmuted to `T`
pub unsafe fn from_abi<T: Type<T>>(self, abi: T::Abi) -> Result<T> {
if self.is_ok() {
T::from_abi(abi)
} else {
Err(Error::from(self))
}
}
/// The error message describing the error.
pub fn message(&self) -> HSTRING {
let mut message = HeapString(std::ptr::null_mut());
unsafe {
let size = crate::imp::FormatMessageW(crate::imp::FORMAT_MESSAGE_ALLOCATE_BUFFER | crate::imp::FORMAT_MESSAGE_FROM_SYSTEM | crate::imp::FORMAT_MESSAGE_IGNORE_INSERTS, std::ptr::null(), self.0 as u32, 0, &mut message.0 as *mut _ as *mut _, 0, std::ptr::null());
HSTRING::from_wide(crate::imp::wide_trim_end(std::slice::from_raw_parts(message.0 as *const u16, size as usize))).unwrap_or_default()
}
}
/// Maps a Win32 error code to an HRESULT value.
pub const fn from_win32(error: u32) -> Self {
Self(if error as i32 <= 0 { error } else { (error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000 } as i32)
}
}
impl RuntimeType for HRESULT {
const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"struct(Windows.Foundation.HResult;i32)");
}
impl TypeKind for HRESULT {
type TypeKind = CopyType;
}
impl<T> std::convert::From<Result<T>> for HRESULT {
fn from(result: Result<T>) -> Self {
if let Err(error) = result {
return error.into();
}
HRESULT(0)
}
}
impl std::fmt::Display for HRESULT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:#010X}", self.0))
}
}
impl std::fmt::Debug for HRESULT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("HRESULT({})", self))
}
}
struct HeapString(*mut u16);
impl Drop for HeapString {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
crate::imp::heap_free(self.0 as _);
}
}
}
}

View File

@@ -1,53 +1,671 @@
// Bindings generated by `windows-bindgen` 0.52.0
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
::windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE);
::windows_targets::link!("kernel32.dll" "system" fn EncodePointer(ptr : *const ::core::ffi::c_void) -> *mut ::core::ffi::c_void);
::windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const ::core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32);
::windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
::windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
::windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE);
::windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap : HANDLE, dwflags : HEAP_FLAGS, dwbytes : usize) -> *mut ::core::ffi::c_void);
::windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const ::core::ffi::c_void) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE);
::windows_targets::link!("kernel32.dll" "system" fn SetEvent(hevent : HANDLE) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
::windows_targets::link!("ole32.dll" "system" fn CoTaskMemAlloc(cb : usize) -> *mut ::core::ffi::c_void);
::windows_targets::link!("ole32.dll" "system" fn CoTaskMemFree(pv : *const ::core::ffi::c_void) -> ());
::windows_targets::link!("oleaut32.dll" "system" fn SysAllocStringLen(strin : PCWSTR, ui : u32) -> BSTR);
::windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR) -> ());
::windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
dead_code,
clippy::all
)]
windows_targets::link!("api-ms-win-core-winrt-l1-1-0.dll" "system" fn RoGetActivationFactory(activatableclassid : * mut core::ffi::c_void, iid : *const GUID, factory : *mut *mut core::ffi::c_void) -> HRESULT);
windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn EncodePointer(ptr : *const core::ffi::c_void) -> *mut core::ffi::c_void);
windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
windows_targets::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE);
windows_targets::link!("kernel32.dll" "system" fn SetEvent(hevent : HANDLE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
windows_targets::link!("ole32.dll" "system" fn CoIncrementMTAUsage(pcookie : *mut CO_MTA_USAGE_COOKIE) -> HRESULT);
windows_targets::link!("ole32.dll" "system" fn CoTaskMemAlloc(cb : usize) -> *mut core::ffi::c_void);
windows_targets::link!("ole32.dll" "system" fn CoTaskMemFree(pv : *const core::ffi::c_void));
windows_targets::link!("ole32.dll" "system" fn PropVariantClear(pvar : *mut PROPVARIANT) -> HRESULT);
windows_targets::link!("ole32.dll" "system" fn PropVariantCopy(pvardest : *mut PROPVARIANT, pvarsrc : *const PROPVARIANT) -> HRESULT);
windows_targets::link!("oleaut32.dll" "system" fn VariantClear(pvarg : *mut VARIANT) -> HRESULT);
windows_targets::link!("oleaut32.dll" "system" fn VariantCopy(pvargdest : *mut VARIANT, pvargsrc : *const VARIANT) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantCompareEx(propvar1 : *const PROPVARIANT, propvar2 : *const PROPVARIANT, unit : PROPVAR_COMPARE_UNIT, flags : PROPVAR_COMPARE_FLAGS) -> i32);
windows_targets::link!("propsys.dll" "system" fn PropVariantToBSTR(propvar : *const PROPVARIANT, pbstrout : *mut BSTR) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToBoolean(propvarin : *const PROPVARIANT, pfret : *mut BOOL) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToDouble(propvarin : *const PROPVARIANT, pdblret : *mut f64) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToInt16(propvarin : *const PROPVARIANT, piret : *mut i16) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToInt32(propvarin : *const PROPVARIANT, plret : *mut i32) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToInt64(propvarin : *const PROPVARIANT, pllret : *mut i64) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToUInt16(propvarin : *const PROPVARIANT, puiret : *mut u16) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToUInt32(propvarin : *const PROPVARIANT, pulret : *mut u32) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToUInt64(propvarin : *const PROPVARIANT, pullret : *mut u64) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn PropVariantToVariant(ppropvar : *const PROPVARIANT, pvar : *mut VARIANT) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToBoolean(varin : *const VARIANT, pfret : *mut BOOL) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToDouble(varin : *const VARIANT, pdblret : *mut f64) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToInt16(varin : *const VARIANT, piret : *mut i16) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToInt32(varin : *const VARIANT, plret : *mut i32) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToInt64(varin : *const VARIANT, pllret : *mut i64) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToPropVariant(pvar : *const VARIANT, ppropvar : *mut PROPVARIANT) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToUInt16(varin : *const VARIANT, puiret : *mut u16) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToUInt32(varin : *const VARIANT, pulret : *mut u32) -> HRESULT);
windows_targets::link!("propsys.dll" "system" fn VariantToUInt64(varin : *const VARIANT, pullret : *mut u64) -> HRESULT);
pub type ADVANCED_FEATURE_FLAGS = u16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ARRAYDESC {
pub tdescElem: TYPEDESC,
pub cDims: u16,
pub rgbounds: [SAFEARRAYBOUND; 1],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union BINDPTR {
pub lpfuncdesc: *mut FUNCDESC,
pub lpvardesc: *mut VARDESC,
pub lptcomp: *mut core::ffi::c_void,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct BLOB {
pub cbSize: u32,
pub pBlobData: *mut u8,
}
pub type BOOL = i32;
pub type BSTR = *const u16;
pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32;
pub const E_INVALIDARG: HRESULT = -2147024809i32;
pub type FARPROC = ::core::option::Option<unsafe extern "system" fn() -> isize>;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32;
pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32;
pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32;
pub type FORMAT_MESSAGE_OPTIONS = u32;
pub type HANDLE = isize;
pub type HEAP_FLAGS = u32;
pub type HMODULE = isize;
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 PCWSTR = *const u16;
pub type PWSTR = *mut u16;
#[repr(C)]
pub struct SECURITY_ATTRIBUTES {
pub nLength: u32,
pub lpSecurityDescriptor: *mut ::core::ffi::c_void,
pub bInheritHandle: BOOL,
#[derive(Clone, Copy)]
pub struct BSTRBLOB {
pub cbSize: u32,
pub pData: *mut u8,
}
impl ::core::marker::Copy for SECURITY_ATTRIBUTES {}
impl ::core::clone::Clone for SECURITY_ATTRIBUTES {
fn clone(&self) -> Self {
*self
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CABOOL {
pub cElems: u32,
pub pElems: *mut VARIANT_BOOL,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CABSTR {
pub cElems: u32,
pub pElems: *mut BSTR,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CABSTRBLOB {
pub cElems: u32,
pub pElems: *mut BSTRBLOB,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAC {
pub cElems: u32,
pub pElems: PSTR,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CACLIPDATA {
pub cElems: u32,
pub pElems: *mut CLIPDATA,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CACLSID {
pub cElems: u32,
pub pElems: *mut GUID,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CACY {
pub cElems: u32,
pub pElems: *mut CY,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CADATE {
pub cElems: u32,
pub pElems: *mut f64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CADBL {
pub cElems: u32,
pub pElems: *mut f64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAFILETIME {
pub cElems: u32,
pub pElems: *mut FILETIME,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAFLT {
pub cElems: u32,
pub pElems: *mut f32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAH {
pub cElems: u32,
pub pElems: *mut i64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAI {
pub cElems: u32,
pub pElems: *mut i16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAL {
pub cElems: u32,
pub pElems: *mut i32,
}
pub type CALLCONV = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CALPSTR {
pub cElems: u32,
pub pElems: *mut PSTR,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CALPWSTR {
pub cElems: u32,
pub pElems: *mut PWSTR,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAPROPVARIANT {
pub cElems: u32,
pub pElems: *mut PROPVARIANT,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CASCODE {
pub cElems: u32,
pub pElems: *mut i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAUB {
pub cElems: u32,
pub pElems: *mut u8,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAUH {
pub cElems: u32,
pub pElems: *mut u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAUI {
pub cElems: u32,
pub pElems: *mut u16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CAUL {
pub cElems: u32,
pub pElems: *mut u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CLIPDATA {
pub cbSize: u32,
pub ulClipFmt: i32,
pub pClipData: *mut u8,
}
pub type CO_MTA_USAGE_COOKIE = *mut core::ffi::c_void;
#[repr(C)]
#[derive(Clone, Copy)]
pub union CY {
pub Anonymous: CY_0,
pub int64: i64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CY_0 {
pub Lo: u32,
pub Hi: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct DECIMAL {
pub wReserved: u16,
pub Anonymous1: DECIMAL_0,
pub Hi32: u32,
pub Anonymous2: DECIMAL_1,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union DECIMAL_0 {
pub Anonymous: DECIMAL_0_0,
pub signscale: u16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct DECIMAL_0_0 {
pub scale: u8,
pub sign: u8,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union DECIMAL_1 {
pub Anonymous: DECIMAL_1_0,
pub Lo64: u64,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct DECIMAL_1_0 {
pub Lo32: u32,
pub Mid32: u32,
}
pub type DESCKIND = i32;
pub type DISPATCH_FLAGS = u16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct DISPPARAMS {
pub rgvarg: *mut VARIANT,
pub rgdispidNamedArgs: *mut i32,
pub cArgs: u32,
pub cNamedArgs: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ELEMDESC {
pub tdesc: TYPEDESC,
pub Anonymous: ELEMDESC_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union ELEMDESC_0 {
pub idldesc: IDLDESC,
pub paramdesc: PARAMDESC,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EXCEPINFO {
pub wCode: u16,
pub wReserved: u16,
pub bstrSource: BSTR,
pub bstrDescription: BSTR,
pub bstrHelpFile: BSTR,
pub dwHelpContext: u32,
pub pvReserved: *mut core::ffi::c_void,
pub pfnDeferredFillIn: LPEXCEPFINO_DEFERRED_FILLIN,
pub scode: i32,
}
pub type FARPROC = Option<unsafe extern "system" fn() -> isize>;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FILETIME {
pub dwLowDateTime: u32,
pub dwHighDateTime: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FUNCDESC {
pub memid: i32,
pub lprgscode: *mut i32,
pub lprgelemdescParam: *mut ELEMDESC,
pub funckind: FUNCKIND,
pub invkind: INVOKEKIND,
pub callconv: CALLCONV,
pub cParams: i16,
pub cParamsOpt: i16,
pub oVft: i16,
pub cScodes: i16,
pub elemdescFunc: ELEMDESC,
pub wFuncFlags: FUNCFLAGS,
}
pub type FUNCFLAGS = u16;
pub type FUNCKIND = i32;
#[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 HMODULE = *mut core::ffi::c_void;
pub type HRESULT = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct IDLDESC {
pub dwReserved: usize,
pub wIDLFlags: IDLFLAGS,
}
pub type IDLFLAGS = u16;
pub type IMPLTYPEFLAGS = i32;
pub type INVOKEKIND = i32;
pub type LOAD_LIBRARY_FLAGS = u32;
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: LOAD_LIBRARY_FLAGS = 4096u32;
pub type LPEXCEPFINO_DEFERRED_FILLIN =
Option<unsafe extern "system" fn(pexcepinfo: *mut EXCEPINFO) -> HRESULT>;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PARAMDESC {
pub pparamdescex: *mut PARAMDESCEX,
pub wParamFlags: PARAMFLAGS,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PARAMDESCEX {
pub cBytes: u32,
pub varDefaultValue: VARIANT,
}
pub type PARAMFLAGS = u16;
pub type PCSTR = *const u8;
pub type PCWSTR = *const u16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PROPVARIANT {
pub Anonymous: PROPVARIANT_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union PROPVARIANT_0 {
pub Anonymous: PROPVARIANT_0_0,
pub decVal: DECIMAL,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PROPVARIANT_0_0 {
pub vt: VARENUM,
pub wReserved1: u16,
pub wReserved2: u16,
pub wReserved3: u16,
pub Anonymous: PROPVARIANT_0_0_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union PROPVARIANT_0_0_0 {
pub cVal: i8,
pub bVal: u8,
pub iVal: i16,
pub uiVal: u16,
pub lVal: i32,
pub ulVal: u32,
pub intVal: i32,
pub uintVal: u32,
pub hVal: i64,
pub uhVal: u64,
pub fltVal: f32,
pub dblVal: f64,
pub boolVal: VARIANT_BOOL,
pub __OBSOLETE__VARIANT_BOOL: VARIANT_BOOL,
pub scode: i32,
pub cyVal: CY,
pub date: f64,
pub filetime: FILETIME,
pub puuid: *mut GUID,
pub pclipdata: *mut CLIPDATA,
pub bstrVal: BSTR,
pub bstrblobVal: BSTRBLOB,
pub blob: BLOB,
pub pszVal: PSTR,
pub pwszVal: PWSTR,
pub punkVal: *mut core::ffi::c_void,
pub pdispVal: *mut core::ffi::c_void,
pub pStream: *mut core::ffi::c_void,
pub pStorage: *mut core::ffi::c_void,
pub pVersionedStream: *mut VERSIONEDSTREAM,
pub parray: *mut SAFEARRAY,
pub cac: CAC,
pub caub: CAUB,
pub cai: CAI,
pub caui: CAUI,
pub cal: CAL,
pub caul: CAUL,
pub cah: CAH,
pub cauh: CAUH,
pub caflt: CAFLT,
pub cadbl: CADBL,
pub cabool: CABOOL,
pub cascode: CASCODE,
pub cacy: CACY,
pub cadate: CADATE,
pub cafiletime: CAFILETIME,
pub cauuid: CACLSID,
pub caclipdata: CACLIPDATA,
pub cabstr: CABSTR,
pub cabstrblob: CABSTRBLOB,
pub calpstr: CALPSTR,
pub calpwstr: CALPWSTR,
pub capropvar: CAPROPVARIANT,
pub pcVal: PSTR,
pub pbVal: *mut u8,
pub piVal: *mut i16,
pub puiVal: *mut u16,
pub plVal: *mut i32,
pub pulVal: *mut u32,
pub pintVal: *mut i32,
pub puintVal: *mut u32,
pub pfltVal: *mut f32,
pub pdblVal: *mut f64,
pub pboolVal: *mut VARIANT_BOOL,
pub pdecVal: *mut DECIMAL,
pub pscode: *mut i32,
pub pcyVal: *mut CY,
pub pdate: *mut f64,
pub pbstrVal: *mut BSTR,
pub ppunkVal: *mut *mut core::ffi::c_void,
pub ppdispVal: *mut *mut core::ffi::c_void,
pub pparray: *mut *mut SAFEARRAY,
pub pvarVal: *mut PROPVARIANT,
}
pub type PROPVAR_COMPARE_FLAGS = i32;
pub type PROPVAR_COMPARE_UNIT = i32;
pub type PSTR = *mut u8;
pub type PWSTR = *mut u16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SAFEARRAY {
pub cDims: u16,
pub fFeatures: ADVANCED_FEATURE_FLAGS,
pub cbElements: u32,
pub cLocks: u32,
pub pvData: *mut core::ffi::c_void,
pub rgsabound: [SAFEARRAYBOUND; 1],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SAFEARRAYBOUND {
pub cElements: u32,
pub lLbound: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SECURITY_ATTRIBUTES {
pub nLength: u32,
pub lpSecurityDescriptor: *mut core::ffi::c_void,
pub bInheritHandle: BOOL,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct STATSTG {
pub pwcsName: PWSTR,
pub r#type: u32,
pub cbSize: u64,
pub mtime: FILETIME,
pub ctime: FILETIME,
pub atime: FILETIME,
pub grfMode: STGM,
pub grfLocksSupported: u32,
pub clsid: GUID,
pub grfStateBits: u32,
pub reserved: u32,
}
pub type STGM = u32;
pub type STREAM_SEEK = u32;
pub type SYSKIND = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct TLIBATTR {
pub guid: GUID,
pub lcid: u32,
pub syskind: SYSKIND,
pub wMajorVerNum: u16,
pub wMinorVerNum: u16,
pub wLibFlags: u16,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct TYPEATTR {
pub guid: GUID,
pub lcid: u32,
pub dwReserved: u32,
pub memidConstructor: i32,
pub memidDestructor: i32,
pub lpstrSchema: PWSTR,
pub cbSizeInstance: u32,
pub typekind: TYPEKIND,
pub cFuncs: u16,
pub cVars: u16,
pub cImplTypes: u16,
pub cbSizeVft: u16,
pub cbAlignment: u16,
pub wTypeFlags: u16,
pub wMajorVerNum: u16,
pub wMinorVerNum: u16,
pub tdescAlias: TYPEDESC,
pub idldescType: IDLDESC,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct TYPEDESC {
pub Anonymous: TYPEDESC_0,
pub vt: VARENUM,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union TYPEDESC_0 {
pub lptdesc: *mut TYPEDESC,
pub lpadesc: *mut ARRAYDESC,
pub hreftype: u32,
}
pub type TYPEKIND = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VARDESC {
pub memid: i32,
pub lpstrSchema: PWSTR,
pub Anonymous: VARDESC_0,
pub elemdescVar: ELEMDESC,
pub wVarFlags: VARFLAGS,
pub varkind: VARKIND,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union VARDESC_0 {
pub oInst: u32,
pub lpvarValue: *mut VARIANT,
}
pub type VARENUM = u16;
pub type VARFLAGS = u16;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VARIANT {
pub Anonymous: VARIANT_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union VARIANT_0 {
pub Anonymous: VARIANT_0_0,
pub decVal: DECIMAL,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VARIANT_0_0 {
pub vt: VARENUM,
pub wReserved1: u16,
pub wReserved2: u16,
pub wReserved3: u16,
pub Anonymous: VARIANT_0_0_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union VARIANT_0_0_0 {
pub llVal: i64,
pub lVal: i32,
pub bVal: u8,
pub iVal: i16,
pub fltVal: f32,
pub dblVal: f64,
pub boolVal: VARIANT_BOOL,
pub __OBSOLETE__VARIANT_BOOL: VARIANT_BOOL,
pub scode: i32,
pub cyVal: CY,
pub date: f64,
pub bstrVal: BSTR,
pub punkVal: *mut core::ffi::c_void,
pub pdispVal: *mut core::ffi::c_void,
pub parray: *mut SAFEARRAY,
pub pbVal: *mut u8,
pub piVal: *mut i16,
pub plVal: *mut i32,
pub pllVal: *mut i64,
pub pfltVal: *mut f32,
pub pdblVal: *mut f64,
pub pboolVal: *mut VARIANT_BOOL,
pub __OBSOLETE__VARIANT_PBOOL: *mut VARIANT_BOOL,
pub pscode: *mut i32,
pub pcyVal: *mut CY,
pub pdate: *mut f64,
pub pbstrVal: *mut BSTR,
pub ppunkVal: *mut *mut core::ffi::c_void,
pub ppdispVal: *mut *mut core::ffi::c_void,
pub pparray: *mut *mut SAFEARRAY,
pub pvarVal: *mut VARIANT,
pub byref: *mut core::ffi::c_void,
pub cVal: i8,
pub uiVal: u16,
pub ulVal: u32,
pub ullVal: u64,
pub intVal: i32,
pub uintVal: u32,
pub pdecVal: *mut DECIMAL,
pub pcVal: PSTR,
pub puiVal: *mut u16,
pub pulVal: *mut u32,
pub pullVal: *mut u64,
pub pintVal: *mut i32,
pub puintVal: *mut u32,
pub Anonymous: VARIANT_0_0_0_0,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VARIANT_0_0_0_0 {
pub pvRecord: *mut core::ffi::c_void,
pub pRecInfo: *mut core::ffi::c_void,
}
pub type VARIANT_BOOL = i16;
pub type VARKIND = i32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VERSIONEDSTREAM {
pub guidVersion: GUID,
pub pStream: *mut core::ffi::c_void,
}
pub const VT_BOOL: VARENUM = 11u16;
pub const VT_BSTR: VARENUM = 8u16;
pub const VT_EMPTY: VARENUM = 0u16;
pub const VT_I1: VARENUM = 16u16;
pub const VT_I2: VARENUM = 2u16;
pub const VT_I4: VARENUM = 3u16;
pub const VT_I8: VARENUM = 20u16;
pub const VT_R4: VARENUM = 4u16;
pub const VT_R8: VARENUM = 5u16;
pub const VT_UI1: VARENUM = 17u16;
pub const VT_UI2: VARENUM = 18u16;
pub const VT_UI4: VARENUM = 19u16;
pub const VT_UI8: VARENUM = 21u16;
pub const VT_UNKNOWN: VARENUM = 13u16;
pub type WAIT_EVENT = u32;
pub type WIN32_ERROR = u32;

View File

@@ -0,0 +1,5 @@
pub trait CanInto<T>: Sized {
const QUERY: bool = false;
}
impl<T> CanInto<T> for T where T: Clone {}

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
use super::*;
/// Attempts to load a function from a given library.
///
/// This is a small wrapper around `LoadLibrary` and `GetProcAddress`.
///
/// # Safety
///
/// * Both the library and function names must be valid null-terminated strings.
pub unsafe fn delay_load<T>(library: crate::PCSTR, function: crate::PCSTR) -> Option<T> {
let library = LoadLibraryExA(library.0, 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
if library == 0 {
return None;
}
let address = GetProcAddress(library, function.0);
if address.is_some() {
return Some(std::mem::transmute_copy(&address));
}
FreeLibrary(library);
None
}

View File

@@ -1,22 +1,34 @@
use super::*;
use crate::ComInterface;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicPtr, Ordering};
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};
#[doc(hidden)]
pub struct FactoryCache<C, I> {
shared: AtomicPtr<std::ffi::c_void>,
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(std::ptr::null_mut()), _c: PhantomData, _i: PhantomData }
Self {
shared: AtomicPtr::new(null_mut()),
_c: PhantomData,
_i: PhantomData,
}
}
}
impl<C: crate::RuntimeName, I: crate::ComInterface> FactoryCache<C, I> {
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.
@@ -24,7 +36,7 @@ impl<C: crate::RuntimeName, I: crate::ComInterface> FactoryCache<C, I> {
// If a pointer is found, the cache is primed and we're good to go.
if !ptr.is_null() {
return callback(unsafe { std::mem::transmute(&ptr) });
return callback(unsafe { transmute::<&*mut c_void, &I>(&ptr) });
}
// Otherwise, we load the factory the usual way.
@@ -32,8 +44,17 @@ impl<C: crate::RuntimeName, I: crate::ComInterface> FactoryCache<C, I> {
// If the factory is agile, we can safely cache it.
if factory.cast::<IAgileObject>().is_ok() {
if self.shared.compare_exchange_weak(std::ptr::null_mut(), factory.as_raw(), Ordering::Relaxed, Ordering::Relaxed).is_ok() {
std::mem::forget(factory);
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
@@ -45,39 +66,40 @@ impl<C: crate::RuntimeName, I: crate::ComInterface> FactoryCache<C, I> {
}
// This is safe because `FactoryCache` only holds agile factory pointers, which are safe to cache and share between threads.
unsafe impl<C, I> std::marker::Sync for FactoryCache<C, I> {}
unsafe impl<C, I> Sync for FactoryCache<C, I> {}
/// Attempts to load the factory object for the given WinRT class.
/// This can be used to access COM interfaces implemented on a Windows Runtime class factory.
pub fn factory<C: crate::RuntimeName, I: crate::ComInterface>() -> crate::Result<I> {
pub fn factory<C: crate::RuntimeName, I: Interface>() -> crate::Result<I> {
let mut factory: Option<I> = None;
let name = crate::HSTRING::from(C::NAME);
let code = if let Some(function) = unsafe { delay_load::<RoGetActivationFactory>(crate::s!("combase.dll"), crate::s!("RoGetActivationFactory")) } {
unsafe {
let mut code = function(std::mem::transmute_copy(&name), &I::IID, &mut factory as *mut _ as *mut _);
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 {
if let Some(mta) = delay_load::<CoIncrementMTAUsage>(crate::s!("ole32.dll"), crate::s!("CoIncrementMTAUsage")) {
let mut cookie = std::ptr::null_mut();
let _ = mta(&mut cookie);
}
// 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 = function(std::mem::transmute_copy(&name), &I::IID, &mut factory as *mut _ as *mut _);
}
code
// Now try a second time to get the activation factory via the OS.
code = get_com_factory();
}
} else {
CLASS_E_CLASSNOTAVAILABLE
code
};
// If this succeeded then return the resulting factory interface.
if code.is_ok() {
return code.and_some(factory);
if let Some(factory) = factory {
return Ok(factory);
}
// If not, first capture the error information from the failure above so that we
@@ -85,7 +107,9 @@ pub fn factory<C: crate::RuntimeName, I: crate::ComInterface>() -> crate::Result
let original: crate::Error = code.into();
// Now attempt to find the factory's implementation heuristically.
if let Some(i) = search_path(C::NAME, |library| unsafe { get_activation_factory(library, &name) }) {
if let Some(i) = search_path(C::NAME, |library| unsafe {
get_activation_factory(library, &name)
}) {
i.cast()
} else {
Err(original)
@@ -103,7 +127,7 @@ where
F: FnMut(crate::PCSTR) -> crate::Result<R>,
{
let suffix = b".dll\0";
let mut library = vec![0; path.len() + suffix.len()];
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());
@@ -118,15 +142,40 @@ where
None
}
unsafe fn get_activation_factory(library: crate::PCSTR, name: &crate::HSTRING) -> crate::Result<IGenericFactory> {
let function = delay_load::<DllGetActivationFactory>(library, crate::s!("DllGetActivationFactory")).ok_or_else(crate::Error::from_win32)?;
let mut abi = std::ptr::null_mut();
function(std::mem::transmute_copy(name), &mut abi).from_abi(abi)
unsafe fn get_activation_factory(
library: crate::PCSTR,
name: &crate::HSTRING,
) -> crate::Result<IGenericFactory> {
let function =
delay_load::<DllGetActivationFactory>(library, crate::s!("DllGetActivationFactory"))
.ok_or_else(crate::Error::from_win32)?;
let mut abi = null_mut();
function(transmute_copy(name), &mut abi).and_then(|| crate::Type::from_abi(abi))
}
type CoIncrementMTAUsage = extern "system" fn(cookie: *mut *mut std::ffi::c_void) -> crate::HRESULT;
type RoGetActivationFactory = extern "system" fn(hstring: *mut std::ffi::c_void, interface: &crate::GUID, result: *mut *mut std::ffi::c_void) -> crate::HRESULT;
type DllGetActivationFactory = extern "system" fn(name: *mut std::ffi::c_void, factory: *mut *mut std::ffi::c_void) -> crate::HRESULT;
unsafe fn delay_load<T>(library: crate::PCSTR, function: crate::PCSTR) -> Option<T> {
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 {
@@ -143,7 +192,7 @@ mod tests {
if unsafe { library.as_bytes() } == &b"A.dll"[..] {
Ok(42)
} else {
Err(crate::Error::OK)
Err(crate::Error::empty())
}
});
assert!(matches!(end_result, Some(42)));
@@ -153,7 +202,7 @@ mod tests {
let mut results = Vec::new();
let end_result = search_path(path, |library| {
results.push(unsafe { library.to_string().unwrap() });
crate::Result::<()>::Err(crate::Error::OK)
crate::Result::<()>::Err(crate::Error::empty())
});
assert!(end_result.is_none());
assert_eq!(results, vec!["A.B.dll", "A.dll"]);

View File

@@ -1,16 +1,26 @@
use crate::ComInterface;
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.
#[repr(transparent)]
#[derive(Clone, PartialEq, Eq)]
pub struct IGenericFactory(crate::IUnknown);
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: crate::ComInterface>(&self) -> crate::Result<I> {
pub fn ActivateInstance<I: Interface>(&self) -> crate::Result<I> {
unsafe {
let mut result__ = std::mem::zeroed();
(crate::Interface::vtable(self).ActivateInstance)(std::mem::transmute_copy(self), &mut result__ as *mut _ as *mut _).from_abi::<crate::IInspectable>(result__)?.cast()
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())
}
}
}
@@ -18,13 +28,6 @@ impl IGenericFactory {
#[repr(C)]
pub struct IGenericFactory_Vtbl {
pub base__: crate::IInspectable_Vtbl,
pub ActivateInstance: unsafe extern "system" fn(this: *mut std::ffi::c_void, instance: *mut *mut std::ffi::c_void) -> crate::HRESULT,
}
unsafe impl crate::Interface for IGenericFactory {
type Vtable = IGenericFactory_Vtbl;
}
unsafe impl crate::ComInterface for IGenericFactory {
const IID: crate::GUID = crate::GUID::from_u128(0x00000035_0000_0000_c000_000000000046);
pub ActivateInstance:
unsafe extern "system" fn(this: *mut c_void, instance: *mut *mut c_void) -> crate::HRESULT,
}

View File

@@ -1,35 +0,0 @@
use super::*;
/// Allocate memory of size `bytes` using `HeapAlloc`.
///
/// The memory allocated by this function is uninitialized.
///
/// This function will fail in OOM situations, if the heap is otherwise corrupt,
/// or if getting a handle to the process heap fails.
pub fn heap_alloc(bytes: usize) -> crate::Result<*mut std::ffi::c_void> {
let ptr = unsafe { HeapAlloc(GetProcessHeap(), 0, bytes) };
if ptr.is_null() {
Err(E_OUTOFMEMORY.into())
} else {
// HeapAlloc is not guaranteed to return zero memory but usually does. This just ensures that
// it predictably returns non-zero memory for testing purposes. This is similar to what MSVC's
// debug allocator does for the same reason.
#[cfg(debug_assertions)]
unsafe {
std::ptr::write_bytes(ptr, 0xCC, bytes);
}
Ok(ptr)
}
}
/// Free memory allocated by `HeapAlloc` or `HeapReAlloc`.
///
/// The pointer is allowed to be null.
///
/// # Safety
///
/// `ptr` must be a valid pointer to memory allocated by `HeapAlloc` or `HeapReAlloc`
pub unsafe fn heap_free(ptr: *mut std::ffi::c_void) {
HeapFree(GetProcessHeap(), 0, ptr);
}

View File

@@ -1,51 +1,35 @@
mod bindings;
#[cfg(windows)]
include!("windows.rs");
mod can_into;
mod com_bindings;
mod delay_load;
mod factory_cache;
mod generic_factory;
mod heap;
mod ref_count;
mod sha1;
mod waiter;
mod weak_ref_count;
pub use bindings::*;
pub use can_into::*;
pub use com_bindings::*;
pub use delay_load::*;
pub use factory_cache::*;
pub use generic_factory::*;
pub use heap::*;
pub use ref_count::*;
pub use sha1::*;
pub use waiter::*;
pub use weak_ref_count::*;
// This is a workaround since 1.56 does not include `bool::then_some`.
pub fn then_some<T>(value: bool, t: T) -> Option<T> {
if value {
Some(t)
} else {
None
}
}
pub fn wide_trim_end(mut wide: &[u16]) -> &[u16] {
while let Some(last) = wide.last() {
match last {
32 | 9..=13 => wide = &wide[..wide.len() - 1],
_ => break,
}
}
wide
}
#[doc(hidden)]
#[macro_export]
macro_rules! interface_hierarchy {
($child:ty, $parent:ty) => {
impl ::windows_core::CanInto<$parent> for $child {}
($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:ty, $first:ty, $($rest:ty),+) => {
($child:ident, $first:ty, $($rest:ty),+) => {
$crate::imp::interface_hierarchy!($child, $first);
$crate::imp::interface_hierarchy!($child, $($rest),+);
};
@@ -53,3 +37,60 @@ macro_rules! interface_hierarchy {
#[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(::std::ptr::NonNull<::std::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;

View File

@@ -1,6 +1,5 @@
use std::sync::atomic::{fence, AtomicI32, Ordering};
use core::sync::atomic::{fence, AtomicI32, Ordering};
#[doc(hidden)]
#[repr(transparent)]
#[derive(Default)]
pub struct RefCount(pub(crate) AtomicI32);
@@ -24,9 +23,9 @@ impl RefCount {
let remaining = self.0.fetch_sub(1, Ordering::Release) - 1;
match remaining.cmp(&0) {
std::cmp::Ordering::Equal => fence(Ordering::Acquire),
std::cmp::Ordering::Less => panic!("Object has been over-released."),
std::cmp::Ordering::Greater => {}
core::cmp::Ordering::Equal => fence(Ordering::Acquire),
core::cmp::Ordering::Less => panic!("Object has been over-released."),
core::cmp::Ordering::Greater => {}
}
remaining as u32

View File

@@ -3,7 +3,10 @@
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 = Blocks {
len: 0,
data: [0; 64],
};
let (blocks, len, state) = process_blocks(blocks, data, len, state);
digest(state, len, blocks)
}
@@ -16,13 +19,29 @@ pub struct ConstBuffer {
}
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 }
Self {
data: [0; BUFFER_SIZE],
head: 0,
}
}
pub const fn push_slice(self, slice: &[u8]) -> Self {
@@ -37,12 +56,42 @@ impl ConstBuffer {
self.head
}
const fn as_slice(&self) -> &[u8] {
&self.data
pub fn as_slice(&self) -> &[u8] {
&self.data[..self.head]
}
pub const fn push_other(self, other: Self) -> Self {
self.push_amount(other.as_slice(), other.len())
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 {
@@ -54,6 +103,22 @@ impl ConstBuffer {
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 {
@@ -61,20 +126,33 @@ struct Blocks {
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 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);
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] {
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];
@@ -92,7 +170,7 @@ const fn process_blocks(mut blocks: Blocks, data: &ConstBuffer, mut len: u64, mu
i += 64;
} else {
let num_elems = data.len() - i;
blocks.data = clone_from_slice_64(blocks.data, data.as_slice(), i, num_elems);
blocks.data = clone_from_slice_64(blocks.data, &data.data, i, num_elems);
blocks.len = num_elems as u32;
break;
}
@@ -196,7 +274,12 @@ const fn process_state(mut state: [u32; 5], block: [u32; 16]) -> [u32; 5] {
}
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] {
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];
@@ -220,14 +303,26 @@ const fn digest(mut state: [u32; 5], len: u64, blocks: Blocks) -> Digest {
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);
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 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);
@@ -253,40 +348,95 @@ const fn blk(block: &[u32], i: usize) -> u32 {
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));
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) {
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));
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) {
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));
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) {
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));
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) {
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));
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)
@@ -323,8 +473,8 @@ impl Digest {
}
}
impl std::fmt::Display for Digest {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
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, "{:08x}", i)?;
}

View File

@@ -1,14 +1,15 @@
use super::*;
#[doc(hidden)]
pub struct Waiter(isize);
pub struct WaiterSignaler(isize);
pub struct Waiter(HANDLE);
pub struct WaiterSignaler(HANDLE);
unsafe impl Send for WaiterSignaler {}
impl Waiter {
pub fn new() -> crate::Result<(Waiter, WaiterSignaler)> {
unsafe {
let handle = CreateEventW(std::ptr::null(), 1, 0, std::ptr::null());
if handle == 0 {
let handle = CreateEventW(core::ptr::null(), 1, 0, core::ptr::null());
if handle.is_null() {
Err(crate::Error::from_win32())
} else {
Ok((Waiter(handle), WaiterSignaler(handle)))

View File

@@ -1,8 +1,10 @@
use super::*;
use crate::ComInterface;
use std::sync::atomic::{AtomicIsize, Ordering};
use crate::Interface;
use core::ffi::c_void;
use core::mem::{transmute, transmute_copy};
use core::ptr::null_mut;
use core::sync::atomic::{AtomicIsize, Ordering};
#[doc(hidden)]
#[repr(transparent)]
#[derive(Default)]
pub struct WeakRefCount(AtomicIsize);
@@ -13,28 +15,43 @@ impl WeakRefCount {
}
pub fn add_ref(&self) -> u32 {
self.0.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |count_or_pointer| 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() })
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| 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();
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 _);
}
// 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
})
remaining
})
}
/// # Safety
pub unsafe fn query(&self, iid: *const crate::GUID, object: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
if *iid != IWeakReferenceSource::IID {
return std::ptr::null_mut();
pub unsafe fn query(&self, iid: &crate::GUID, object: *mut c_void) -> *mut c_void {
if iid != &IWeakReferenceSource::IID {
return null_mut();
}
let mut count_or_pointer = self.0.load(Ordering::Relaxed);
@@ -44,13 +61,18 @@ impl WeakRefCount {
}
let tear_off = TearOff::new(object, count_or_pointer as u32);
let tear_off_ptr: *mut std::ffi::c_void = std::mem::transmute_copy(&tear_off);
let encoding: usize = ((tear_off_ptr as usize) >> 1) | (1 << (std::mem::size_of::<usize>() * 8 - 1));
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) {
match self.0.compare_exchange_weak(
count_or_pointer,
encoding as isize,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => {
let result: *mut std::ffi::c_void = std::mem::transmute(tear_off);
let result: *mut c_void = transmute(tear_off);
TearOff::from_strong_ptr(result).strong_count.add_ref();
return result;
}
@@ -61,7 +83,10 @@ impl WeakRefCount {
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);
TearOff::from_strong_ptr(tear_off_ptr)
.strong_count
.0
.store(count_or_pointer as i32, Ordering::SeqCst);
}
}
}
@@ -74,15 +99,15 @@ fn is_weak_ref(value: isize) -> bool {
struct TearOff {
strong_vtable: *const IWeakReferenceSource_Vtbl,
weak_vtable: *const IWeakReference_Vtbl,
object: *mut std::ffi::c_void,
object: *mut c_void,
strong_count: RefCount,
weak_count: RefCount,
}
impl TearOff {
#[allow(clippy::new_ret_no_self)]
unsafe fn new(object: *mut std::ffi::c_void, strong_count: u32) -> IWeakReferenceSource {
std::mem::transmute(std::boxed::Box::new(TearOff {
unsafe fn new(object: *mut c_void, strong_count: u32) -> IWeakReferenceSource {
transmute(Box::new(TearOff {
strong_vtable: &Self::STRONG_VTABLE,
weak_vtable: &Self::WEAK_VTABLE,
object,
@@ -91,43 +116,63 @@ impl TearOff {
}))
}
unsafe fn from_encoding(encoding: isize) -> *mut std::ffi::c_void {
unsafe fn from_encoding(encoding: isize) -> *mut c_void {
let tear_off = TearOff::decode(encoding);
tear_off.strong_count.add_ref();
tear_off as *mut _ as *mut _
}
const STRONG_VTABLE: IWeakReferenceSource_Vtbl = IWeakReferenceSource_Vtbl {
base__: crate::IUnknown_Vtbl { QueryInterface: Self::StrongQueryInterface, AddRef: Self::StrongAddRef, Release: Self::StrongRelease },
base__: crate::IUnknown_Vtbl {
QueryInterface: Self::StrongQueryInterface,
AddRef: Self::StrongAddRef,
Release: Self::StrongRelease,
},
GetWeakReference: Self::StrongDowngrade,
};
const WEAK_VTABLE: IWeakReference_Vtbl = IWeakReference_Vtbl {
base__: crate::IUnknown_Vtbl { QueryInterface: Self::WeakQueryInterface, AddRef: Self::WeakAddRef, Release: Self::WeakRelease },
base__: crate::IUnknown_Vtbl {
QueryInterface: Self::WeakQueryInterface,
AddRef: Self::WeakAddRef,
Release: Self::WeakRelease,
},
Resolve: Self::WeakUpgrade,
};
unsafe fn from_strong_ptr<'a>(this: *mut std::ffi::c_void) -> &'a mut Self {
&mut *(this as *mut *mut std::ffi::c_void as *mut Self)
unsafe fn from_strong_ptr<'a>(this: *mut c_void) -> &'a mut Self {
&mut *(this as *mut *mut c_void as *mut Self)
}
unsafe fn from_weak_ptr<'a>(this: *mut std::ffi::c_void) -> &'a mut Self {
&mut *((this as *mut *mut std::ffi::c_void).sub(1) as *mut Self)
unsafe fn from_weak_ptr<'a>(this: *mut c_void) -> &'a mut Self {
&mut *((this as *mut *mut c_void).sub(1) as *mut Self)
}
unsafe fn decode<'a>(value: isize) -> &'a mut Self {
std::mem::transmute(value << 1)
transmute(value << 1)
}
unsafe fn query_interface(&self, iid: *const crate::GUID, interface: *mut *mut std::ffi::c_void) -> crate::HRESULT {
((*(*(self.object as *mut *mut crate::IUnknown_Vtbl))).QueryInterface)(self.object, iid, interface)
unsafe fn query_interface(
&self,
iid: *const crate::GUID,
interface: *mut *mut c_void,
) -> crate::HRESULT {
((*(*(self.object as *mut *mut crate::IUnknown_Vtbl))).QueryInterface)(
self.object,
iid,
interface,
)
}
unsafe extern "system" fn StrongQueryInterface(ptr: *mut std::ffi::c_void, iid: *const crate::GUID, interface: *mut *mut std::ffi::c_void) -> crate::HRESULT {
unsafe extern "system" fn StrongQueryInterface(
ptr: *mut c_void,
iid: *const crate::GUID,
interface: *mut *mut c_void,
) -> crate::HRESULT {
let this = Self::from_strong_ptr(ptr);
if iid.is_null() || interface.is_null() {
return ::windows_core::HRESULT(-2147467261); // E_POINTER
return E_POINTER;
}
// Only directly respond to queries for the the tear-off's strong interface. This is
@@ -143,18 +188,29 @@ impl TearOff {
this.query_interface(iid, interface)
}
unsafe extern "system" fn WeakQueryInterface(ptr: *mut std::ffi::c_void, iid: *const crate::GUID, interface: *mut *mut std::ffi::c_void) -> crate::HRESULT {
unsafe extern "system" fn WeakQueryInterface(
ptr: *mut c_void,
iid: *const crate::GUID,
interface: *mut *mut c_void,
) -> crate::HRESULT {
let this = Self::from_weak_ptr(ptr);
if iid.is_null() || interface.is_null() {
return ::windows_core::HRESULT(-2147467261); // E_POINTER
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 == crate::IUnknown::IID || *iid == IAgileObject::IID { ptr } else { std::ptr::null_mut() };
*interface = if *iid == IWeakReference::IID
|| *iid == crate::IUnknown::IID
|| *iid == IAgileObject::IID
{
ptr
} else {
null_mut()
};
// TODO: implement IMarshal
@@ -166,21 +222,21 @@ impl TearOff {
}
}
unsafe extern "system" fn StrongAddRef(ptr: *mut std::ffi::c_void) -> u32 {
unsafe extern "system" fn StrongAddRef(ptr: *mut c_void) -> u32 {
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 std::ffi::c_void) -> u32 {
unsafe extern "system" fn WeakAddRef(ptr: *mut c_void) -> u32 {
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 std::ffi::c_void) -> u32 {
unsafe extern "system" fn StrongRelease(ptr: *mut c_void) -> u32 {
let this = Self::from_strong_ptr(ptr);
// Forward strong `Release` to the object so that it can destroy itself. It will then
@@ -188,7 +244,7 @@ impl TearOff {
((*(*(this.object as *mut *mut crate::IUnknown_Vtbl))).Release)(this.object)
}
unsafe extern "system" fn WeakRelease(ptr: *mut std::ffi::c_void) -> u32 {
unsafe extern "system" fn WeakRelease(ptr: *mut c_void) -> u32 {
let this = Self::from_weak_ptr(ptr);
// Implement `Release` directly as we own the weak reference.
@@ -197,13 +253,16 @@ impl TearOff {
// 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 _ = std::boxed::Box::from_raw(this);
let _ = Box::from_raw(this);
}
remaining
}
unsafe extern "system" fn StrongDowngrade(ptr: *mut std::ffi::c_void, interface: *mut *mut std::ffi::c_void) -> crate::HRESULT {
unsafe extern "system" fn StrongDowngrade(
ptr: *mut c_void,
interface: *mut *mut c_void,
) -> crate::HRESULT {
let this = Self::from_strong_ptr(ptr);
// The strong vtable hands out a reference to the weak vtable. This is always safe and
@@ -214,7 +273,11 @@ impl TearOff {
crate::HRESULT(0)
}
unsafe extern "system" fn WeakUpgrade(ptr: *mut std::ffi::c_void, iid: *const crate::GUID, interface: *mut *mut std::ffi::c_void) -> crate::HRESULT {
unsafe extern "system" fn WeakUpgrade(
ptr: *mut c_void,
iid: *const crate::GUID,
interface: *mut *mut c_void,
) -> crate::HRESULT {
let this = Self::from_weak_ptr(ptr);
this.strong_count
@@ -222,7 +285,7 @@ impl TearOff {
.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.
then_some(count != 0, count + 1)
bool::then_some(count != 0, count + 1)
})
.map(|_| {
// Let the object respond to the upgrade query.
@@ -233,7 +296,7 @@ impl TearOff {
result
})
.unwrap_or_else(|_| {
*interface = std::ptr::null_mut();
*interface = null_mut();
crate::HRESULT(0)
})
}

View File

@@ -0,0 +1,11 @@
mod factory_cache;
pub use factory_cache::*;
mod generic_factory;
pub use generic_factory::*;
mod waiter;
pub use waiter::*;
mod bindings;
pub use bindings::*;

View File

@@ -1,20 +1,34 @@
use super::*;
use core::ffi::c_void;
use core::ptr::null_mut;
/// 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)]
#[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 = std::ptr::null_mut();
(self.vtable().GetRuntimeClassName)(std::mem::transmute_copy(self), &mut abi).ok()?;
Ok(std::mem::transmute(abi))
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)
}
}
}
@@ -23,126 +37,79 @@ impl IInspectable {
#[repr(C)]
pub struct IInspectable_Vtbl {
pub base: IUnknown_Vtbl,
pub GetIids: unsafe extern "system" fn(this: *mut std::ffi::c_void, count: *mut u32, values: *mut *mut GUID) -> HRESULT,
pub GetRuntimeClassName: unsafe extern "system" fn(this: *mut std::ffi::c_void, value: *mut *mut std::ffi::c_void) -> HRESULT,
pub GetTrustLevel: unsafe extern "system" fn(this: *mut std::ffi::c_void, value: *mut i32) -> HRESULT,
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;
}
unsafe impl ComInterface for IInspectable {
const IID: GUID = GUID::from_u128(0xaf86e2e0_b12d_4c6a_9c5a_d7aa65101e90);
}
impl CanInto<IUnknown> for IInspectable {}
impl RuntimeType for IInspectable {
const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"cinterface(IInspectable)");
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"cinterface(IInspectable)");
}
impl RuntimeName for IInspectable {}
#[cfg(feature = "implement")]
impl IInspectable_Vtbl {
pub const fn new<Identity: IUnknownImpl, Name: RuntimeName, const OFFSET: isize>() -> Self {
unsafe extern "system" fn GetIids(_: *mut std::ffi::c_void, count: *mut u32, values: *mut *mut GUID) -> HRESULT {
unsafe extern "system" fn GetIids(
_: *mut c_void,
count: *mut u32,
values: *mut *mut GUID,
) -> HRESULT {
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 = std::ptr::null_mut();
*values = null_mut();
HRESULT(0)
}
unsafe extern "system" fn GetRuntimeClassName<T: RuntimeName>(_: *mut std::ffi::c_void, value: *mut *mut std::ffi::c_void) -> HRESULT {
let h: HSTRING = T::NAME.into(); // TODO: should be try_into
*value = std::mem::transmute(h);
HRESULT(0)
}
unsafe extern "system" fn GetTrustLevel(_: *mut std::ffi::c_void, value: *mut i32) -> HRESULT {
// 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.
*value = 0;
HRESULT(0)
}
Self { base: IUnknown_Vtbl::new::<Identity, OFFSET>(), GetIids, GetRuntimeClassName: GetRuntimeClassName::<Name>, GetTrustLevel }
}
}
unsafe extern "system" fn GetRuntimeClassName<T: RuntimeName>(
_: *mut c_void,
value: *mut *mut c_void,
) -> HRESULT {
if value.is_null() {
return imp::E_POINTER;
}
impl std::fmt::Debug for IInspectable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Attempts to retrieve the string representation of the object via the
// IStringable interface. If that fails, it will use the canonical type
// name to give some idea of what the object represents.
let name = <Self as ComInterface>::cast::<imp::IStringable>(self).and_then(|s| s.ToString()).or_else(|_| self.GetRuntimeClassName()).unwrap_or_default();
write!(f, "\"{}\"", name)
}
}
#[cfg(windows)]
{
*value = core::mem::transmute::<HSTRING, *mut c_void>(T::NAME.into());
}
macro_rules! primitive_boxed_type {
($(($t:ty, $m:ident)),+) => {
$(impl std::convert::TryFrom<$t> for IInspectable {
type Error = Error;
fn try_from(value: $t) -> Result<Self> {
imp::PropertyValue::$m(value)
#[cfg(not(windows))]
{
*value = core::ptr::null_mut();
}
HRESULT(0)
}
impl std::convert::TryFrom<IInspectable> for $t {
type Error = Error;
fn try_from(value: IInspectable) -> Result<Self> {
<IInspectable as ComInterface>::cast::<imp::IReference<$t>>(&value)?.Value()
unsafe extern "system" fn GetTrustLevel<T: IUnknownImpl, const OFFSET: isize>(
this: *mut c_void,
value: *mut i32,
) -> HRESULT {
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>,
}
impl std::convert::TryFrom<&IInspectable> for $t {
type Error = Error;
fn try_from(value: &IInspectable) -> Result<Self> {
<IInspectable as ComInterface>::cast::<imp::IReference<$t>>(value)?.Value()
}
})*
};
}
primitive_boxed_type! {
(bool, CreateBoolean),
(u8, CreateUInt8),
(i16, CreateInt16),
(u16, CreateUInt16),
(i32, CreateInt32),
(u32, CreateUInt32),
(i64, CreateInt64),
(u64, CreateUInt64),
(f32, CreateSingle),
(f64, CreateDouble)
}
impl std::convert::TryFrom<&str> for IInspectable {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
let value: HSTRING = value.into();
imp::PropertyValue::CreateString(&value)
}
}
impl std::convert::TryFrom<HSTRING> for IInspectable {
type Error = Error;
fn try_from(value: HSTRING) -> Result<Self> {
imp::PropertyValue::CreateString(&value)
}
}
impl std::convert::TryFrom<&HSTRING> for IInspectable {
type Error = Error;
fn try_from(value: &HSTRING) -> Result<Self> {
imp::PropertyValue::CreateString(value)
}
}
impl std::convert::TryFrom<IInspectable> for HSTRING {
type Error = Error;
fn try_from(value: IInspectable) -> Result<Self> {
<IInspectable as ComInterface>::cast::<imp::IReference<HSTRING>>(&value)?.Value()
}
}
impl std::convert::TryFrom<&IInspectable> for HSTRING {
type Error = Error;
fn try_from(value: &IInspectable) -> Result<Self> {
<IInspectable as ComInterface>::cast::<imp::IReference<HSTRING>>(value)?.Value()
}
}

View File

@@ -1,14 +1,29 @@
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 {
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>() }
@@ -21,22 +36,24 @@ pub unsafe trait Interface: Sized {
/// 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 {
&**(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 std::ffi::c_void {
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 { std::mem::transmute_copy(self) }
unsafe { transmute_copy(self) }
}
/// Returns the raw COM interface pointer and releases ownership. It the caller's responsibility to release the COM interface pointer.
fn into_raw(self) -> *mut std::ffi::c_void {
#[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();
std::mem::forget(self);
forget(self);
raw
}
@@ -46,8 +63,8 @@ pub unsafe trait Interface: Sized {
///
/// 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 std::ffi::c_void) -> Self {
std::mem::transmute_copy(&raw)
unsafe fn from_raw(raw: *mut c_void) -> Self {
transmute_copy(&raw)
}
/// Creates an `Interface` that is valid so long as the `raw` COM interface pointer is valid.
@@ -56,17 +73,269 @@ pub unsafe trait Interface: Sized {
///
/// 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`.
unsafe fn from_raw_borrowed(raw: &*mut std::ffi::c_void) -> Option<&Self> {
#[inline(always)]
unsafe fn from_raw_borrowed(raw: &*mut c_void) -> Option<&Self> {
if raw.is_null() {
None
} else {
Some(std::mem::transmute_copy(&raw))
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>()
.and_then(|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 {
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)
}
}
/// # Safety
#[doc(hidden)]
pub unsafe fn from_raw_borrowed<T: Interface>(raw: &*mut std::ffi::c_void) -> Option<&T> {
pub unsafe fn from_raw_borrowed<T: Interface>(raw: &*mut c_void) -> Option<&T> {
T::from_raw_borrowed(raw)
}
/// 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<'a, I> Copy for InterfaceRef<'a, I> {}
impl<'a, I> Clone for InterfaceRef<'a, I> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, I: core::fmt::Debug + Interface> core::fmt::Debug for InterfaceRef<'a, I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<I as core::fmt::Debug>::fmt(&**self, f)
}
}
impl<'a, I: Interface> InterfaceRef<'a, 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) -> InterfaceRef<'a, I> {
InterfaceRef::from_interface(interface)
}
}
impl<'a, I: Interface> core::ops::Deref for InterfaceRef<'a, 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);

View File

@@ -4,56 +4,57 @@ Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs
#![doc(html_no_source)]
#![allow(non_snake_case)]
#![cfg_attr(windows_debugger_visualizer, debugger_visualizer(natvis_file = "../windows.natvis"))]
#![cfg_attr(
windows_debugger_visualizer,
debugger_visualizer(natvis_file = "../.natvis")
)]
#![cfg_attr(all(not(feature = "std")), no_std)]
#[cfg(windows)]
include!("windows.rs");
extern crate self as windows_core;
extern crate alloc;
use alloc::boxed::Box;
#[doc(hidden)]
pub mod imp;
mod agile_reference;
mod array;
mod as_impl;
mod com_interface;
mod error;
mod event;
mod com_object;
mod guid;
mod hresult;
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 strings;
mod r#type;
mod unknown;
mod weak;
pub use agile_reference::*;
pub use array::*;
pub use as_impl::*;
pub use com_interface::*;
pub use error::*;
pub use event::*;
pub use com_object::*;
pub use guid::*;
pub use hresult::*;
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 strings::*;
pub use unknown::*;
pub use weak::*;
/// A specialized [`Result`] type that provides Windows error information.
pub type Result<T> = std::result::Result<T, Error>;
/// 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: ComInterface>() -> Result<I> {
crate::imp::factory::<C, I>()
}
pub use windows_implement::implement;
pub use windows_interface::interface;
pub use windows_result::*;

View File

@@ -0,0 +1,57 @@
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> {
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> {
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> {
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> {
match self {
Some(this) => transmute_copy(this),
None => zeroed(),
}
}
}

View File

@@ -0,0 +1,25 @@
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<'a, T: Type<T>> OutRef<'a, 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(())
}
}
}

View File

@@ -1,133 +1,63 @@
use super::*;
use core::mem::transmute_copy;
use core::mem::zeroed;
#[doc(hidden)]
pub enum Param<T: Type<T>> {
Owned(T),
Borrowed(T::Abi),
/// 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: Type<T>> Param<T> {
pub fn abi(&self) -> T::Abi {
unsafe {
match self {
Self::Owned(item) => std::mem::transmute_copy(item),
Self::Borrowed(borrowed) => std::mem::transmute_copy(borrowed),
}
}
impl<T> Param<T> for Option<&T>
where
T: Type<T>,
{
unsafe fn param(self) -> ParamValue<T> {
ParamValue::Borrowed(match self {
Some(item) => transmute_copy(item),
None => zeroed(),
})
}
}
#[doc(hidden)]
pub trait TryIntoParam<T: Type<T>> {
fn try_into_param(self) -> Result<Param<T>>;
}
impl<T> TryIntoParam<T> for Option<&T>
impl<T, U> Param<T, InterfaceType> for &U
where
T: ComInterface,
T: TypeKind<TypeKind = InterfaceType> + Clone,
T: Interface,
U: Interface,
U: imp::CanInto<T>,
{
fn try_into_param(self) -> Result<Param<T>> {
match self {
Some(from) => Ok(Param::Borrowed(from.abi())),
None => Ok(Param::Borrowed(unsafe { std::mem::zeroed() })),
}
}
}
impl<T, U> TryIntoParam<T> for &U
where
T: ComInterface,
U: ComInterface,
U: CanTryInto<T>,
{
fn try_into_param(self) -> Result<Param<T>> {
if U::CAN_INTO {
Ok(Param::Borrowed(self.abi()))
unsafe fn param(self) -> ParamValue<T> {
if U::QUERY {
self.cast()
.map_or(ParamValue::Borrowed(zeroed()), |ok| ParamValue::Owned(ok))
} else {
Ok(Param::Owned(self.cast()?))
ParamValue::Borrowed(transmute_copy(self))
}
}
}
#[doc(hidden)]
pub trait CanTryInto<T>: ComInterface
impl<T> Param<T, CloneType> for &T
where
T: ComInterface,
T: TypeKind<TypeKind = CloneType> + Clone,
{
const CAN_INTO: bool = false;
}
impl<T, U> CanTryInto<T> for U
where
T: ComInterface,
U: ComInterface,
U: CanInto<T>,
{
const CAN_INTO: bool = true;
}
#[doc(hidden)]
pub trait CanInto<T>: Sized
where
T: Clone,
{
fn can_into(&self) -> &T {
unsafe { std::mem::transmute(self) }
}
fn can_clone_into(&self) -> T {
self.can_into().clone()
}
}
impl<T> CanInto<T> for T where T: Clone {}
#[doc(hidden)]
pub trait IntoParam<T: TypeKind, C = <T as TypeKind>::TypeKind>: Sized
where
T: Type<T>,
{
fn into_param(self) -> Param<T>;
}
impl<T> IntoParam<T> for Option<&T>
where
T: Type<T>,
{
fn into_param(self) -> Param<T> {
match self {
Some(item) => Param::Borrowed(item.abi()),
None => Param::Borrowed(unsafe { std::mem::zeroed() }),
}
unsafe fn param(self) -> ParamValue<T> {
ParamValue::Borrowed(transmute_copy(self))
}
}
impl<T, U> IntoParam<T, ReferenceType> for &U
where
T: TypeKind<TypeKind = ReferenceType> + Clone,
U: TypeKind<TypeKind = ReferenceType> + Clone,
U: CanInto<T>,
{
fn into_param(self) -> Param<T> {
Param::Borrowed(self.abi())
}
}
impl<T> IntoParam<T, ValueType> for &T
where
T: TypeKind<TypeKind = ValueType> + Clone,
{
fn into_param(self) -> Param<T> {
Param::Borrowed(self.abi())
}
}
impl<T, U> IntoParam<T, CopyType> for U
impl<T, U> Param<T, CopyType> for U
where
T: TypeKind<TypeKind = CopyType> + Clone,
U: TypeKind<TypeKind = CopyType> + Clone,
U: CanInto<T>,
U: imp::CanInto<T>,
{
fn into_param(self) -> Param<T> {
unsafe { Param::Owned(std::mem::transmute_copy(&self)) }
unsafe fn param(self) -> ParamValue<T> {
ParamValue::Owned(transmute_copy(&self))
}
}

View File

@@ -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()) }
}
}

View File

@@ -0,0 +1,26 @@
use super::*;
use core::ffi::c_void;
use core::marker::PhantomData;
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, PhantomData<&'a T>);
impl<'a, T: Type<T, Default = Option<T>, Abi = *mut c_void>> Ref<'a, T> {
/// Converts the argument to a [Result<&T>] reference.
pub fn ok(&self) -> Result<&T> {
if self.0.is_null() {
Err(Error::from_hresult(imp::E_POINTER))
} else {
unsafe { Ok(transmute::<&*mut c_void, &T>(&self.0)) }
}
}
}
impl<'a, T: Type<T>> core::ops::Deref for Ref<'a, T> {
type Target = T::Default;
fn deref(&self) -> &Self::Target {
unsafe { transmute(&self.0) }
}
}

View File

@@ -2,14 +2,14 @@ use super::*;
#[doc(hidden)]
pub trait RuntimeType: Type<Self> {
const SIGNATURE: crate::imp::ConstBuffer;
const SIGNATURE: imp::ConstBuffer;
}
macro_rules! primitives {
($(($t:ty, $s:literal)),+) => {
$(
impl RuntimeType for $t {
const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice($s);
const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice($s);
}
)*
};

View File

@@ -1,25 +1,30 @@
use super::*;
use core::ffi::c_void;
use core::marker::PhantomData;
#[doc(hidden)]
#[repr(C)]
pub struct ScopedHeap {
pub vtable: *const std::ffi::c_void,
pub this: *const std::ffi::c_void,
pub vtable: *const c_void,
pub this: *const c_void,
}
#[doc(hidden)]
pub struct ScopedInterface<'a, T: Interface> {
interface: T,
lifetime: std::marker::PhantomData<&'a T>,
lifetime: PhantomData<&'a T>,
}
impl<'a, T: Interface> ScopedInterface<'a, T> {
pub fn new(interface: T) -> Self {
Self { interface, lifetime: std::marker::PhantomData }
Self {
interface,
lifetime: PhantomData,
}
}
}
impl<'a, T: Interface> std::ops::Deref for ScopedInterface<'a, T> {
impl<'a, T: Interface> core::ops::Deref for ScopedInterface<'a, T> {
type Target = T;
fn deref(&self) -> &T {
@@ -30,7 +35,7 @@ impl<'a, T: Interface> std::ops::Deref for ScopedInterface<'a, T> {
impl<'a, T: Interface> Drop for ScopedInterface<'a, T> {
fn drop(&mut self) {
unsafe {
let _ = std::boxed::Box::from_raw(self.interface.as_raw() as *const _ as *mut ScopedHeap);
let _ = Box::from_raw(self.interface.as_raw() as *const _ as *mut ScopedHeap);
}
}
}

View File

@@ -1,162 +0,0 @@
use super::*;
/// A BSTR string ([BSTR](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/string-manipulation-functions))
/// is a length-prefixed wide string.
#[repr(transparent)]
pub struct BSTR(*const u16);
impl BSTR {
/// Create an empty `BSTR`.
///
/// This function does not allocate memory.
pub const fn new() -> Self {
Self(std::ptr::null_mut())
}
/// Returns `true` if the string is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the length of the string.
pub fn len(&self) -> usize {
if self.0.is_null() {
0
} else {
unsafe { crate::imp::SysStringLen(self.0) as usize }
}
}
/// Get the string as 16-bit wide characters (wchars).
pub fn as_wide(&self) -> &[u16] {
if self.0.is_null() {
return &[];
}
unsafe { std::slice::from_raw_parts(self.0, self.len()) }
}
/// Create a `BSTR` from a slice of 16 bit characters (wchars).
pub fn from_wide(value: &[u16]) -> Result<Self> {
if value.is_empty() {
return Ok(Self::new());
}
let result = unsafe { Self(crate::imp::SysAllocStringLen(value.as_ptr(), value.len().try_into()?)) };
if result.is_empty() {
Err(crate::imp::E_OUTOFMEMORY.into())
} else {
Ok(result)
}
}
/// # Safety
#[doc(hidden)]
pub unsafe fn from_raw(raw: *const u16) -> Self {
Self(raw)
}
/// # Safety
#[doc(hidden)]
pub fn into_raw(self) -> *const u16 {
unsafe { std::mem::transmute(self) }
}
}
impl std::clone::Clone for BSTR {
fn clone(&self) -> Self {
Self::from_wide(self.as_wide()).unwrap()
}
}
impl std::convert::From<&str> for BSTR {
fn from(value: &str) -> Self {
let value: std::vec::Vec<u16> = value.encode_utf16().collect();
Self::from_wide(&value).unwrap()
}
}
impl std::convert::From<std::string::String> for BSTR {
fn from(value: std::string::String) -> Self {
value.as_str().into()
}
}
impl std::convert::From<&std::string::String> for BSTR {
fn from(value: &std::string::String) -> Self {
value.as_str().into()
}
}
impl<'a> std::convert::TryFrom<&'a BSTR> for std::string::String {
type Error = std::string::FromUtf16Error;
fn try_from(value: &BSTR) -> std::result::Result<Self, Self::Error> {
std::string::String::from_utf16(value.as_wide())
}
}
impl std::convert::TryFrom<BSTR> for std::string::String {
type Error = std::string::FromUtf16Error;
fn try_from(value: BSTR) -> std::result::Result<Self, Self::Error> {
std::string::String::try_from(&value)
}
}
impl std::default::Default for BSTR {
fn default() -> Self {
Self(std::ptr::null_mut())
}
}
impl std::fmt::Display for BSTR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "{}", crate::Decode(|| std::char::decode_utf16(self.as_wide().iter().cloned())))
}
}
impl std::fmt::Debug for BSTR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "{}", self)
}
}
impl std::cmp::PartialEq for BSTR {
fn eq(&self, other: &Self) -> bool {
self.as_wide() == other.as_wide()
}
}
impl std::cmp::Eq for BSTR {}
impl std::cmp::PartialEq<BSTR> for &str {
fn eq(&self, other: &BSTR) -> bool {
other == self
}
}
impl std::cmp::PartialEq<BSTR> for String {
fn eq(&self, other: &BSTR) -> bool {
other == self
}
}
impl<T: AsRef<str> + ?Sized> std::cmp::PartialEq<T> for BSTR {
fn eq(&self, other: &T) -> bool {
self.as_wide().iter().copied().eq(other.as_ref().encode_utf16())
}
}
impl std::ops::Drop for BSTR {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { crate::imp::SysFreeString(self.0) }
}
}
}
impl TypeKind for BSTR {
type TypeKind = ValueType;
}

View File

@@ -1,458 +0,0 @@
use super::*;
/// A WinRT string ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring))
/// is reference-counted and immutable.
#[repr(transparent)]
pub struct HSTRING(Option<std::ptr::NonNull<Header>>);
impl HSTRING {
/// Create an empty `HSTRING`.
///
/// This function does not allocate memory.
pub const fn new() -> Self {
Self(None)
}
/// Returns `true` if the string is empty.
pub const fn is_empty(&self) -> bool {
// An empty HSTRING is represented by a null pointer.
self.0.is_none()
}
/// Returns the length of the string.
pub fn len(&self) -> usize {
if let Some(header) = self.get_header() {
header.len as usize
} else {
0
}
}
/// Get the string as 16-bit wide characters (wchars).
pub fn as_wide(&self) -> &[u16] {
unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
/// Returns a raw pointer to the `HSTRING` buffer.
pub fn as_ptr(&self) -> *const u16 {
if let Some(header) = self.get_header() {
header.data
} else {
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
}
}
/// Create a `HSTRING` from a slice of 16 bit characters (wchars).
pub fn from_wide(value: &[u16]) -> Result<Self> {
unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
}
/// Get the contents of this `HSTRING` as a String lossily.
pub fn to_string_lossy(&self) -> std::string::String {
std::string::String::from_utf16_lossy(self.as_wide())
}
/// Get the contents of this `HSTRING` as a OsString.
#[cfg(windows)]
pub fn to_os_string(&self) -> std::ffi::OsString {
std::os::windows::ffi::OsStringExt::from_wide(self.as_wide())
}
/// # Safety
/// len must not be less than the number of items in the iterator.
unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Result<Self> {
if len == 0 {
return Ok(Self::new());
}
let ptr = Header::alloc(len.try_into()?)?;
// Place each utf-16 character into the buffer and
// increase len as we go along.
for (index, wide) in iter.enumerate() {
debug_assert!(index < len);
std::ptr::write((*ptr).data.add(index), wide);
(*ptr).len = index as u32 + 1;
}
// Write a 0 byte to the end of the buffer.
std::ptr::write((*ptr).data.offset((*ptr).len as isize), 0);
Ok(Self(std::ptr::NonNull::new(ptr)))
}
fn get_header(&self) -> Option<&Header> {
if let Some(header) = &self.0 {
unsafe { Some(header.as_ref()) }
} else {
None
}
}
}
impl RuntimeType for HSTRING {
const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"string");
}
impl TypeKind for HSTRING {
type TypeKind = ValueType;
}
impl Default for HSTRING {
fn default() -> Self {
Self::new()
}
}
impl Clone for HSTRING {
fn clone(&self) -> Self {
if let Some(header) = self.get_header() {
Self(std::ptr::NonNull::new(header.duplicate().unwrap()))
} else {
Self::new()
}
}
}
impl Drop for HSTRING {
fn drop(&mut self) {
if self.is_empty() {
return;
}
if let Some(header) = self.0.take() {
// REFERENCE_FLAG indicates a string backed by static or stack memory that is
// thus not reference-counted and does not need to be freed.
unsafe {
let header = header.as_ref();
if header.flags & REFERENCE_FLAG == 0 && header.count.release() == 0 {
crate::imp::heap_free(header as *const _ as *mut _);
}
}
}
}
}
unsafe impl Send for HSTRING {}
unsafe impl Sync for HSTRING {}
impl std::fmt::Display for HSTRING {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Decode(|| std::char::decode_utf16(self.as_wide().iter().cloned())))
}
}
impl std::fmt::Debug for HSTRING {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}\"", self)
}
}
impl std::convert::From<&str> for HSTRING {
fn from(value: &str) -> Self {
unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()).unwrap() }
}
}
impl std::convert::From<std::string::String> for HSTRING {
fn from(value: std::string::String) -> Self {
value.as_str().into()
}
}
impl std::convert::From<&std::string::String> for HSTRING {
fn from(value: &std::string::String) -> Self {
value.as_str().into()
}
}
#[cfg(windows)]
impl std::convert::From<&std::path::Path> for HSTRING {
fn from(value: &std::path::Path) -> Self {
value.as_os_str().into()
}
}
#[cfg(windows)]
impl std::convert::From<&std::ffi::OsStr> for HSTRING {
fn from(value: &std::ffi::OsStr) -> Self {
unsafe { Self::from_wide_iter(std::os::windows::ffi::OsStrExt::encode_wide(value), value.len()).unwrap() }
}
}
#[cfg(windows)]
impl std::convert::From<std::ffi::OsString> for HSTRING {
fn from(value: std::ffi::OsString) -> Self {
value.as_os_str().into()
}
}
#[cfg(windows)]
impl std::convert::From<&std::ffi::OsString> for HSTRING {
fn from(value: &std::ffi::OsString) -> Self {
value.as_os_str().into()
}
}
impl Eq for HSTRING {}
impl Ord for HSTRING {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_wide().cmp(other.as_wide())
}
}
impl PartialOrd for HSTRING {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for HSTRING {
fn eq(&self, other: &Self) -> bool {
*self.as_wide() == *other.as_wide()
}
}
impl PartialEq<std::string::String> for HSTRING {
fn eq(&self, other: &std::string::String) -> bool {
*self == **other
}
}
impl PartialEq<std::string::String> for &HSTRING {
fn eq(&self, other: &std::string::String) -> bool {
**self == **other
}
}
impl PartialEq<&std::string::String> for HSTRING {
fn eq(&self, other: &&std::string::String) -> bool {
*self == ***other
}
}
impl PartialEq<str> for HSTRING {
fn eq(&self, other: &str) -> bool {
self.as_wide().iter().copied().eq(other.encode_utf16())
}
}
impl PartialEq<str> for &HSTRING {
fn eq(&self, other: &str) -> bool {
**self == *other
}
}
impl PartialEq<&str> for HSTRING {
fn eq(&self, other: &&str) -> bool {
*self == **other
}
}
impl PartialEq<HSTRING> for str {
fn eq(&self, other: &HSTRING) -> bool {
*other == *self
}
}
impl PartialEq<HSTRING> for &str {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
impl PartialEq<&HSTRING> for str {
fn eq(&self, other: &&HSTRING) -> bool {
**other == *self
}
}
impl PartialEq<HSTRING> for std::string::String {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
impl PartialEq<HSTRING> for &std::string::String {
fn eq(&self, other: &HSTRING) -> bool {
*other == ***self
}
}
impl PartialEq<&HSTRING> for std::string::String {
fn eq(&self, other: &&HSTRING) -> bool {
**other == **self
}
}
#[cfg(windows)]
impl PartialEq<std::ffi::OsString> for HSTRING {
fn eq(&self, other: &std::ffi::OsString) -> bool {
*self == **other
}
}
#[cfg(windows)]
impl PartialEq<std::ffi::OsString> for &HSTRING {
fn eq(&self, other: &std::ffi::OsString) -> bool {
**self == **other
}
}
#[cfg(windows)]
impl PartialEq<&std::ffi::OsString> for HSTRING {
fn eq(&self, other: &&std::ffi::OsString) -> bool {
*self == ***other
}
}
#[cfg(windows)]
impl PartialEq<std::ffi::OsStr> for HSTRING {
fn eq(&self, other: &std::ffi::OsStr) -> bool {
self.as_wide().iter().copied().eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
}
}
#[cfg(windows)]
impl PartialEq<std::ffi::OsStr> for &HSTRING {
fn eq(&self, other: &std::ffi::OsStr) -> bool {
**self == *other
}
}
#[cfg(windows)]
impl PartialEq<&std::ffi::OsStr> for HSTRING {
fn eq(&self, other: &&std::ffi::OsStr) -> bool {
*self == **other
}
}
#[cfg(windows)]
impl PartialEq<HSTRING> for std::ffi::OsStr {
fn eq(&self, other: &HSTRING) -> bool {
*other == *self
}
}
#[cfg(windows)]
impl PartialEq<HSTRING> for &std::ffi::OsStr {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
#[cfg(windows)]
impl PartialEq<&HSTRING> for std::ffi::OsStr {
fn eq(&self, other: &&HSTRING) -> bool {
**other == *self
}
}
#[cfg(windows)]
impl PartialEq<HSTRING> for std::ffi::OsString {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
#[cfg(windows)]
impl PartialEq<HSTRING> for &std::ffi::OsString {
fn eq(&self, other: &HSTRING) -> bool {
*other == ***self
}
}
#[cfg(windows)]
impl PartialEq<&HSTRING> for std::ffi::OsString {
fn eq(&self, other: &&HSTRING) -> bool {
**other == **self
}
}
impl<'a> std::convert::TryFrom<&'a HSTRING> for std::string::String {
type Error = std::string::FromUtf16Error;
fn try_from(hstring: &HSTRING) -> std::result::Result<Self, Self::Error> {
std::string::String::from_utf16(hstring.as_wide())
}
}
impl std::convert::TryFrom<HSTRING> for std::string::String {
type Error = std::string::FromUtf16Error;
fn try_from(hstring: HSTRING) -> std::result::Result<Self, Self::Error> {
std::string::String::try_from(&hstring)
}
}
#[cfg(windows)]
impl<'a> std::convert::From<&'a HSTRING> for std::ffi::OsString {
fn from(hstring: &HSTRING) -> Self {
hstring.to_os_string()
}
}
#[cfg(windows)]
impl std::convert::From<HSTRING> for std::ffi::OsString {
fn from(hstring: HSTRING) -> Self {
Self::from(&hstring)
}
}
impl IntoParam<PCWSTR> for &HSTRING {
fn into_param(self) -> Param<PCWSTR> {
Param::Owned(PCWSTR(self.as_ptr()))
}
}
const REFERENCE_FLAG: u32 = 1;
#[repr(C)]
struct Header {
flags: u32,
len: u32,
_0: u32,
_1: u32,
data: *mut u16,
count: crate::imp::RefCount,
buffer_start: u16,
}
impl Header {
fn alloc(len: u32) -> Result<*mut Header> {
debug_assert!(len != 0);
// Allocate enough space for header and two bytes per character.
// The space for the terminating null character is already accounted for inside of `Header`.
let alloc_size = std::mem::size_of::<Header>() + 2 * len as usize;
let header = crate::imp::heap_alloc(alloc_size)? as *mut Header;
// SAFETY: uses `std::ptr::write` (since `header` is unintialized). `Header` is safe to be all zeros.
unsafe {
header.write(std::mem::MaybeUninit::<Header>::zeroed().assume_init());
(*header).len = len;
(*header).count = crate::imp::RefCount::new(1);
(*header).data = &mut (*header).buffer_start;
}
Ok(header)
}
fn duplicate(&self) -> Result<*mut Header> {
if self.flags & REFERENCE_FLAG == 0 {
// If this is not a "fast pass" string then simply increment the reference count.
self.count.add_ref();
Ok(self as *const Header as *mut Header)
} else {
// Otherwise, allocate a new string and copy the value into the new string.
let copy = Header::alloc(self.len)?;
// SAFETY: since we are duplicating the string it is safe to copy all data from self to the initialized `copy`.
// We copy `len + 1` characters since `len` does not account for the terminating null character.
unsafe {
std::ptr::copy_nonoverlapping(self.data, (*copy).data, self.len as usize + 1);
}
Ok(copy)
}
}
}

View File

@@ -6,10 +6,10 @@ pub trait TypeKind {
}
#[doc(hidden)]
pub struct ReferenceType;
pub struct InterfaceType;
#[doc(hidden)]
pub struct ValueType;
pub struct CloneType;
#[doc(hidden)]
pub struct CopyType;
@@ -19,43 +19,39 @@ pub trait Type<T: TypeKind, C = <T as TypeKind>::TypeKind>: TypeKind + Sized + C
type Abi;
type Default;
fn abi(&self) -> Self::Abi {
unsafe { std::mem::transmute_copy(self) }
}
/// # Safety
unsafe fn from_abi(abi: Self::Abi) -> Result<Self>;
fn from_default(default: &Self::Default) -> Result<Self>;
}
impl<T> Type<T, ReferenceType> for T
impl<T> Type<T, InterfaceType> for T
where
T: TypeKind<TypeKind = ReferenceType> + Clone,
T: TypeKind<TypeKind = InterfaceType> + Clone,
{
type Abi = *mut std::ffi::c_void;
type Abi = *mut core::ffi::c_void;
type Default = Option<Self>;
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
if !abi.is_null() {
Ok(std::mem::transmute_copy(&abi))
Ok(core::mem::transmute_copy(&abi))
} else {
Err(Error::OK)
Err(Error::empty())
}
}
fn from_default(default: &Self::Default) -> Result<Self> {
default.as_ref().cloned().ok_or(Error::OK)
default.as_ref().cloned().ok_or(Error::empty())
}
}
impl<T> Type<T, ValueType> for T
impl<T> Type<T, CloneType> for T
where
T: TypeKind<TypeKind = ValueType> + Clone,
T: TypeKind<TypeKind = CloneType> + Clone,
{
type Abi = std::mem::MaybeUninit<Self>;
type Abi = core::mem::MaybeUninit<Self>;
type Default = Self;
unsafe fn from_abi(abi: std::mem::MaybeUninit<Self>) -> Result<Self> {
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
Ok(abi.assume_init())
}
@@ -71,7 +67,7 @@ where
type Abi = Self;
type Default = Self;
unsafe fn from_abi(abi: Self) -> Result<Self> {
unsafe fn from_abi(abi: Self::Abi) -> Result<Self> {
Ok(abi)
}
@@ -81,7 +77,7 @@ where
}
impl<T: Interface> TypeKind for T {
type TypeKind = ReferenceType;
type TypeKind = InterfaceType;
}
impl<T> TypeKind for *mut T {
@@ -102,9 +98,3 @@ 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;
/// # Safety
#[doc(hidden)]
pub unsafe fn from_abi<T: Type<T>>(abi: T::Abi) -> Result<T> {
T::from_abi(abi)
}

View File

@@ -1,32 +1,36 @@
use super::*;
use core::ffi::c_void;
use core::ptr::NonNull;
/// 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(std::ptr::NonNull<std::ffi::c_void>);
pub struct IUnknown(NonNull<c_void>);
#[doc(hidden)]
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct IUnknown_Vtbl {
pub QueryInterface: unsafe extern "system" fn(this: *mut std::ffi::c_void, iid: *const GUID, interface: *mut *mut std::ffi::c_void) -> HRESULT,
pub AddRef: unsafe extern "system" fn(this: *mut std::ffi::c_void) -> u32,
pub Release: unsafe extern "system" fn(this: *mut std::ffi::c_void) -> u32,
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;
}
unsafe impl ComInterface for IUnknown {
const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046);
}
impl Clone for IUnknown {
fn clone(&self) -> Self {
unsafe {
(self.vtable().AddRef)(std::mem::transmute_copy(self));
(self.vtable().AddRef)(core::mem::transmute_copy(self));
}
Self(self.0)
@@ -36,68 +40,154 @@ impl Clone for IUnknown {
impl Drop for IUnknown {
fn drop(&mut self) {
unsafe {
(self.vtable().Release)(std::mem::transmute_copy(self));
(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`.
self.cast::<IUnknown>().unwrap().0 == other.cast::<IUnknown>().unwrap().0
self.as_raw() == other.as_raw()
|| self.cast::<IUnknown>().unwrap().0 == other.cast::<IUnknown>().unwrap().0
}
}
impl Eq for IUnknown {}
impl std::fmt::Debug for IUnknown {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IUnknown").field(&self.0).finish()
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 std::ffi::c_void) -> HRESULT;
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 interfacer pointer is no longer used as calling `Release`
/// 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.
unsafe fn Release(&self) -> u32;
///
/// 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;
/// Given a reference to an inner type, returns a reference to the outer shared type.
///
/// # Safety
///
/// This function should only be called from methods that implement COM interfaces, i.e.
/// implementations of methods on `IFoo_Impl` traits.
// TODO: This can be made safe, if IFoo_Impl are moved to the Object_Impl types.
// That requires some substantial redesign, though.
unsafe fn from_inner_ref(inner: &Self::Impl) -> &Self;
/// 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>;
/// The distance from the start of `<Foo>_Impl` to the `this` field within it, measured in
/// pointer-sized elements. The `this` field contains the `MyApp` instance.
const INNER_OFFSET_IN_POINTERS: usize;
}
#[cfg(feature = "implement")]
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 std::ffi::c_void, iid: *const GUID, interface: *mut *mut std::ffi::c_void) -> HRESULT {
let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T;
unsafe extern "system" fn QueryInterface<T: IUnknownImpl, const OFFSET: isize>(
this: *mut c_void,
iid: *const GUID,
interface: *mut *mut c_void,
) -> HRESULT {
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 std::ffi::c_void) -> u32 {
let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T;
unsafe extern "system" fn AddRef<T: IUnknownImpl, const OFFSET: isize>(
this: *mut c_void,
) -> u32 {
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 std::ffi::c_void) -> u32 {
let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T;
(*this).Release()
unsafe extern "system" fn Release<T: IUnknownImpl, const OFFSET: isize>(
this: *mut c_void,
) -> u32 {
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>,
}
Self { QueryInterface: QueryInterface::<T, OFFSET>, AddRef: AddRef::<T, OFFSET>, Release: Release::<T, OFFSET> }
}
}

View File

@@ -0,0 +1,852 @@
use super::*;
use core::mem::transmute;
/// A VARIANT ([VARIANT](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant)) is a container that can store different types of values.
#[repr(transparent)]
pub struct VARIANT(imp::VARIANT);
/// A PROPVARIANT ([PROPVARIANT](https://learn.microsoft.com/en-us/windows/win32/api/propidlbase/ns-propidlbase-propvariant)) is a container that can store different types of values.
#[repr(transparent)]
pub struct PROPVARIANT(imp::PROPVARIANT);
impl Default for VARIANT {
fn default() -> Self {
Self::new()
}
}
impl Default for PROPVARIANT {
fn default() -> Self {
Self::new()
}
}
impl Clone for VARIANT {
fn clone(&self) -> Self {
unsafe {
let mut value = Self::new();
imp::VariantCopy(&mut value.0, &self.0);
value
}
}
}
impl Clone for PROPVARIANT {
fn clone(&self) -> Self {
unsafe {
let mut value = Self::new();
imp::PropVariantCopy(&mut value.0, &self.0);
value
}
}
}
impl Drop for VARIANT {
fn drop(&mut self) {
unsafe { imp::VariantClear(&mut self.0) };
}
}
impl Drop for PROPVARIANT {
fn drop(&mut self) {
unsafe { imp::PropVariantClear(&mut self.0) };
}
}
impl TypeKind for VARIANT {
type TypeKind = CloneType;
}
impl TypeKind for PROPVARIANT {
type TypeKind = CloneType;
}
impl core::fmt::Debug for VARIANT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_struct("VARIANT");
debug.field("type", &unsafe { self.0.Anonymous.Anonymous.vt });
if let Ok(value) = BSTR::try_from(self) {
debug.field("value", &value);
}
debug.finish()
}
}
impl core::fmt::Debug for PROPVARIANT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_struct("PROPVARIANT");
debug.field("type", &unsafe { self.0.Anonymous.Anonymous.vt });
if let Ok(value) = BSTR::try_from(self) {
debug.field("value", &value);
}
debug.finish()
}
}
impl core::fmt::Display for VARIANT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "{}", BSTR::try_from(self).unwrap_or_default())
}
}
impl core::fmt::Display for PROPVARIANT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "{}", BSTR::try_from(self).unwrap_or_default())
}
}
impl PartialEq for VARIANT {
fn eq(&self, other: &Self) -> bool {
unsafe {
if self.0.Anonymous.Anonymous.vt != other.0.Anonymous.Anonymous.vt {
return false;
}
let this = PROPVARIANT::try_from(self);
let other = PROPVARIANT::try_from(other);
if let (Ok(this), Ok(other)) = (this, other) {
this.eq(&other)
} else {
false
}
}
}
}
impl PartialEq for PROPVARIANT {
fn eq(&self, other: &Self) -> bool {
unsafe {
if self.0.Anonymous.Anonymous.vt != other.0.Anonymous.Anonymous.vt {
return false;
}
imp::PropVariantCompareEx(&self.0, &other.0, 0, 0) == 0
}
}
}
impl Eq for VARIANT {}
impl Eq for PROPVARIANT {}
impl VARIANT {
/// Create an empty `VARIANT`.
///
/// This function does not allocate memory.
pub fn new() -> Self {
unsafe { core::mem::zeroed() }
}
/// Returns true if the `VARIANT` is empty.
pub const fn is_empty(&self) -> bool {
unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_EMPTY }
}
/// Creates a `VARIANT` by taking ownership of the raw data.
///
/// # Safety
///
/// The raw data must be owned by the caller and represent a valid `VARIANT` data structure.
pub unsafe fn from_raw(raw: imp::VARIANT) -> Self {
Self(raw)
}
/// Returns the underlying raw data for the `VARIANT`.
pub fn as_raw(&self) -> &imp::VARIANT {
&self.0
}
}
impl PROPVARIANT {
/// Create an empty `PROPVARIANT`.
///
/// This function does not allocate memory.
pub fn new() -> Self {
unsafe { core::mem::zeroed() }
}
/// Returns true if the `PROPVARIANT` is empty.
pub const fn is_empty(&self) -> bool {
unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_EMPTY }
}
/// Creates a `PROPVARIANT` by taking ownership of the raw data.
///
/// # Safety
///
/// The raw data must be owned by the caller and represent a valid `PROPVARIANT` data structure.
pub unsafe fn from_raw(raw: imp::PROPVARIANT) -> Self {
Self(raw)
}
/// Returns the underlying raw data for the `PROPVARIANT`.
pub fn as_raw(&self) -> &imp::PROPVARIANT {
&self.0
}
}
impl TryFrom<&VARIANT> for PROPVARIANT {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
unsafe {
let mut value = Self::new();
HRESULT(imp::VariantToPropVariant(&from.0, &mut value.0)).map(|| value)
}
}
}
impl TryFrom<&PROPVARIANT> for VARIANT {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
unsafe {
let mut value = Self::new();
HRESULT(imp::PropVariantToVariant(&from.0, &mut value.0)).map(|| value)
}
}
}
// VT_UNKNOWN
impl From<IUnknown> for VARIANT {
fn from(value: IUnknown) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_UNKNOWN,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 {
punkVal: value.into_raw(),
},
},
},
})
}
}
impl From<IUnknown> for PROPVARIANT {
fn from(value: IUnknown) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_UNKNOWN,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 {
punkVal: value.into_raw(),
},
},
},
})
}
}
impl TryFrom<&VARIANT> for IUnknown {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
unsafe {
if from.0.Anonymous.Anonymous.vt == imp::VT_UNKNOWN
&& !from.0.Anonymous.Anonymous.Anonymous.punkVal.is_null()
{
let unknown: &IUnknown = transmute(&from.0.Anonymous.Anonymous.Anonymous.punkVal);
Ok(unknown.clone())
} else {
Err(Error::from_hresult(imp::TYPE_E_TYPEMISMATCH))
}
}
}
}
impl TryFrom<&PROPVARIANT> for IUnknown {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
unsafe {
if from.0.Anonymous.Anonymous.vt == imp::VT_UNKNOWN
&& !from.0.Anonymous.Anonymous.Anonymous.punkVal.is_null()
{
let unknown: &IUnknown = transmute(&from.0.Anonymous.Anonymous.Anonymous.punkVal);
Ok(unknown.clone())
} else {
Err(Error::from_hresult(imp::TYPE_E_TYPEMISMATCH))
}
}
}
}
// VT_BSTR
impl From<BSTR> for VARIANT {
fn from(value: BSTR) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_BSTR,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 {
bstrVal: value.into_raw(),
},
},
},
})
}
}
impl From<BSTR> for PROPVARIANT {
fn from(value: BSTR) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_BSTR,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 {
bstrVal: value.into_raw(),
},
},
},
})
}
}
impl From<&str> for VARIANT {
fn from(value: &str) -> Self {
BSTR::from(value).into()
}
}
impl From<&str> for PROPVARIANT {
fn from(value: &str) -> Self {
BSTR::from(value).into()
}
}
impl TryFrom<&VARIANT> for BSTR {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let pv = PROPVARIANT::try_from(from)?;
BSTR::try_from(&pv)
}
}
impl TryFrom<&PROPVARIANT> for BSTR {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = Self::new();
HRESULT(unsafe { imp::PropVariantToBSTR(&from.0, &mut value as *mut _ as *mut _) })
.map(|| value)
}
}
// VT_BOOL
impl From<bool> for VARIANT {
fn from(value: bool) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_BOOL,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 {
boolVal: if value { -1 } else { 0 },
},
},
},
})
}
}
impl From<bool> for PROPVARIANT {
fn from(value: bool) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_BOOL,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 {
boolVal: if value { -1 } else { 0 },
},
},
},
})
}
}
impl TryFrom<&VARIANT> for bool {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToBoolean(&from.0, &mut value) }).map(|| value != 0)
}
}
impl TryFrom<&PROPVARIANT> for bool {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToBoolean(&from.0, &mut value) }).map(|| value != 0)
}
}
// VT_UI1
impl From<u8> for VARIANT {
fn from(value: u8) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_UI1,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { bVal: value },
},
},
})
}
}
impl From<u8> for PROPVARIANT {
fn from(value: u8) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_UI1,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { bVal: value },
},
},
})
}
}
// VT_I1
impl From<i8> for VARIANT {
fn from(value: i8) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_I1,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { cVal: value },
},
},
})
}
}
impl From<i8> for PROPVARIANT {
fn from(value: i8) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_I1,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { cVal: value },
},
},
})
}
}
// VT_UI2
impl From<u16> for VARIANT {
fn from(value: u16) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_UI2,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { uiVal: value },
},
},
})
}
}
impl From<u16> for PROPVARIANT {
fn from(value: u16) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_UI2,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { uiVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for u16 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToUInt16(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for u16 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToUInt16(&from.0, &mut value) }).map(|| value)
}
}
// VT_I2
impl From<i16> for VARIANT {
fn from(value: i16) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_I2,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { iVal: value },
},
},
})
}
}
impl From<i16> for PROPVARIANT {
fn from(value: i16) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_I2,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { iVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for i16 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToInt16(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for i16 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToInt16(&from.0, &mut value) }).map(|| value)
}
}
// VT_UI4
impl From<u32> for VARIANT {
fn from(value: u32) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_UI4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { ulVal: value },
},
},
})
}
}
impl From<u32> for PROPVARIANT {
fn from(value: u32) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_UI4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { ulVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for u32 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToUInt32(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for u32 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToUInt32(&from.0, &mut value) }).map(|| value)
}
}
// VT_I4
impl From<i32> for VARIANT {
fn from(value: i32) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_I4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { lVal: value },
},
},
})
}
}
impl From<i32> for PROPVARIANT {
fn from(value: i32) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_I4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { lVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for i32 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToInt32(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for i32 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToInt32(&from.0, &mut value) }).map(|| value)
}
}
// VT_UI8
impl From<u64> for VARIANT {
fn from(value: u64) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_UI8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { ullVal: value },
},
},
})
}
}
impl From<u64> for PROPVARIANT {
fn from(value: u64) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_UI8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { uhVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for u64 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToUInt64(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for u64 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToUInt64(&from.0, &mut value) }).map(|| value)
}
}
// VT_I8
impl From<i64> for VARIANT {
fn from(value: i64) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_I8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { llVal: value },
},
},
})
}
}
impl From<i64> for PROPVARIANT {
fn from(value: i64) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_I8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { hVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for i64 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::VariantToInt64(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for i64 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0;
HRESULT(unsafe { imp::PropVariantToInt64(&from.0, &mut value) }).map(|| value)
}
}
// VT_R4
impl From<f32> for VARIANT {
fn from(value: f32) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_R4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { fltVal: value },
},
},
})
}
}
impl From<f32> for PROPVARIANT {
fn from(value: f32) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_R4,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { fltVal: value },
},
},
})
}
}
// VT_R8
impl From<f64> for VARIANT {
fn from(value: f64) -> Self {
Self(imp::VARIANT {
Anonymous: imp::VARIANT_0 {
Anonymous: imp::VARIANT_0_0 {
vt: imp::VT_R8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::VARIANT_0_0_0 { dblVal: value },
},
},
})
}
}
impl From<f64> for PROPVARIANT {
fn from(value: f64) -> Self {
Self(imp::PROPVARIANT {
Anonymous: imp::PROPVARIANT_0 {
Anonymous: imp::PROPVARIANT_0_0 {
vt: imp::VT_R8,
wReserved1: 0,
wReserved2: 0,
wReserved3: 0,
Anonymous: imp::PROPVARIANT_0_0_0 { dblVal: value },
},
},
})
}
}
impl TryFrom<&VARIANT> for f64 {
type Error = Error;
fn try_from(from: &VARIANT) -> Result<Self> {
let mut value = 0.0;
HRESULT(unsafe { imp::VariantToDouble(&from.0, &mut value) }).map(|| value)
}
}
impl TryFrom<&PROPVARIANT> for f64 {
type Error = Error;
fn try_from(from: &PROPVARIANT) -> Result<Self> {
let mut value = 0.0;
HRESULT(unsafe { imp::PropVariantToDouble(&from.0, &mut value) }).map(|| value)
}
}

View File

@@ -1,11 +1,11 @@
use super::*;
use std::marker::PhantomData;
use core::marker::PhantomData;
/// `Weak` holds a non-owning reference to an object.
#[derive(Clone, PartialEq, Eq, Default)]
pub struct Weak<I: ComInterface>(Option<crate::imp::IWeakReference>, PhantomData<I>);
pub struct Weak<I: Interface>(Option<imp::IWeakReference>, PhantomData<I>);
impl<I: ComInterface> Weak<I> {
impl<I: Interface> Weak<I> {
/// Creates a new `Weak` object without any backing object.
pub fn new() -> Self {
Self(None, PhantomData)
@@ -13,11 +13,16 @@ impl<I: ComInterface> Weak<I> {
/// 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() })
self.0
.as_ref()
.and_then(|inner| unsafe { inner.Resolve().ok() })
}
pub(crate) fn downgrade(source: &crate::imp::IWeakReferenceSource) -> Result<Self> {
pub(crate) fn downgrade(source: &imp::IWeakReferenceSource) -> Result<Self> {
let reference = unsafe { source.GetWeakReference().ok() };
Ok(Self(reference, PhantomData))
}
}
unsafe impl<I: Interface> Send for Weak<I> {}
unsafe impl<I: Interface> Sync for Weak<I> {}

View File

@@ -0,0 +1,76 @@
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::*;
mod variant;
pub use variant::*;
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::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;
}

View File

@@ -1,84 +0,0 @@
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="windows_core::array::Array&lt;*&gt;">
<DisplayString>{{ len={len} }}</DisplayString>
<Expand>
<Item Name="[len]">len</Item>
<ArrayItems>
<Size>len</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="windows_core::error::Error">
<Expand>
<ExpandedItem>code</ExpandedItem>
<Item Name="[info]">info</Item>
</Expand>
</Type>
<Type Name="windows_core::hresult::HRESULT">
<DisplayString>{(HRESULT)__0}</DisplayString>
</Type>
<Type Name="windows_core::imp::ref_count::RefCount">
<DisplayString>{__0}</DisplayString>
</Type>
<Type Name="windows_core::strings::hstring::HSTRING">
<Intrinsic Name="header" Expression="*((windows_core::strings::hstring::Header**)this)" ReturnType="windows_core::strings::hstring::Header *" />
<Intrinsic Name="is_empty" Expression="header() == nullptr" />
<DisplayString Condition="is_empty()">""</DisplayString>
<DisplayString>{((char16_t*)header()->data),[header()->len]su}</DisplayString>
<Expand>
<Item Name="[len]">is_empty() ? (unsigned int)0 : header()->len</Item>
<Item Name="[ref_count]" Condition="!is_empty()">header()->count</Item>
<Item Name="[flags]" Condition="!is_empty()">header()->flags</Item>
<Synthetic Name="[chars]" Condition="!is_empty()">
<Expand>
<ArrayItems>
<Size>header()->len</Size>
<ValuePointer>(char16_t*)header()->data</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="windows_core::strings::pstr::PSTR">
<AlternativeType Name="windows_core::strings::pcstr::PCSTR" />
<Intrinsic Name="len" Expression="strlen(((char*)__0))" />
<DisplayString>{(char*)__0,[len()]s8}</DisplayString>
<Expand>
<Item Name="[len]">len()</Item>
<Synthetic Name="[chars]">
<Expand>
<ArrayItems>
<Size>len()</Size>
<ValuePointer>(char*)__0</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="windows_core::strings::pwstr::PWSTR">
<AlternativeType Name="windows_core::strings::pcwstr::PCWSTR" />
<Intrinsic Name="len" Expression="wcslen(((WCHAR*)__0))" />
<DisplayString>{(char16_t*)__0,[len()]su}</DisplayString>
<Expand>
<Item Name="[len]">len()</Item>
<Synthetic Name="[chars]">
<Expand>
<ArrayItems>
<Size>len()</Size>
<ValuePointer>(char16_t*)__0</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"e2760c01e9e1b707bbe2ba1af6ddbc7555e3f6111eb4981fed6a1077afce1695","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","src/lib.rs":"d372c5fa2db9fe014e71ba3cd2c81a1b566571fbca2d6a6330f4e0d224bfa880"},"package":"2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"}

View File

@@ -0,0 +1,64 @@
# 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.70"
name = "windows-implement"
version = "0.58.0"
authors = ["Microsoft"]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "The implement macro for the windows crate"
readme = false
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 = []
[lib]
name = "windows_implement"
path = "src/lib.rs"
proc-macro = true
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "2.0"
features = [
"parsing",
"proc-macro",
"printing",
"full",
"derive",
]
default-features = false
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,588 @@
/*!
Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
*/
use quote::{quote, ToTokens};
/// Implements one or more COM interfaces.
///
/// # Example
///
/// Here is a [more complete tutorial](https://kennykerr.ca/rust-getting-started/how-to-implement-com-interface.html).
///
/// ```rust,ignore
/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")]
/// unsafe trait IValue: IUnknown {
/// fn GetValue(&self, value: *mut i32) -> HRESULT;
/// }
///
/// #[implement(IValue)]
/// struct Value(i32);
///
/// impl IValue_Impl for Value {
/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT {
/// *value = self.0;
/// HRESULT(0)
/// }
/// }
///
/// fn main() {
/// let rust_instance = Value(123);
/// let com_object: IValue = rust_instance.into();
/// // You can now call interface methods on com_object.
/// }
/// ```
#[proc_macro_attribute]
pub fn implement(
attributes: proc_macro::TokenStream,
original_type: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let attributes = syn::parse_macro_input!(attributes as ImplementAttributes);
let interfaces_len = proc_macro2::Literal::usize_unsuffixed(attributes.implement.len());
let identity_type = if let Some(first) = attributes.implement.first() {
first.to_ident()
} else {
quote! { ::windows_core::IInspectable }
};
let original_type2 = original_type.clone();
let original_type2 = syn::parse_macro_input!(original_type2 as syn::ItemStruct);
let vis = &original_type2.vis;
let original_ident = &original_type2.ident;
let mut constraints = quote! {};
if let Some(where_clause) = &original_type2.generics.where_clause {
where_clause.predicates.to_tokens(&mut constraints);
}
let generics = if original_type2.generics.lt_token.is_some() {
let mut params = quote! {};
original_type2.generics.params.to_tokens(&mut params);
quote! { <#params> }
} else {
quote! { <> }
};
let impl_ident = quote::format_ident!("{}_Impl", original_ident);
let vtbl_idents = attributes
.implement
.iter()
.map(|implement| implement.to_vtbl_ident());
let vtbl_idents2 = vtbl_idents.clone();
let vtable_news = attributes
.implement
.iter()
.enumerate()
.map(|(enumerate, implement)| {
let vtbl_ident = implement.to_vtbl_ident();
let offset = proc_macro2::Literal::isize_unsuffixed(-1 - enumerate as isize);
quote! { #vtbl_ident::new::<Self, #offset>() }
});
let offset = attributes
.implement
.iter()
.enumerate()
.map(|(offset, _)| proc_macro2::Literal::usize_unsuffixed(offset));
let queries = attributes
.implement
.iter()
.enumerate()
.map(|(count, implement)| {
let vtbl_ident = implement.to_vtbl_ident();
let offset = proc_macro2::Literal::usize_unsuffixed(count);
quote! {
else if #vtbl_ident::matches(iid) {
&self.vtables.#offset as *const _ as *mut _
}
}
});
// Dynamic casting requires that the object not contain non-static lifetimes.
let enable_dyn_casting = original_type2.generics.lifetimes().count() == 0;
let dynamic_cast_query = if enable_dyn_casting {
quote! {
else if *iid == ::windows_core::DYNAMIC_CAST_IID {
// DYNAMIC_CAST_IID is special. We _do not_ increase the reference count for this pseudo-interface.
// Also, instead of returning an interface pointer, we simply write the `&dyn Any` directly to the
// 'interface' pointer. Since the size of `&dyn Any` is 2 pointers, not one, the caller must be
// prepared for this. This is not a normal QueryInterface call.
//
// See the `Interface::cast_to_any` method, which is the only caller that should use DYNAMIC_CAST_ID.
(interface as *mut *const dyn core::any::Any).write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any);
return ::windows_core::HRESULT(0);
}
}
} else {
quote!()
};
// The distance from the beginning of the generated type to the 'this' field, in units of pointers (not bytes).
let offset_of_this_in_pointers = 1 + attributes.implement.len();
let offset_of_this_in_pointers_token =
proc_macro2::Literal::usize_unsuffixed(offset_of_this_in_pointers);
let trust_level = proc_macro2::Literal::usize_unsuffixed(attributes.trust_level);
let conversions = attributes.implement.iter().enumerate().map(|(enumerate, implement)| {
let interface_ident = implement.to_ident();
let offset = proc_macro2::Literal::usize_unsuffixed(enumerate);
quote! {
impl #generics ::core::convert::From<#original_ident::#generics> for #interface_ident where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
impl #generics ::windows_core::ComObjectInterface<#interface_ident> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, #interface_ident> {
unsafe {
let interface_ptr = &self.vtables.#offset;
::core::mem::transmute(interface_ptr)
}
}
}
impl #generics ::windows_core::AsImpl<#original_ident::#generics> for #interface_ident where #constraints {
// SAFETY: the offset is guranteed to be in bounds, and the implementation struct
// is guaranteed to live at least as long as `self`.
#[inline(always)]
unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> {
let this = ::windows_core::Interface::as_raw(self);
// Subtract away the vtable offset plus 1, for the `identity` field, to get
// to the impl struct which contains that original implementation type.
let this = (this as *mut *mut ::core::ffi::c_void).sub(1 + #offset) as *mut #impl_ident::#generics;
::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics as *mut #original_ident::#generics)
}
}
}
});
let tokens = quote! {
#[repr(C)]
#[allow(non_camel_case_types)]
#vis struct #impl_ident #generics where #constraints {
identity: &'static ::windows_core::IInspectable_Vtbl,
vtables: (#(&'static #vtbl_idents,)*),
this: #original_ident::#generics,
count: ::windows_core::imp::WeakRefCount,
}
impl #generics #impl_ident::#generics where #constraints {
const VTABLES: (#(#vtbl_idents2,)*) = (#(#vtable_news,)*);
const IDENTITY: ::windows_core::IInspectable_Vtbl = ::windows_core::IInspectable_Vtbl::new::<Self, #identity_type, 0>();
}
impl #generics ::windows_core::ComObjectInner for #original_ident::#generics where #constraints {
type Outer = #impl_ident::#generics;
// IMPORTANT! This function handles assembling the "boxed" type of a COM object.
// It immediately moves the box into a heap allocation (box) and returns only a ComObject
// reference that points to it. We intentionally _do not_ expose any owned instances of
// Foo_Impl to safe Rust code, because doing so would allow unsound behavior in safe Rust
// code, due to the adjustments of the reference count that Foo_Impl permits.
//
// This is why this function returns ComObject<Self> instead of returning #impl_ident.
fn into_object(self) -> ::windows_core::ComObject<Self> {
let boxed = ::windows_core::imp::Box::new(#impl_ident::#generics {
identity: &#impl_ident::#generics::IDENTITY,
vtables: (#(&#impl_ident::#generics::VTABLES.#offset,)*),
this: self,
count: ::windows_core::imp::WeakRefCount::new(),
});
unsafe {
let ptr = ::windows_core::imp::Box::into_raw(boxed);
::windows_core::ComObject::from_raw(
::core::ptr::NonNull::new_unchecked(ptr)
)
}
}
}
impl #generics ::windows_core::IUnknownImpl for #impl_ident::#generics where #constraints {
type Impl = #original_ident::#generics;
#[inline(always)]
fn get_impl(&self) -> &Self::Impl {
&self.this
}
#[inline(always)]
fn get_impl_mut(&mut self) -> &mut Self::Impl {
&mut self.this
}
#[inline(always)]
fn is_reference_count_one(&self) -> bool {
self.count.is_one()
}
#[inline(always)]
fn into_inner(self) -> Self::Impl {
self.this
}
unsafe fn QueryInterface(&self, iid: *const ::windows_core::GUID, interface: *mut *mut ::core::ffi::c_void) -> ::windows_core::HRESULT {
if iid.is_null() || interface.is_null() {
return ::windows_core::imp::E_POINTER;
}
let iid = &*iid;
let interface_ptr: *mut ::core::ffi::c_void = if iid == &<::windows_core::IUnknown as ::windows_core::Interface>::IID
|| iid == &<::windows_core::IInspectable as ::windows_core::Interface>::IID
|| iid == &<::windows_core::imp::IAgileObject as ::windows_core::Interface>::IID {
&self.identity as *const _ as *mut _
}
#(#queries)*
#dynamic_cast_query
else {
::core::ptr::null_mut()
};
if !interface_ptr.is_null() {
*interface = interface_ptr;
self.count.add_ref();
return ::windows_core::HRESULT(0);
}
let interface_ptr = self.count.query(iid, &self.identity as *const _ as *mut _);
*interface = interface_ptr;
if interface_ptr.is_null() {
::windows_core::imp::E_NOINTERFACE
} else {
::windows_core::HRESULT(0)
}
}
#[inline(always)]
fn AddRef(&self) -> u32 {
self.count.add_ref()
}
#[inline(always)]
unsafe fn Release(self_: *mut Self) -> u32 {
let remaining = (*self_).count.release();
if remaining == 0 {
_ = ::windows_core::imp::Box::from_raw(self_);
}
remaining
}
unsafe fn GetTrustLevel(&self, value: *mut i32) -> ::windows_core::HRESULT {
if value.is_null() {
return ::windows_core::imp::E_POINTER;
}
*value = #trust_level;
::windows_core::HRESULT(0)
}
unsafe fn from_inner_ref(inner: &Self::Impl) -> &Self {
&*((inner as *const Self::Impl as *const *const ::core::ffi::c_void)
.sub(#offset_of_this_in_pointers_token) as *const Self)
}
fn to_object(&self) -> ::windows_core::ComObject<Self::Impl> {
self.count.add_ref();
unsafe {
::windows_core::ComObject::from_raw(
::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self)
)
}
}
const INNER_OFFSET_IN_POINTERS: usize = #offset_of_this_in_pointers_token;
}
impl #generics #original_ident::#generics where #constraints {
/// Try casting as the provided interface
///
/// # Safety
///
/// This function can only be safely called if `self` has been heap allocated and pinned using
/// the mechanisms provided by `implement` macro.
#[inline(always)]
unsafe fn cast<I: ::windows_core::Interface>(&self) -> ::windows_core::Result<I> {
let boxed = (self as *const _ as *const *mut ::core::ffi::c_void).sub(1 + #interfaces_len) as *mut #impl_ident::#generics;
let mut result = ::core::ptr::null_mut();
_ = <#impl_ident::#generics as ::windows_core::IUnknownImpl>::QueryInterface(&*boxed, &I::IID, &mut result);
::windows_core::Type::from_abi(result)
}
}
impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IUnknown where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IInspectable where #constraints {
#[inline(always)]
fn from(this: #original_ident::#generics) -> Self {
let com_object = ::windows_core::ComObject::new(this);
com_object.into_interface()
}
}
impl #generics ::windows_core::ComObjectInterface<::windows_core::IUnknown> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IUnknown> {
unsafe {
let interface_ptr = &self.identity;
::core::mem::transmute(interface_ptr)
}
}
}
impl #generics ::windows_core::ComObjectInterface<::windows_core::IInspectable> for #impl_ident::#generics where #constraints {
#[inline(always)]
fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IInspectable> {
unsafe {
let interface_ptr = &self.identity;
::core::mem::transmute(interface_ptr)
}
}
}
impl #generics ::windows_core::AsImpl<#original_ident::#generics> for ::windows_core::IUnknown where #constraints {
// SAFETY: the offset is guranteed to be in bounds, and the implementation struct
// is guaranteed to live at least as long as `self`.
#[inline(always)]
unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> {
let this = ::windows_core::Interface::as_raw(self);
// Subtract away the vtable offset plus 1, for the `identity` field, to get
// to the impl struct which contains that original implementation type.
let this = (this as *mut *mut ::core::ffi::c_void).sub(1) as *mut #impl_ident::#generics;
::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics as *mut #original_ident::#generics)
}
}
impl #generics ::core::ops::Deref for #impl_ident::#generics where #constraints {
type Target = #original_ident::#generics;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.this
}
}
// We intentionally do not provide a DerefMut impl, due to paranoia around soundness.
#(#conversions)*
};
let mut tokens: proc_macro::TokenStream = tokens.into();
tokens.extend(core::iter::once(original_type));
tokens
}
#[derive(Default)]
struct ImplementType {
type_name: String,
generics: Vec<ImplementType>,
}
impl ImplementType {
fn to_ident(&self) -> proc_macro2::TokenStream {
let type_name = syn::parse_str::<proc_macro2::TokenStream>(&self.type_name)
.expect("Invalid token stream");
let generics = self.generics.iter().map(|g| g.to_ident());
quote! { #type_name<#(#generics,)*> }
}
fn to_vtbl_ident(&self) -> proc_macro2::TokenStream {
let ident = self.to_ident();
quote! {
<#ident as ::windows_core::Interface>::Vtable
}
}
}
#[derive(Default)]
struct ImplementAttributes {
pub implement: Vec<ImplementType>,
pub trust_level: usize,
}
impl syn::parse::Parse for ImplementAttributes {
fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
let mut input = Self::default();
while !cursor.is_empty() {
input.parse_implement(cursor)?;
}
Ok(input)
}
}
impl ImplementAttributes {
fn parse_implement(&mut self, cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result<()> {
let tree = cursor.parse::<UseTree2>()?;
self.walk_implement(&tree, &mut String::new())?;
if !cursor.is_empty() {
cursor.parse::<syn::Token![,]>()?;
}
Ok(())
}
fn walk_implement(
&mut self,
tree: &UseTree2,
namespace: &mut String,
) -> syn::parse::Result<()> {
match tree {
UseTree2::Path(input) => {
if !namespace.is_empty() {
namespace.push_str("::");
}
namespace.push_str(&input.ident.to_string());
self.walk_implement(&input.tree, namespace)?;
}
UseTree2::Name(_) => {
self.implement.push(tree.to_element_type(namespace)?);
}
UseTree2::Group(input) => {
for tree in &input.items {
self.walk_implement(tree, namespace)?;
}
}
UseTree2::TrustLevel(input) => self.trust_level = *input,
}
Ok(())
}
}
enum UseTree2 {
Path(UsePath2),
Name(UseName2),
Group(UseGroup2),
TrustLevel(usize),
}
impl UseTree2 {
fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result<ImplementType> {
match self {
UseTree2::Path(input) => {
if !namespace.is_empty() {
namespace.push_str("::");
}
namespace.push_str(&input.ident.to_string());
input.tree.to_element_type(namespace)
}
UseTree2::Name(input) => {
let mut type_name = input.ident.to_string();
if !namespace.is_empty() {
type_name = format!("{namespace}::{type_name}");
}
let mut generics = vec![];
for g in &input.generics {
generics.push(g.to_element_type(&mut String::new())?);
}
Ok(ImplementType {
type_name,
generics,
})
}
UseTree2::Group(input) => Err(syn::parse::Error::new(
input.brace_token.span.join(),
"Syntax not supported",
)),
_ => unimplemented!(),
}
}
}
struct UsePath2 {
pub ident: syn::Ident,
pub tree: Box<UseTree2>,
}
struct UseName2 {
pub ident: syn::Ident,
pub generics: Vec<UseTree2>,
}
struct UseGroup2 {
pub brace_token: syn::token::Brace,
pub items: syn::punctuated::Punctuated<UseTree2, syn::Token![,]>,
}
impl syn::parse::Parse for UseTree2 {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result<UseTree2> {
let lookahead = input.lookahead1();
if lookahead.peek(syn::Ident) {
use syn::ext::IdentExt;
let ident = input.call(syn::Ident::parse_any)?;
if input.peek(syn::Token![::]) {
input.parse::<syn::Token![::]>()?;
Ok(UseTree2::Path(UsePath2 {
ident,
tree: Box::new(input.parse()?),
}))
} else if input.peek(syn::Token![=]) {
if ident != "TrustLevel" {
return Err(syn::parse::Error::new(
ident.span(),
"Unrecognized key-value pair",
));
}
input.parse::<syn::Token![=]>()?;
let span = input.span();
let value = input.call(syn::Ident::parse_any)?;
match value.to_string().as_str() {
"Partial" => Ok(UseTree2::TrustLevel(1)),
"Full" => Ok(UseTree2::TrustLevel(2)),
_ => Err(syn::parse::Error::new(
span,
"`TrustLevel` must be `Partial` or `Full`",
)),
}
} else {
let generics = if input.peek(syn::Token![<]) {
input.parse::<syn::Token![<]>()?;
let mut generics = Vec::new();
loop {
generics.push(input.parse::<UseTree2>()?);
if input.parse::<syn::Token![,]>().is_err() {
break;
}
}
input.parse::<syn::Token![>]>()?;
generics
} else {
Vec::new()
};
Ok(UseTree2::Name(UseName2 { ident, generics }))
}
} else if lookahead.peek(syn::token::Brace) {
let content;
let brace_token = syn::braced!(content in input);
let items = content.parse_terminated(UseTree2::parse, syn::Token![,])?;
Ok(UseTree2::Group(UseGroup2 { brace_token, items }))
} else {
Err(lookahead.error())
}
}
}

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"06b4907a2ab2cd6418fe91777edd5b336ab8bf31c8d9420baaa1d064f4885be2","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","src/lib.rs":"a4d6dc0b000d483e77c10bb8bdf0c36f906aaec35dbe7649150bbf9648221f65"},"package":"053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"}

View File

@@ -0,0 +1,65 @@
# 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.70"
name = "windows-interface"
version = "0.58.0"
authors = ["Microsoft"]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "The interface macro for the windows crate"
readme = false
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 = []
[lib]
name = "windows_interface"
path = "src/lib.rs"
proc-macro = true
[dependencies.proc-macro2]
version = "1.0"
[dependencies.quote]
version = "1.0"
[dependencies.syn]
version = "2.0"
features = [
"parsing",
"proc-macro",
"printing",
"full",
"derive",
"clone-impls",
]
default-features = false
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,760 @@
/*!
Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
*/
use quote::quote;
use syn::spanned::Spanned;
/// Defines a COM interface to call or implement.
///
/// # Example
/// ```rust,ignore
/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")]
/// unsafe trait IValue: IUnknown {
/// fn GetValue(&self, value: *mut i32) -> HRESULT;
/// }
///
/// #[implement(IValue)]
/// struct Value(i32);
///
/// impl IValue_Impl for Value {
/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT {
/// *value = self.0;
/// HRESULT(0)
/// }
/// }
///
/// fn main() {
/// let object: IValue = Value(123).into();
/// // Call interface methods...
/// }
/// ```
#[proc_macro_attribute]
pub fn interface(
attributes: proc_macro::TokenStream,
original_type: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let guid = syn::parse_macro_input!(attributes as Guid);
let interface = syn::parse_macro_input!(original_type as Interface);
let tokens = match interface.gen_tokens(&guid) {
Ok(t) => t,
Err(e) => return e.to_compile_error().into(),
};
tokens.into()
}
macro_rules! bail {
($item:expr, $($msg:tt),*) => {
return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*))));
};
}
macro_rules! unexpected_token {
($item:expr, $msg:expr) => {
if let Some(i) = $item {
bail!(i, "unexpected {}", $msg);
}
};
}
macro_rules! expected_token {
($sig:tt.$item:tt(), $msg:expr) => {
if let None = $sig.$item() {
bail!($sig, "expected {}", $msg);
}
};
}
/// Parsed interface
///
/// ```rust,ignore
/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
/// unsafe trait IUIAnimationVariable: IUnknown {
/// //^ parses this
/// fn GetValue(&self, value: *mut f64) -> HRESULT;
/// }
/// ```
struct Interface {
visibility: syn::Visibility,
name: syn::Ident,
parent: Option<syn::Path>,
methods: Vec<InterfaceMethod>,
docs: Vec<syn::Attribute>,
}
impl Interface {
/// Generates all the code needed for a COM interface
fn gen_tokens(&self, guid: &Guid) -> syn::Result<proc_macro2::TokenStream> {
let vis = &self.visibility;
let name = &self.name;
let docs = &self.docs;
let parent = self.parent_type();
let vtable_name = quote::format_ident!("{}_Vtbl", name);
let guid = guid.to_tokens()?;
let implementation = self.gen_implementation();
let com_trait = self.get_com_trait();
let vtable = self.gen_vtable(&vtable_name);
let conversions = self.gen_conversions();
Ok(quote! {
#[repr(transparent)]
#(#docs)*
#vis struct #name(#parent);
#implementation
unsafe impl ::windows_core::Interface for #name {
type Vtable = #vtable_name;
const IID: ::windows_core::GUID = #guid;
}
impl ::windows_core::RuntimeName for #name {}
impl ::core::ops::Deref for #name {
type Target = #parent;
fn deref(&self) -> &Self::Target {
unsafe { ::core::mem::transmute(self) }
}
}
#com_trait
#vtable
#conversions
})
}
/// Generates the methods users can call on the COM interface pointer
fn gen_implementation(&self) -> proc_macro2::TokenStream {
let name = &self.name;
let methods = self
.methods
.iter()
.map(|m| {
let vis = &m.visibility;
let name = &m.name;
let generics = m.gen_consume_generics();
let params = m.gen_consume_params();
let args = m.gen_consume_args();
let ret = &m.ret;
if m.is_result() {
quote! {
#[inline(always)]
#vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
(::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*).ok()
}
}
} else {
quote! {
#[inline(always)]
#vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
(::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*)
}
}
}
})
.collect::<Vec<_>>();
quote! {
impl #name {
#(#methods)*
}
}
}
fn get_com_trait(&self) -> proc_macro2::TokenStream {
let name = quote::format_ident!("{}_Impl", self.name);
let vis = &self.visibility;
let methods = self
.methods
.iter()
.map(|m| {
let name = &m.name;
let docs = &m.docs;
let args = m.gen_args();
let ret = &m.ret;
quote! {
#(#docs)*
unsafe fn #name(&self, #(#args),*) #ret;
}
})
.collect::<Vec<_>>();
let parent = self.parent_trait_constraint();
quote! {
#[allow(non_camel_case_types)]
#vis trait #name: Sized + #parent {
#(#methods)*
}
}
}
/// Generates the vtable for a COM interface
fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream {
let vis = &self.visibility;
let name = &self.name;
let trait_name = quote::format_ident!("{}_Impl", name);
let implvtbl_name = quote::format_ident!("{}_ImplVtbl", name);
let vtable_entries = self
.methods
.iter()
.map(|m| {
let name = &m.name;
let ret = &m.ret;
let args = m.gen_args();
if m.is_result() {
quote! {
pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows_core::HRESULT,
}
} else {
quote! {
pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret,
}
}
})
.collect::<Vec<_>>();
let parent_vtable_generics = quote!(Identity, OFFSET);
let parent_vtable = self.parent_vtable();
// or_parent_matches will be `|| parent::matches(iid)` if this interface inherits from another
// interface (except for IUnknown) or will be empty if this is not applicable. This is what allows
// QueryInterface to work correctly for all interfaces in an inheritance chain, e.g.
// IFoo3 derives from IFoo2 derives from IFoo.
//
// We avoid matching IUnknown because object identity depends on the uniqueness of the IUnknown pointer.
let or_parent_matches = match parent_vtable.as_ref() {
Some(parent) if !self.parent_is_iunknown() => quote! (|| <#parent>::matches(iid)),
_ => quote!(),
};
let functions = self
.methods
.iter()
.map(|m| {
let name = &m.name;
let args = m.gen_args();
let params = &m
.args
.iter()
.map(|a| {
let pat = &a.pat;
quote! { #pat }
})
.collect::<Vec<_>>();
let ret = &m.ret;
let ret = if m.is_result() {
quote! { -> ::windows_core::HRESULT }
} else {
quote! { #ret }
};
if parent_vtable.is_some() {
quote! {
unsafe extern "system" fn #name<
Identity: ::windows_core::IUnknownImpl,
const OFFSET: isize
>(
this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl.
#(#args),*
) #ret
where
Identity : #trait_name
{
// This step is essentially a virtual dispatch adjustor thunk. Its purpose is to adjust
// the "this" pointer from the address used by the COM interface to the root of the
// MyApp_Impl object. Since a given MyApp_Impl may implement more than one COM interface
// (and more than one COM interface chain), we need to know how to get from COM's "this"
// back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units).
let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity);
// Last, we invoke the implementation function.
// We use explicit <Impl as IFoo_Impl> so that we can select the correct method
// for situations where IFoo3 derives from IFoo2 and both declare a method with
// the same name.
<Identity as #trait_name>::#name(this_outer, #(#params),*).into()
}
}
} else {
quote! {
unsafe extern "system" fn #name<Impl: #trait_name>(this: *mut ::core::ffi::c_void, #(#args),*) #ret {
let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap;
let this = (*this).this as *const Impl;
(*this).#name(#(#params),*).into()
}
}
}
})
.collect::<Vec<_>>();
if let Some(parent_vtable) = parent_vtable {
let entries = self
.methods
.iter()
.map(|m| {
let name = &m.name;
quote!(#name: #name::<Identity, OFFSET>)
})
.collect::<Vec<_>>();
quote! {
#[repr(C)]
#[doc(hidden)]
#vis struct #vtable_name {
pub base__: #parent_vtable,
#(#vtable_entries)*
}
impl #vtable_name {
pub const fn new<
Identity: ::windows_core::IUnknownImpl,
const OFFSET: isize,
>() -> Self
where
Identity : #trait_name
{
#(#functions)*
Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* }
}
#[inline(always)]
pub fn matches(iid: &::windows_core::GUID) -> bool {
*iid == <#name as ::windows_core::Interface>::IID
#or_parent_matches
}
}
}
} else {
let entries = self
.methods
.iter()
.map(|m| {
let name = &m.name;
quote!(#name: #name::<Impl>)
})
.collect::<Vec<_>>();
quote! {
#[repr(C)]
#[doc(hidden)]
#vis struct #vtable_name {
#(#vtable_entries)*
}
impl #vtable_name {
pub const fn new<Impl: #trait_name>() -> Self {
#(#functions)*
Self { #(#entries),* }
}
}
struct #implvtbl_name<T: #trait_name> (::core::marker::PhantomData<T>);
impl<T: #trait_name> #implvtbl_name<T> {
const VTABLE: #vtable_name = #vtable_name::new::<T>();
}
impl #name {
fn new<'a, T: #trait_name>(this: &'a T) -> ::windows_core::ScopedInterface<'a, #name> {
let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_name::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ };
let this = ::core::mem::ManuallyDrop::new(::windows_core::imp::Box::new(this));
unsafe { ::windows_core::ScopedInterface::new(::core::mem::transmute(&this.vtable)) }
}
}
}
}
}
/// Generates various conversions such as from and to `IUnknown`
fn gen_conversions(&self) -> proc_macro2::TokenStream {
let name = &self.name;
let name_string = format!("{name}");
quote! {
impl ::core::convert::From<#name> for ::windows_core::IUnknown {
fn from(value: #name) -> Self {
unsafe { ::core::mem::transmute(value) }
}
}
impl ::core::convert::From<&#name> for ::windows_core::IUnknown {
fn from(value: &#name) -> Self {
::core::convert::From::from(::core::clone::Clone::clone(value))
}
}
impl ::core::clone::Clone for #name {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl ::core::cmp::PartialEq for #name {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl ::core::cmp::Eq for #name {}
impl ::core::fmt::Debug for #name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_tuple(#name_string).field(&::windows_core::Interface::as_raw(self)).finish()
}
}
}
}
fn parent_type(&self) -> proc_macro2::TokenStream {
if let Some(parent) = &self.parent {
quote!(#parent)
} else {
quote!(::core::ptr::NonNull<::core::ffi::c_void>)
}
}
fn parent_vtable(&self) -> Option<proc_macro2::TokenStream> {
if let Some((ident, path)) = self.parent_path().split_last() {
let ident = quote::format_ident!("{}_Vtbl", ident);
Some(quote! { #(#path::)* #ident })
} else {
None
}
}
fn parent_is_iunknown(&self) -> bool {
if let Some(ident) = self.parent_path().last() {
ident == "IUnknown"
} else {
false
}
}
fn parent_path(&self) -> Vec<syn::Ident> {
if let Some(parent) = &self.parent {
parent
.segments
.iter()
.map(|segment| segment.ident.clone())
.collect()
} else {
vec![]
}
}
/// Gets the parent trait constrait which is nothing if the parent is IUnknown
fn parent_trait_constraint(&self) -> proc_macro2::TokenStream {
if let Some((ident, path)) = self.parent_path().split_last() {
if ident != "IUnknown" {
let ident = quote::format_ident!("{}_Impl", ident);
return quote! { #(#path::)* #ident };
}
}
quote! {}
}
}
impl syn::parse::Parse for Interface {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attributes = input.call(syn::Attribute::parse_outer)?;
let mut docs = Vec::new();
for attr in attributes.into_iter() {
let path = attr.path();
if path.is_ident("doc") {
docs.push(attr);
} else {
return Err(syn::Error::new(path.span(), "Unrecognized attribute "));
}
}
let visibility = input.parse::<syn::Visibility>()?;
_ = input.parse::<syn::Token![unsafe]>()?;
_ = input.parse::<syn::Token![trait]>()?;
let name = input.parse::<syn::Ident>()?;
_ = input.parse::<syn::Token![:]>();
let parent = input.parse::<syn::Path>().ok();
let content;
syn::braced!(content in input);
let mut methods = Vec::new();
while !content.is_empty() {
methods.push(content.parse::<InterfaceMethod>()?);
}
Ok(Self {
visibility,
methods,
name,
parent,
docs,
})
}
}
/// Parsed interface guid attribute
///
/// ```rust,ignore
/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
/// //^ parses this
/// unsafe trait IUIAnimationVariable: IUnknown {
/// fn GetValue(&self, value: *mut f64) -> HRESULT;
/// }
/// ```
struct Guid(Option<syn::LitStr>);
impl Guid {
fn to_tokens(&self) -> syn::Result<proc_macro2::TokenStream> {
fn hex_lit(num: &str) -> syn::LitInt {
syn::LitInt::new(&format!("0x{num}"), proc_macro2::Span::call_site())
}
fn ensure_length(
part: Option<&str>,
index: usize,
length: usize,
span: proc_macro2::Span,
) -> syn::Result<String> {
let part = match part {
Some(p) => p,
None => {
return Err(syn::Error::new(
span,
format!("The IID missing part at index {index}"),
))
}
};
if part.len() != length {
return Err(syn::Error::new(
span,
format!(
"The IID part at index {} must be {} characters long but was {} characters",
index,
length,
part.len()
),
));
}
Ok(part.to_owned())
}
if let Some(value) = &self.0 {
let guid_value = value.value();
let mut delimited = guid_value.split('-').fuse();
let chunks = [
ensure_length(delimited.next(), 0, 8, value.span())?,
ensure_length(delimited.next(), 1, 4, value.span())?,
ensure_length(delimited.next(), 2, 4, value.span())?,
ensure_length(delimited.next(), 3, 4, value.span())?,
ensure_length(delimited.next(), 4, 12, value.span())?,
];
let data1 = hex_lit(&chunks[0]);
let data2 = hex_lit(&chunks[1]);
let data3 = hex_lit(&chunks[2]);
let (data4_1, data4_2) = chunks[3].split_at(2);
let data4_1 = hex_lit(data4_1);
let data4_2 = hex_lit(data4_2);
let (data4_3, rest) = chunks[4].split_at(2);
let data4_3 = hex_lit(data4_3);
let (data4_4, rest) = rest.split_at(2);
let data4_4 = hex_lit(data4_4);
let (data4_5, rest) = rest.split_at(2);
let data4_5 = hex_lit(data4_5);
let (data4_6, rest) = rest.split_at(2);
let data4_6 = hex_lit(data4_6);
let (data4_7, data4_8) = rest.split_at(2);
let data4_7 = hex_lit(data4_7);
let data4_8 = hex_lit(data4_8);
Ok(quote! {
::windows_core::GUID {
data1: #data1,
data2: #data2,
data3: #data3,
data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8]
}
})
} else {
Ok(quote! {
::windows_core::GUID::zeroed()
})
}
}
}
impl syn::parse::Parse for Guid {
fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let string: Option<syn::LitStr> = cursor.parse().ok();
Ok(Self(string))
}
}
/// A parsed interface method
///
/// ```rust,ignore
/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")]
/// unsafe trait IUIAnimationVariable: IUnknown {
/// fn GetValue(&self, value: *mut f64) -> HRESULT;
/// //^ parses this
/// }
/// ```
struct InterfaceMethod {
pub name: syn::Ident,
pub visibility: syn::Visibility,
pub args: Vec<InterfaceMethodArg>,
pub ret: syn::ReturnType,
pub docs: Vec<syn::Attribute>,
}
impl InterfaceMethod {
fn is_result(&self) -> bool {
if let syn::ReturnType::Type(_, ty) = &self.ret {
if let syn::Type::Path(path) = &**ty {
if let Some(segment) = path.path.segments.last() {
let ident = segment.ident.to_string();
if ident == "Result" {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if args.args.len() == 1 {
return true;
}
}
}
}
}
}
false
}
/// Generates arguments (of the form `$pat: $type`)
fn gen_args(&self) -> Vec<proc_macro2::TokenStream> {
self.args
.iter()
.map(|a| {
let pat = &a.pat;
let ty = &a.ty;
quote! { #pat: #ty }
})
.collect::<Vec<_>>()
}
fn gen_consume_generics(&self) -> Vec<proc_macro2::TokenStream> {
self.args
.iter()
.enumerate()
.filter_map(|(generic_index, a)| {
if let Some((ty, ident)) = a.borrow_type() {
let generic_ident = quote::format_ident!("P{generic_index}");
if ident == "Ref" {
Some(quote! { #generic_ident: ::windows_core::Param<#ty> })
} else {
Some(quote! { #generic_ident: ::windows_core::OutParam<#ty> })
}
} else {
None
}
})
.collect::<Vec<_>>()
}
fn gen_consume_params(&self) -> Vec<proc_macro2::TokenStream> {
self.args
.iter()
.enumerate()
.map(|(generic_index, a)| {
let pat = &a.pat;
if a.borrow_type().is_some() {
let generic_ident = quote::format_ident!("P{generic_index}");
quote! { #pat: #generic_ident }
} else {
let ty = &a.ty;
quote! { #pat: #ty }
}
})
.collect::<Vec<_>>()
}
fn gen_consume_args(&self) -> Vec<proc_macro2::TokenStream> {
self.args
.iter()
.map(|a| {
let pat = &a.pat;
if let Some((_, ident)) = a.borrow_type() {
if ident == "Ref" {
quote! { #pat.param().borrow() }
} else {
quote! { #pat.borrow_mut() }
}
} else {
quote! { #pat }
}
})
.collect::<Vec<_>>()
}
}
impl syn::parse::Parse for InterfaceMethod {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let docs = input.call(syn::Attribute::parse_outer)?;
let visibility = input.parse::<syn::Visibility>()?;
let method = input.parse::<syn::TraitItemFn>()?;
unexpected_token!(docs.iter().find(|a| !a.path().is_ident("doc")), "attribute");
unexpected_token!(method.default, "default method implementation");
let sig = method.sig;
unexpected_token!(sig.abi, "abi declaration");
unexpected_token!(sig.asyncness, "async declaration");
unexpected_token!(sig.generics.params.iter().next(), "generics declaration");
unexpected_token!(sig.constness, "const declaration");
expected_token!(
sig.receiver(),
"the method to have &self as its first argument"
);
unexpected_token!(sig.variadic, "variadic args");
let args = sig
.inputs
.into_iter()
.filter_map(|a| match a {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(p) => Some(p),
})
.map(|p| {
Ok(InterfaceMethodArg {
ty: p.ty,
pat: p.pat,
})
})
.collect::<Result<Vec<InterfaceMethodArg>, syn::Error>>()?;
let ret = sig.output;
Ok(InterfaceMethod {
name: sig.ident,
visibility,
args,
ret,
docs,
})
}
}
/// An argument to an interface method
struct InterfaceMethodArg {
/// The type of the argument
pub ty: Box<syn::Type>,
/// The name of the argument
pub pat: Box<syn::Pat>,
}
impl InterfaceMethodArg {
fn borrow_type(&self) -> Option<(syn::Type, String)> {
if let syn::Type::Path(path) = &*self.ty {
if let Some(segment) = path.path.segments.last() {
let ident = segment.ident.to_string();
if matches!(ident.as_str(), "Ref" | "OutRef") {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if args.args.len() == 1 {
if let Some(syn::GenericArgument::Type(ty)) = args.args.first() {
return Some((ty.clone(), ident));
}
}
}
}
}
}
None
}
}

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"5b207ca32f201bf44d5049ffdf90114c161bf103a2c79564e471c36d001565e1","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"0b5595048105c4e66cbc8ce38a551a0c7f28c0c4c69670e696a32df0e5aec9f5","src/bindings.rs":"13e2ec0239e501aae80277cbcdc666a1a671b41b8a4ef91a07f9cb56bd490ecb","src/bstr.rs":"651542aaaa71c11eb129263fe1966f592823ce5717ee937aa7ae23f6b0e45b91","src/com.rs":"3111c836c9a2cabcf01b06fc65af474a1e071d9c887db178105c2b52ec78bdd5","src/error.rs":"6c3d05bb2177791bff427eeaea204462202bb3ed2dc8727babdfefc4f1b3e84f","src/hresult.rs":"2a0f9f532e5e715d316735f626c1cf9e895ca1cc0aa65fedeeb01953028c4573","src/lib.rs":"985e3b67988b97ddd0358189a3d1ac9ea346b067787fae981ac3166ce04b2674","src/strings.rs":"fba5cda8e1d5ce430cfff8f9195a5cc4d7f880262715469583e8ec835a51ea8a"},"package":"1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"}

View File

@@ -0,0 +1,54 @@
# 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.60"
name = "windows-result"
version = "0.2.0"
authors = ["Microsoft"]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Windows error handling"
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 = []
[lib]
name = "windows_result"
path = "src/lib.rs"
[dependencies.windows-targets]
version = "0.52.6"
[features]
default = ["std"]
std = []
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,32 @@
## Windows error handling
The [windows-result](https://crates.io/crates/windows-result) crate provides efficient Windows error handling and propagation with support for Win32, COM, and WinRT APIs.
* [Getting started](https://kennykerr.ca/rust-getting-started/)
* [Samples](https://github.com/microsoft/windows-rs/tree/0.58.0/crates/samples)
* [Releases](https://github.com/microsoft/windows-rs/releases)
Start by adding the following to your Cargo.toml file:
```toml
[dependencies.windows-result]
version = "0.2"
```
Use the `HRESULT`, `Error`, and specialized `Result` types as needed:
```rust
use windows_result::*;
const S_OK: HRESULT = HRESULT(0);
const ERROR_CANCELLED: u32 = 1223;
const E_CANCELLED: HRESULT = HRESULT::from_win32(ERROR_CANCELLED);
fn main() -> Result<()> {
S_OK.ok()?;
let e = Error::new(E_CANCELLED, "test message");
assert_eq!(e.code(), E_CANCELLED);
assert_eq!(e.message(), "test message");
Ok(())
}
```

View File

@@ -0,0 +1,89 @@
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
dead_code,
clippy::all
)]
windows_targets::link!("api-ms-win-core-winrt-error-l1-1-0.dll" "system" fn RoOriginateErrorW(error : HRESULT, cchmax : u32, message : PCWSTR) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const core::ffi::c_void) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE);
windows_targets::link!("oleaut32.dll" "system" fn GetErrorInfo(dwreserved : u32, pperrinfo : *mut * mut core::ffi::c_void) -> HRESULT);
windows_targets::link!("oleaut32.dll" "system" fn SetErrorInfo(dwreserved : u32, perrinfo : * mut core::ffi::c_void) -> HRESULT);
windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR));
windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
pub type BOOL = i32;
pub type BSTR = *const u16;
pub const ERROR_INVALID_DATA: WIN32_ERROR = 13u32;
pub const ERROR_NO_UNICODE_TRANSLATION: WIN32_ERROR = 1113u32;
pub const E_UNEXPECTED: HRESULT = 0x8000FFFF_u32 as _;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32;
pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32;
pub const FORMAT_MESSAGE_FROM_SYSTEM: FORMAT_MESSAGE_OPTIONS = 4096u32;
pub const FORMAT_MESSAGE_IGNORE_INSERTS: FORMAT_MESSAGE_OPTIONS = 512u32;
pub type FORMAT_MESSAGE_OPTIONS = u32;
#[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 HEAP_FLAGS = u32;
pub type HMODULE = *mut core::ffi::c_void;
pub type HRESULT = i32;
pub const IID_IErrorInfo: GUID = GUID::from_u128(0x1cf2b120_547d_101b_8e65_08002b2bd119);
#[repr(C)]
pub struct IErrorInfo_Vtbl {
pub base__: IUnknown_Vtbl,
pub GetGUID: unsafe extern "system" fn(*mut core::ffi::c_void, *mut GUID) -> HRESULT,
pub GetSource: unsafe extern "system" fn(*mut core::ffi::c_void, *mut BSTR) -> HRESULT,
pub GetDescription: unsafe extern "system" fn(*mut core::ffi::c_void, *mut BSTR) -> HRESULT,
pub GetHelpFile: unsafe extern "system" fn(*mut core::ffi::c_void, *mut BSTR) -> HRESULT,
pub GetHelpContext: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> HRESULT,
}
pub const IID_IRestrictedErrorInfo: GUID = GUID::from_u128(0x82ba7092_4c88_427d_a7bc_16dd93feb67e);
#[repr(C)]
pub struct IRestrictedErrorInfo_Vtbl {
pub base__: IUnknown_Vtbl,
pub GetErrorDetails: unsafe extern "system" fn(
*mut core::ffi::c_void,
*mut BSTR,
*mut HRESULT,
*mut BSTR,
*mut BSTR,
) -> HRESULT,
pub GetReference: unsafe extern "system" fn(*mut core::ffi::c_void, *mut BSTR) -> HRESULT,
}
pub const IID_IUnknown: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046);
#[repr(C)]
pub struct IUnknown_Vtbl {
pub QueryInterface: unsafe extern "system" fn(
this: *mut core::ffi::c_void,
iid: *const GUID,
interface: *mut *mut core::ffi::c_void,
) -> HRESULT,
pub AddRef: unsafe extern "system" fn(this: *mut core::ffi::c_void) -> u32,
pub Release: unsafe extern "system" fn(this: *mut core::ffi::c_void) -> u32,
}
pub type LOAD_LIBRARY_FLAGS = u32;
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: LOAD_LIBRARY_FLAGS = 4096u32;
pub type PCSTR = *const u8;
pub type PCWSTR = *const u16;
pub type PWSTR = *mut u16;
pub type WIN32_ERROR = u32;

View File

@@ -0,0 +1,50 @@
use super::*;
#[repr(transparent)]
pub struct BasicString(*const u16);
impl BasicString {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
if self.0.is_null() {
0
} else {
unsafe { SysStringLen(self.0) as usize }
}
}
pub fn as_wide(&self) -> &[u16] {
let len = self.len();
if len != 0 {
unsafe { core::slice::from_raw_parts(self.as_ptr(), len) }
} else {
&[]
}
}
pub fn as_ptr(&self) -> *const u16 {
if !self.is_empty() {
self.0
} else {
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
}
}
}
impl Default for BasicString {
fn default() -> Self {
Self(core::ptr::null_mut())
}
}
impl Drop for BasicString {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { SysFreeString(self.0) }
}
}
}

View File

@@ -0,0 +1,54 @@
use super::*;
#[doc(hidden)]
#[macro_export]
macro_rules! com_call {
($vtbl:ty, $this:ident.$method:ident($($args:tt)*)) => {
((&**($this.as_raw() as *mut *mut $vtbl)).$method)($this.as_raw(), $($args)*)
}
}
#[repr(transparent)]
pub struct ComPtr(core::ptr::NonNull<core::ffi::c_void>);
impl ComPtr {
pub fn as_raw(&self) -> *mut core::ffi::c_void {
unsafe { core::mem::transmute_copy(self) }
}
pub fn cast(&self, iid: &GUID) -> Option<Self> {
let mut result = None;
unsafe {
com_call!(
IUnknown_Vtbl,
self.QueryInterface(iid, &mut result as *mut _ as _)
);
}
result
}
}
impl PartialEq for ComPtr {
fn eq(&self, other: &Self) -> bool {
self.cast(&IID_IUnknown).unwrap().0 == other.cast(&IID_IUnknown).unwrap().0
}
}
impl Eq for ComPtr {}
impl Clone for ComPtr {
fn clone(&self) -> Self {
unsafe {
com_call!(IUnknown_Vtbl, self.AddRef());
}
Self(self.0)
}
}
impl Drop for ComPtr {
fn drop(&mut self) {
unsafe {
com_call!(IUnknown_Vtbl, self.Release());
}
}
}

View File

@@ -0,0 +1,396 @@
use super::*;
use core::num::NonZeroI32;
#[allow(unused_imports)]
use core::mem::size_of;
/// An error object consists of both an error code and optional detailed error information for debugging.
///
/// # Extended error info and the `windows_slim_errors` configuration option
///
/// `Error` contains an [`HRESULT`] value that describes the error, as well as an optional
/// `IErrorInfo` COM object. The `IErrorInfo` object is a COM object that can provide detailed information
/// about an error, such as a text string, a `ProgID` of the originator, etc. If the error object
/// was originated in an WinRT component, then additional information such as a stack track may be
/// captured.
///
/// However, many systems based on COM do not use `IErrorInfo`. For these systems, the optional error
/// info within `Error` has no benefits, but has substantial costs because it increases the size of
/// the `Error` object, which also increases the size of `Result<T>`.
///
/// This error information can be disabled at compile time by setting `RUSTFLAGS=--cfg=windows_slim_errors`.
/// This removes the `IErrorInfo` support within the [`Error`] type, which has these benefits:
///
/// * It reduces the size of [`Error`] to 4 bytes (the size of [`HRESULT`]).
///
/// * It reduces the size of `Result<(), Error>` to 4 bytes, allowing it to be returned in a single
/// machine register.
///
/// * The `Error` (and `Result<T, Error>`) types no longer have a [`Drop`] impl. This removes the need
/// for lifetime checking and running drop code when [`Error`] and [`Result`] go out of scope. This
/// significantly reduces code size for codebase that make extensive use of [`Error`].
///
/// Of course, these benefits come with a cost; you lose extended error information for those
/// COM objects that support it.
///
/// This is controlled by a `--cfg` option rather than a Cargo feature because this compilation
/// option sets a policy that applies to an entire graph of crates. Individual crates that take a
/// dependency on the `windows-result` crate are not in a good position to decide whether they want
/// slim errors or full errors. Cargo features are meant to be additive, but specifying the size
/// and contents of `Error` is not a feature so much as a whole-program policy decision.
///
/// # References
///
/// * [`IErrorInfo`](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-ierrorinfo)
#[derive(Clone)]
pub struct Error {
/// The `HRESULT` error code, but represented using [`NonZeroI32`]. [`NonZeroI32`] provides
/// a "niche" to the Rust compiler, which is a space-saving optimization. This allows the
/// compiler to use more compact representation for enum variants (such as [`Result`]) that
/// contain instances of [`Error`].
code: NonZeroI32,
/// Contains details about the error, such as error text.
info: ErrorInfo,
}
/// We remap S_OK to this error because the S_OK representation (zero) is reserved for niche
/// optimizations.
const S_EMPTY_ERROR: NonZeroI32 = const_nonzero_i32(u32::from_be_bytes(*b"S_OK") as i32);
/// Converts an HRESULT into a NonZeroI32. If the input is S_OK (zero), then this is converted to
/// S_EMPTY_ERROR. This is necessary because NonZeroI32, as the name implies, cannot represent the
/// value zero. So we remap it to a value no one should be using, during storage.
const fn const_nonzero_i32(i: i32) -> NonZeroI32 {
if let Some(nz) = NonZeroI32::new(i) {
nz
} else {
panic!();
}
}
fn nonzero_hresult(hr: HRESULT) -> NonZeroI32 {
if let Some(nz) = NonZeroI32::new(hr.0) {
nz
} else {
S_EMPTY_ERROR
}
}
impl Error {
/// Creates an error object without any failure information.
pub const fn empty() -> Self {
Self {
code: S_EMPTY_ERROR,
info: ErrorInfo::empty(),
}
}
/// Creates a new error object, capturing the stack and other information about the
/// point of failure.
pub fn new<T: AsRef<str>>(code: HRESULT, message: T) -> Self {
#[cfg(windows)]
{
let message: &str = message.as_ref();
if message.is_empty() {
Self::from_hresult(code)
} else {
ErrorInfo::originate_error(code, message);
code.into()
}
}
#[cfg(not(windows))]
{
let _ = message;
Self::from_hresult(code)
}
}
/// Creates a new error object with an error code, but without additional error information.
pub fn from_hresult(code: HRESULT) -> Self {
Self {
code: nonzero_hresult(code),
info: ErrorInfo::empty(),
}
}
/// Creates a new `Error` from the Win32 error code returned by `GetLastError()`.
pub fn from_win32() -> Self {
#[cfg(windows)]
{
let error = unsafe { GetLastError() };
Self::from_hresult(HRESULT::from_win32(error))
}
#[cfg(not(windows))]
{
unimplemented!()
}
}
/// The error code describing the error.
pub const fn code(&self) -> HRESULT {
if self.code.get() == S_EMPTY_ERROR.get() {
HRESULT(0)
} else {
HRESULT(self.code.get())
}
}
/// The error message describing the error.
pub fn message(&self) -> String {
if let Some(message) = self.info.message() {
return message;
}
// Otherwise fallback to a generic error code description.
self.code().message()
}
/// The error object describing the error.
#[cfg(windows)]
pub fn as_ptr(&self) -> *mut core::ffi::c_void {
self.info.as_ptr()
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl From<Error> for HRESULT {
fn from(error: Error) -> Self {
let code = error.code();
error.info.into_thread();
code
}
}
impl From<HRESULT> for Error {
fn from(code: HRESULT) -> Self {
Self {
code: nonzero_hresult(code),
info: ErrorInfo::from_thread(),
}
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(from: Error) -> Self {
Self::from_raw_os_error(from.code().0)
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(from: std::io::Error) -> Self {
match from.raw_os_error() {
Some(status) => HRESULT::from_win32(status as u32).into(),
None => HRESULT(E_UNEXPECTED).into(),
}
}
}
impl From<alloc::string::FromUtf16Error> for Error {
fn from(_: alloc::string::FromUtf16Error) -> Self {
Self::from_hresult(HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION))
}
}
impl From<alloc::string::FromUtf8Error> for Error {
fn from(_: alloc::string::FromUtf8Error) -> Self {
Self::from_hresult(HRESULT::from_win32(ERROR_NO_UNICODE_TRANSLATION))
}
}
impl From<core::num::TryFromIntError> for Error {
fn from(_: core::num::TryFromIntError) -> Self {
Self::from_hresult(HRESULT::from_win32(ERROR_INVALID_DATA))
}
}
impl core::fmt::Debug for Error {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug = fmt.debug_struct("Error");
debug
.field("code", &self.code())
.field("message", &self.message())
.finish()
}
}
impl core::fmt::Display for Error {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let message = self.message();
if message.is_empty() {
core::write!(fmt, "{}", self.code())
} else {
core::write!(fmt, "{} ({})", self.message(), self.code())
}
}
}
impl core::hash::Hash for Error {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.code.hash(state);
// We do not hash the error info.
}
}
// Equality tests only the HRESULT, not the error info (if any).
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
self.code == other.code
}
}
impl Eq for Error {}
impl PartialOrd for Error {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Error {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.code.cmp(&other.code)
}
}
use error_info::*;
#[cfg(all(windows, not(windows_slim_errors)))]
mod error_info {
use super::*;
use crate::com::ComPtr;
/// This type stores error detail, represented by a COM `IErrorInfo` object.
///
/// # References
///
/// * [`IErrorInfo`](https://learn.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-ierrorinfo)
#[derive(Clone, Default)]
pub(crate) struct ErrorInfo {
pub(super) ptr: Option<ComPtr>,
}
impl ErrorInfo {
pub(crate) const fn empty() -> Self {
Self { ptr: None }
}
pub(crate) fn from_thread() -> Self {
unsafe {
let mut ptr = core::mem::MaybeUninit::zeroed();
crate::bindings::GetErrorInfo(0, ptr.as_mut_ptr() as *mut _);
Self {
ptr: ptr.assume_init(),
}
}
}
pub(crate) fn into_thread(self) {
if let Some(ptr) = self.ptr {
unsafe {
crate::bindings::SetErrorInfo(0, ptr.as_raw());
}
}
}
pub(crate) fn originate_error(code: HRESULT, message: &str) {
let message: Vec<_> = message.encode_utf16().collect();
unsafe {
RoOriginateErrorW(code.0, message.len() as u32, message.as_ptr());
}
}
pub(crate) fn message(&self) -> Option<String> {
use crate::bstr::BasicString;
let ptr = self.ptr.as_ref()?;
let mut message = BasicString::default();
// First attempt to retrieve the restricted error information.
if let Some(info) = ptr.cast(&IID_IRestrictedErrorInfo) {
let mut fallback = BasicString::default();
let mut code = 0;
unsafe {
com_call!(
IRestrictedErrorInfo_Vtbl,
info.GetErrorDetails(
&mut fallback as *mut _ as _,
&mut code,
&mut message as *mut _ as _,
&mut BasicString::default() as *mut _ as _
)
);
}
if message.is_empty() {
message = fallback
};
}
// Next attempt to retrieve the regular error information.
if message.is_empty() {
unsafe {
com_call!(
IErrorInfo_Vtbl,
ptr.GetDescription(&mut message as *mut _ as _)
);
}
}
Some(String::from_utf16_lossy(wide_trim_end(message.as_wide())))
}
pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
if let Some(info) = self.ptr.as_ref() {
info.as_raw()
} else {
core::ptr::null_mut()
}
}
}
unsafe impl Send for ErrorInfo {}
unsafe impl Sync for ErrorInfo {}
}
#[cfg(not(all(windows, not(windows_slim_errors))))]
mod error_info {
use super::*;
// We use this name so that the NatVis <Type> element for ErrorInfo does *not* match this type.
// This prevents the NatVis description from failing to load.
#[derive(Clone, Default)]
pub(crate) struct EmptyErrorInfo;
pub(crate) use EmptyErrorInfo as ErrorInfo;
impl EmptyErrorInfo {
pub(crate) const fn empty() -> Self {
Self
}
pub(crate) fn from_thread() -> Self {
Self
}
pub(crate) fn into_thread(self) {}
#[cfg(windows)]
pub(crate) fn originate_error(_code: HRESULT, _message: &str) {}
pub(crate) fn message(&self) -> Option<String> {
None
}
#[cfg(windows)]
pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
core::ptr::null_mut()
}
}
}

View File

@@ -0,0 +1,154 @@
use super::*;
/// An error code value returned by most COM functions.
#[repr(transparent)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[must_use]
#[allow(non_camel_case_types)]
pub struct HRESULT(pub i32);
impl HRESULT {
/// Returns [`true`] if `self` is a success code.
#[inline]
pub const fn is_ok(self) -> bool {
self.0 >= 0
}
/// Returns [`true`] if `self` is a failure code.
#[inline]
pub const fn is_err(self) -> bool {
!self.is_ok()
}
/// Asserts that `self` is a success code.
///
/// This will invoke the [`panic!`] macro if `self` is a failure code and display
/// the [`HRESULT`] value for diagnostics.
#[inline]
#[track_caller]
pub fn unwrap(self) {
assert!(self.is_ok(), "HRESULT 0x{:X}", self.0);
}
/// Converts the [`HRESULT`] to [`Result<()>`][Result<_>].
#[inline]
pub fn ok(self) -> Result<()> {
if self.is_ok() {
Ok(())
} else {
Err(self.into())
}
}
/// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`]
/// converted to [`Result<T>`].
#[inline]
pub fn map<F, T>(self, op: F) -> Result<T>
where
F: FnOnce() -> T,
{
self.ok()?;
Ok(op())
}
/// Calls `op` if `self` is a success code, otherwise returns [`HRESULT`]
/// converted to [`Result<T>`].
#[inline]
pub fn and_then<F, T>(self, op: F) -> Result<T>
where
F: FnOnce() -> Result<T>,
{
self.ok()?;
op()
}
/// The error message describing the error.
pub fn message(self) -> String {
#[cfg(windows)]
{
let mut message = HeapString::default();
let mut code = self.0;
let mut module = core::ptr::null_mut();
let mut flags = FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS;
unsafe {
if self.0 & 0x1000_0000 == 0x1000_0000 {
code ^= 0x1000_0000;
flags |= FORMAT_MESSAGE_FROM_HMODULE;
module = LoadLibraryExA(
b"ntdll.dll\0".as_ptr(),
core::ptr::null_mut(),
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
);
}
let size = FormatMessageW(
flags,
module as _,
code as _,
0,
&mut message.0 as *mut _ as *mut _,
0,
core::ptr::null(),
);
if !message.0.is_null() && size > 0 {
String::from_utf16_lossy(wide_trim_end(core::slice::from_raw_parts(
message.0,
size as usize,
)))
} else {
String::default()
}
}
}
#[cfg(not(windows))]
{
return alloc::format!("0x{:08x}", self.0 as u32);
}
}
/// Maps a Win32 error code to an HRESULT value.
pub const fn from_win32(error: u32) -> Self {
Self(if error as i32 <= 0 {
error
} else {
(error & 0x0000_FFFF) | (7 << 16) | 0x8000_0000
} as i32)
}
/// Maps an NT error code to an HRESULT value.
pub const fn from_nt(error: i32) -> Self {
Self(if error >= 0 {
error
} else {
error | 0x1000_0000
})
}
}
impl<T> From<Result<T>> for HRESULT {
fn from(result: Result<T>) -> Self {
if let Err(error) = result {
return error.into();
}
HRESULT(0)
}
}
impl core::fmt::Display for HRESULT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{:#010X}", self.0))
}
}
impl core::fmt::Debug for HRESULT {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("HRESULT({})", self))
}
}

View File

@@ -0,0 +1,38 @@
/*!
Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
*/
#![cfg_attr(
windows_debugger_visualizer,
debugger_visualizer(natvis_file = "../.natvis")
)]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![cfg_attr(not(windows), allow(unused_imports))]
extern crate alloc;
#[allow(unused_imports)]
use alloc::{string::String, vec::Vec};
mod bindings;
use bindings::*;
#[cfg(all(windows, not(windows_slim_errors)))]
mod com;
#[cfg(windows)]
mod strings;
#[cfg(windows)]
use strings::*;
#[cfg(all(windows, not(windows_slim_errors)))]
mod bstr;
mod error;
pub use error::*;
mod hresult;
pub use hresult::HRESULT;
/// A specialized [`Result`] type that provides Windows error information.
pub type Result<T> = core::result::Result<T, Error>;

View File

@@ -0,0 +1,29 @@
use super::*;
pub struct HeapString(pub *mut u16);
impl Default for HeapString {
fn default() -> Self {
Self(core::ptr::null_mut())
}
}
impl Drop for HeapString {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
HeapFree(GetProcessHeap(), 0, self.0 as _);
}
}
}
}
pub fn wide_trim_end(mut wide: &[u16]) -> &[u16] {
while let Some(last) = wide.last() {
match last {
32 | 9..=13 => wide = &wide[..wide.len() - 1],
_ => break,
}
}
wide
}

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"32a1010e66211d4fb11ce57a26770d00e095287dbd181c4dfb709d0c059dbd19","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","readme.md":"3e1748347e92c36f13f857141779c447faa7e2cd8e8a0d1f9dfe155467f7637e","src/bindings.rs":"ff9b855659617081e57cb702b830e09e44dfcc4cc1245ef2c9096bae987d12b0","src/bstr.rs":"2fcd125475d865390635c387f0a95508fd0ea8235ed89cb8963a8c85d9a05aaf","src/decode.rs":"cecdb73a3b54fb8f3d24a245dddbe790a5e7889771e5c3301cbc5ac375abc7bd","src/hstring.rs":"563c38bbfd0309d9d1ed46982d47c307adbd4b1996edb416c3c21d758d0588f9","src/hstring_builder.rs":"baec1f7c7a7e765dc7abfbfea2aa640f1d73c0427372439bc7b7ab18de9c4d05","src/hstring_header.rs":"3752d9cdf4b9520d360068c84efc6586e3fd28d048cd554b63dba99a2db62117","src/lib.rs":"1ef152a7c2539a80fdef923285cbe1db1a196f6bb1a0b3492609fce64ba90e81","src/literals.rs":"08a5ff6547a9ce50d27ab910cb9d8f8a3b647a412bf320f49ea4a124265157d2","src/pcstr.rs":"832f18aea1b97eb12ecab7e58a4f5f16a22f118a4d9d66d4bdb84c83c2fd3d58","src/pcwstr.rs":"67f619fe360dbdfede4ddb5502fb0a7ef7bd68c787d873c6cf0634eff2bd5860","src/pstr.rs":"34b4b2035330d192e29914f68a1b9096387562e0bf2e1d4bef2937a9fcce7734","src/pwstr.rs":"d4701cd606421100b896d6b0cc09117df0f2e59f47e95d91eb178dfd431c89fe","src/ref_count.rs":"e1617f090d56948719730c81e34449c3d36f288b86a74182719a291c340f65d6"},"package":"4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"}

View File

@@ -0,0 +1,58 @@
# 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.60"
name = "windows-strings"
version = "0.1.0"
authors = ["Microsoft"]
build = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust for 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 = []
[lib]
name = "windows_strings"
path = "src/lib.rs"
[dependencies.windows-result]
version = "0.2.0"
default-features = false
[dependencies.windows-targets]
version = "0.52.6"
[features]
default = ["std"]
std = []
[lints.rust]
missing_docs = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"]

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,36 @@
## Windows string types
The [windows-strings](https://crates.io/crates/windows-strings) crate provides common Windows string types used by various Windows APIs.
* [Getting started](https://kennykerr.ca/rust-getting-started/)
* [Samples](https://github.com/microsoft/windows-rs/tree/0.58.0/crates/samples)
* [Releases](https://github.com/microsoft/windows-rs/releases)
Start by adding the following to your Cargo.toml file:
```toml
[dependencies.windows-strings]
version = "0.1"
```
Use the Windows string types as needed:
```rust
use windows_strings::*;
const A: PCSTR = s!("ansi");
const W: PCWSTR = w!("wide");
fn main() -> Result<()> {
let b = BSTR::from("bstr");
let h = HSTRING::from("hstring");
assert_eq!(b, "bstr");
assert_eq!(h, "hstring");
assert_eq!(unsafe { A.to_string()? }, "ansi");
assert_eq!(unsafe { W.to_string()? }, "wide");
Ok(())
}
```

View File

@@ -0,0 +1,20 @@
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
dead_code,
clippy::all
)]
windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap : HANDLE, dwflags : HEAP_FLAGS, dwbytes : usize) -> *mut core::ffi::c_void);
windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const core::ffi::c_void) -> BOOL);
windows_targets::link!("oleaut32.dll" "system" fn SysAllocStringLen(strin : PCWSTR, ui : u32) -> BSTR);
windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR));
windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
pub type BOOL = i32;
pub type BSTR = *const u16;
pub const E_OUTOFMEMORY: HRESULT = 0x8007000E_u32 as _;
pub type HANDLE = *mut core::ffi::c_void;
pub type HEAP_FLAGS = u32;
pub type HRESULT = i32;
pub type PCWSTR = *const u16;

View File

@@ -0,0 +1,176 @@
use super::*;
/// A BSTR string ([BSTR](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/string-manipulation-functions))
/// is a length-prefixed wide string.
#[repr(transparent)]
pub struct BSTR(*const u16);
impl BSTR {
/// Create an empty `BSTR`.
///
/// This function does not allocate memory.
pub const fn new() -> Self {
Self(core::ptr::null_mut())
}
/// Returns `true` if the string is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the length of the string.
pub fn len(&self) -> usize {
if self.0.is_null() {
0
} else {
unsafe { bindings::SysStringLen(self.0) as usize }
}
}
/// Get the string as 16-bit wide characters (wchars).
pub fn as_wide(&self) -> &[u16] {
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
/// Returns a raw pointer to the `BSTR` buffer.
pub fn as_ptr(&self) -> *const u16 {
if !self.is_empty() {
self.0
} else {
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
}
}
/// Create a `BSTR` from a slice of 16 bit characters (wchars).
pub fn from_wide(value: &[u16]) -> Result<Self> {
if value.is_empty() {
return Ok(Self::new());
}
let result = unsafe {
Self(bindings::SysAllocStringLen(
value.as_ptr(),
value.len().try_into()?,
))
};
if result.is_empty() {
Err(Error::from_hresult(HRESULT(bindings::E_OUTOFMEMORY)))
} else {
Ok(result)
}
}
/// # Safety
#[doc(hidden)]
pub unsafe fn from_raw(raw: *const u16) -> Self {
Self(raw)
}
/// # Safety
#[doc(hidden)]
pub fn into_raw(self) -> *const u16 {
unsafe { core::mem::transmute(self) }
}
}
impl Clone for BSTR {
fn clone(&self) -> Self {
Self::from_wide(self.as_wide()).unwrap()
}
}
impl From<&str> for BSTR {
fn from(value: &str) -> Self {
let value: alloc::vec::Vec<u16> = value.encode_utf16().collect();
Self::from_wide(&value).unwrap()
}
}
impl From<String> for BSTR {
fn from(value: String) -> Self {
value.as_str().into()
}
}
impl From<&String> for BSTR {
fn from(value: &String) -> Self {
value.as_str().into()
}
}
impl<'a> TryFrom<&'a BSTR> for String {
type Error = alloc::string::FromUtf16Error;
fn try_from(value: &BSTR) -> core::result::Result<Self, Self::Error> {
String::from_utf16(value.as_wide())
}
}
impl TryFrom<BSTR> for String {
type Error = alloc::string::FromUtf16Error;
fn try_from(value: BSTR) -> core::result::Result<Self, Self::Error> {
String::try_from(&value)
}
}
impl Default for BSTR {
fn default() -> Self {
Self(core::ptr::null_mut())
}
}
impl core::fmt::Display for BSTR {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(
f,
"{}",
Decode(|| core::char::decode_utf16(self.as_wide().iter().cloned()))
)
}
}
impl core::fmt::Debug for BSTR {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "{}", self)
}
}
impl PartialEq for BSTR {
fn eq(&self, other: &Self) -> bool {
self.as_wide() == other.as_wide()
}
}
impl Eq for BSTR {}
impl PartialEq<BSTR> for &str {
fn eq(&self, other: &BSTR) -> bool {
other == self
}
}
impl PartialEq<BSTR> for String {
fn eq(&self, other: &BSTR) -> bool {
other == self
}
}
impl<T: AsRef<str> + ?Sized> PartialEq<T> for BSTR {
fn eq(&self, other: &T) -> bool {
self.as_wide()
.iter()
.copied()
.eq(other.as_ref().encode_utf16())
}
}
impl Drop for BSTR {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { bindings::SysFreeString(self.0) }
}
}
}

View File

@@ -1,53 +1,28 @@
mod bstr;
mod hstring;
mod literals;
mod pcstr;
mod pcwstr;
mod pstr;
mod pwstr;
pub use bstr::*;
pub use hstring::*;
#[doc(hidden)]
pub use literals::*;
pub use pcstr::*;
pub use pcwstr::*;
pub use pstr::*;
pub use pwstr::*;
use super::*;
extern "C" {
#[doc(hidden)]
pub fn strlen(s: PCSTR) -> usize;
#[doc(hidden)]
pub fn wcslen(s: PCWSTR) -> usize;
}
/// An internal helper for decoding an iterator of chars and displaying them
#[doc(hidden)]
pub struct Decode<F>(pub F);
impl<F, R, E> std::fmt::Display for Decode<F>
impl<F, R, E> core::fmt::Display for Decode<F>
where
F: Clone + FnOnce() -> R,
R: IntoIterator<Item = std::result::Result<char, E>>,
R: IntoIterator<Item = core::result::Result<char, E>>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use core::fmt::Write;
let iter = self.0.clone();
for c in iter().into_iter() {
f.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))?
f.write_char(c.unwrap_or(core::char::REPLACEMENT_CHARACTER))?
}
Ok(())
}
}
/// Mirror of `std::char::decode_utf16` for utf-8.
fn decode_utf8(mut buffer: &[u8]) -> impl Iterator<Item = std::result::Result<char, std::str::Utf8Error>> + '_ {
pub fn decode_utf8(
mut buffer: &[u8],
) -> impl Iterator<Item = core::result::Result<char, core::str::Utf8Error>> + '_ {
let mut current = "".chars();
let mut previous_error = None;
std::iter::from_fn(move || {
core::iter::from_fn(move || {
loop {
match (current.next(), previous_error) {
(Some(c), _) => return Some(Ok(c)),
@@ -59,7 +34,7 @@ fn decode_utf8(mut buffer: &[u8]) -> impl Iterator<Item = std::result::Result<ch
// We're completely done
(None, None) if buffer.is_empty() => return None,
(None, None) => {
match std::str::from_utf8(buffer) {
match core::str::from_utf8(buffer) {
Ok(s) => {
current = s.chars();
buffer = &[];
@@ -72,7 +47,7 @@ fn decode_utf8(mut buffer: &[u8]) -> impl Iterator<Item = std::result::Result<ch
// Set the current iterator to the valid section and indicate previous error
// SAFETY: `valid` is known to be valid utf-8 from error
current = unsafe { std::str::from_utf8_unchecked(valid) }.chars();
current = unsafe { core::str::from_utf8_unchecked(valid) }.chars();
previous_error = Some(e);
}
}

View File

@@ -0,0 +1,404 @@
use super::*;
/// An ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring))
/// is a reference-counted and immutable UTF-16 string type.
#[repr(transparent)]
pub struct HSTRING(pub(crate) *mut HStringHeader);
impl HSTRING {
/// Create an empty `HSTRING`.
///
/// This function does not allocate memory.
pub const fn new() -> Self {
Self(core::ptr::null_mut())
}
/// Returns `true` if the string is empty.
pub fn is_empty(&self) -> bool {
// An empty HSTRING is represented by a null pointer.
self.0.is_null()
}
/// Returns the length of the string. The length is measured in `u16`s (UTF-16 code units), not including the terminating null character.
pub fn len(&self) -> usize {
if let Some(header) = self.as_header() {
header.len as usize
} else {
0
}
}
/// Get the string as 16-bit wide characters (wchars).
pub fn as_wide(&self) -> &[u16] {
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}
/// Returns a raw pointer to the `HSTRING` buffer.
pub fn as_ptr(&self) -> *const u16 {
if let Some(header) = self.as_header() {
header.data
} else {
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
}
}
/// Create a `HSTRING` from a slice of 16 bit characters (wchars).
pub fn from_wide(value: &[u16]) -> Result<Self> {
unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
}
/// Get the contents of this `HSTRING` as a String lossily.
pub fn to_string_lossy(&self) -> String {
String::from_utf16_lossy(self.as_wide())
}
/// Get the contents of this `HSTRING` as a OsString.
#[cfg(feature = "std")]
pub fn to_os_string(&self) -> std::ffi::OsString {
std::os::windows::ffi::OsStringExt::from_wide(self.as_wide())
}
/// # Safety
/// len must not be less than the number of items in the iterator.
unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Result<Self> {
if len == 0 {
return Ok(Self::new());
}
let ptr = HStringHeader::alloc(len.try_into()?)?;
// Place each utf-16 character into the buffer and
// increase len as we go along.
for (index, wide) in iter.enumerate() {
debug_assert!(index < len);
(*ptr).data.add(index).write(wide);
(*ptr).len = index as u32 + 1;
}
// Write a 0 byte to the end of the buffer.
(*ptr).data.offset((*ptr).len as isize).write(0);
Ok(Self(ptr))
}
fn as_header(&self) -> Option<&HStringHeader> {
unsafe { self.0.as_ref() }
}
}
impl Default for HSTRING {
fn default() -> Self {
Self::new()
}
}
impl Clone for HSTRING {
fn clone(&self) -> Self {
if let Some(header) = self.as_header() {
Self(header.duplicate().unwrap())
} else {
Self::new()
}
}
}
impl Drop for HSTRING {
fn drop(&mut self) {
if let Some(header) = self.as_header() {
// HSTRING_REFERENCE_FLAG indicates a string backed by static or stack memory that is
// thus not reference-counted and does not need to be freed.
unsafe {
if header.flags & HSTRING_REFERENCE_FLAG == 0 && header.count.release() == 0 {
HStringHeader::free(self.0);
}
}
}
}
}
unsafe impl Send for HSTRING {}
unsafe impl Sync for HSTRING {}
impl core::fmt::Display for HSTRING {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}",
Decode(|| core::char::decode_utf16(self.as_wide().iter().cloned()))
)
}
}
impl core::fmt::Debug for HSTRING {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "\"{}\"", self)
}
}
impl From<&str> for HSTRING {
fn from(value: &str) -> Self {
unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()).unwrap() }
}
}
impl From<String> for HSTRING {
fn from(value: String) -> Self {
value.as_str().into()
}
}
impl From<&String> for HSTRING {
fn from(value: &String) -> Self {
value.as_str().into()
}
}
#[cfg(feature = "std")]
impl From<&std::path::Path> for HSTRING {
fn from(value: &std::path::Path) -> Self {
value.as_os_str().into()
}
}
#[cfg(feature = "std")]
impl From<&std::ffi::OsStr> for HSTRING {
fn from(value: &std::ffi::OsStr) -> Self {
unsafe {
Self::from_wide_iter(
std::os::windows::ffi::OsStrExt::encode_wide(value),
value.len(),
)
.unwrap()
}
}
}
#[cfg(feature = "std")]
impl From<std::ffi::OsString> for HSTRING {
fn from(value: std::ffi::OsString) -> Self {
value.as_os_str().into()
}
}
#[cfg(feature = "std")]
impl From<&std::ffi::OsString> for HSTRING {
fn from(value: &std::ffi::OsString) -> Self {
value.as_os_str().into()
}
}
impl Eq for HSTRING {}
impl Ord for HSTRING {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_wide().cmp(other.as_wide())
}
}
impl core::hash::Hash for HSTRING {
fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
self.as_wide().hash(hasher)
}
}
impl PartialOrd for HSTRING {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for HSTRING {
fn eq(&self, other: &Self) -> bool {
*self.as_wide() == *other.as_wide()
}
}
impl PartialEq<String> for HSTRING {
fn eq(&self, other: &String) -> bool {
*self == **other
}
}
impl PartialEq<String> for &HSTRING {
fn eq(&self, other: &String) -> bool {
**self == **other
}
}
impl PartialEq<&String> for HSTRING {
fn eq(&self, other: &&String) -> bool {
*self == ***other
}
}
impl PartialEq<str> for HSTRING {
fn eq(&self, other: &str) -> bool {
self.as_wide().iter().copied().eq(other.encode_utf16())
}
}
impl PartialEq<str> for &HSTRING {
fn eq(&self, other: &str) -> bool {
**self == *other
}
}
impl PartialEq<&str> for HSTRING {
fn eq(&self, other: &&str) -> bool {
*self == **other
}
}
impl PartialEq<HSTRING> for str {
fn eq(&self, other: &HSTRING) -> bool {
*other == *self
}
}
impl PartialEq<HSTRING> for &str {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
impl PartialEq<&HSTRING> for str {
fn eq(&self, other: &&HSTRING) -> bool {
**other == *self
}
}
impl PartialEq<HSTRING> for String {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
impl PartialEq<HSTRING> for &String {
fn eq(&self, other: &HSTRING) -> bool {
*other == ***self
}
}
impl PartialEq<&HSTRING> for String {
fn eq(&self, other: &&HSTRING) -> bool {
**other == **self
}
}
#[cfg(feature = "std")]
impl PartialEq<std::ffi::OsString> for HSTRING {
fn eq(&self, other: &std::ffi::OsString) -> bool {
*self == **other
}
}
#[cfg(feature = "std")]
impl PartialEq<std::ffi::OsString> for &HSTRING {
fn eq(&self, other: &std::ffi::OsString) -> bool {
**self == **other
}
}
#[cfg(feature = "std")]
impl PartialEq<&std::ffi::OsString> for HSTRING {
fn eq(&self, other: &&std::ffi::OsString) -> bool {
*self == ***other
}
}
#[cfg(feature = "std")]
impl PartialEq<std::ffi::OsStr> for HSTRING {
fn eq(&self, other: &std::ffi::OsStr) -> bool {
self.as_wide()
.iter()
.copied()
.eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
}
}
#[cfg(feature = "std")]
impl PartialEq<std::ffi::OsStr> for &HSTRING {
fn eq(&self, other: &std::ffi::OsStr) -> bool {
**self == *other
}
}
#[cfg(feature = "std")]
impl PartialEq<&std::ffi::OsStr> for HSTRING {
fn eq(&self, other: &&std::ffi::OsStr) -> bool {
*self == **other
}
}
#[cfg(feature = "std")]
impl PartialEq<HSTRING> for std::ffi::OsStr {
fn eq(&self, other: &HSTRING) -> bool {
*other == *self
}
}
#[cfg(feature = "std")]
impl PartialEq<HSTRING> for &std::ffi::OsStr {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
#[cfg(feature = "std")]
impl PartialEq<&HSTRING> for std::ffi::OsStr {
fn eq(&self, other: &&HSTRING) -> bool {
**other == *self
}
}
#[cfg(feature = "std")]
impl PartialEq<HSTRING> for std::ffi::OsString {
fn eq(&self, other: &HSTRING) -> bool {
*other == **self
}
}
#[cfg(feature = "std")]
impl PartialEq<HSTRING> for &std::ffi::OsString {
fn eq(&self, other: &HSTRING) -> bool {
*other == ***self
}
}
#[cfg(feature = "std")]
impl PartialEq<&HSTRING> for std::ffi::OsString {
fn eq(&self, other: &&HSTRING) -> bool {
**other == **self
}
}
impl<'a> TryFrom<&'a HSTRING> for String {
type Error = alloc::string::FromUtf16Error;
fn try_from(hstring: &HSTRING) -> core::result::Result<Self, Self::Error> {
String::from_utf16(hstring.as_wide())
}
}
impl TryFrom<HSTRING> for String {
type Error = alloc::string::FromUtf16Error;
fn try_from(hstring: HSTRING) -> core::result::Result<Self, Self::Error> {
String::try_from(&hstring)
}
}
#[cfg(feature = "std")]
impl<'a> From<&'a HSTRING> for std::ffi::OsString {
fn from(hstring: &HSTRING) -> Self {
hstring.to_os_string()
}
}
#[cfg(feature = "std")]
impl From<HSTRING> for std::ffi::OsString {
fn from(hstring: HSTRING) -> Self {
Self::from(&hstring)
}
}

View File

@@ -0,0 +1,89 @@
use super::*;
/// An [HSTRING] builder that supports preallocating the `HSTRING` to avoid extra allocations and copies.
///
/// This is similar to the `WindowsPreallocateStringBuffer` function but implemented directly in Rust for efficiency.
/// It is implemented as a separate type since [HSTRING] values are immutable.
pub struct HStringBuilder(*mut HStringHeader);
impl HStringBuilder {
/// Creates a preallocated `HSTRING` value.
pub fn new(len: usize) -> Result<Self> {
let header = HStringHeader::alloc(len.try_into()?)?;
if len > 0 {
unsafe { core::ptr::write_bytes((*header).data, 0, len) };
}
Ok(Self(header))
}
/// Shortens the string by removing any trailing 0 characters.
pub fn trim_end(&mut self) {
if let Some(header) = self.as_header_mut() {
while header.len > 0
&& unsafe { header.data.offset(header.len as isize - 1).read() == 0 }
{
header.len -= 1;
}
if header.len == 0 {
unsafe {
HStringHeader::free(self.0);
}
self.0 = core::ptr::null_mut();
}
}
}
fn as_header(&self) -> Option<&HStringHeader> {
unsafe { self.0.as_ref() }
}
fn as_header_mut(&mut self) -> Option<&mut HStringHeader> {
unsafe { self.0.as_mut() }
}
}
impl From<HStringBuilder> for HSTRING {
fn from(value: HStringBuilder) -> Self {
if let Some(header) = value.as_header() {
unsafe { header.data.offset(header.len as isize).write(0) };
let result = Self(value.0);
core::mem::forget(value);
result
} else {
Self::new()
}
}
}
impl core::ops::Deref for HStringBuilder {
type Target = [u16];
fn deref(&self) -> &[u16] {
if let Some(header) = self.as_header() {
unsafe { core::slice::from_raw_parts(header.data, header.len as usize) }
} else {
&[]
}
}
}
impl core::ops::DerefMut for HStringBuilder {
fn deref_mut(&mut self) -> &mut [u16] {
if let Some(header) = self.as_header() {
unsafe { core::slice::from_raw_parts_mut(header.data, header.len as usize) }
} else {
&mut []
}
}
}
impl Drop for HStringBuilder {
fn drop(&mut self) {
unsafe {
HStringHeader::free(self.0);
}
}
}

View File

@@ -0,0 +1,68 @@
use super::*;
pub const HSTRING_REFERENCE_FLAG: u32 = 1;
#[repr(C)]
pub struct HStringHeader {
pub flags: u32,
pub len: u32,
pub _0: u32,
pub _1: u32,
pub data: *mut u16,
pub count: RefCount,
pub buffer_start: u16,
}
impl HStringHeader {
pub fn alloc(len: u32) -> Result<*mut Self> {
if len == 0 {
return Ok(core::ptr::null_mut());
}
// Allocate enough space for header and two bytes per character.
// The space for the terminating null character is already accounted for inside of `HStringHeader`.
let bytes = core::mem::size_of::<Self>() + 2 * len as usize;
let header =
unsafe { bindings::HeapAlloc(bindings::GetProcessHeap(), 0, bytes) } as *mut Self;
if header.is_null() {
return Err(Error::from_hresult(HRESULT(bindings::E_OUTOFMEMORY)));
}
unsafe {
// Use `ptr::write` (since `header` is unintialized). `HStringHeader` is safe to be all zeros.
header.write(core::mem::MaybeUninit::<Self>::zeroed().assume_init());
(*header).len = len;
(*header).count = RefCount::new(1);
(*header).data = &mut (*header).buffer_start;
}
Ok(header)
}
pub unsafe fn free(header: *mut Self) {
if header.is_null() {
return;
}
bindings::HeapFree(bindings::GetProcessHeap(), 0, header as *mut _);
}
pub fn duplicate(&self) -> Result<*mut Self> {
if self.flags & HSTRING_REFERENCE_FLAG == 0 {
// If this is not a "fast pass" string then simply increment the reference count.
self.count.add_ref();
Ok(self as *const Self as *mut Self)
} else {
// Otherwise, allocate a new string and copy the value into the new string.
let copy = Self::alloc(self.len)?;
// SAFETY: since we are duplicating the string it is safe to copy all data from self to the initialized `copy`.
// We copy `len + 1` characters since `len` does not account for the terminating null character.
unsafe {
core::ptr::copy_nonoverlapping(self.data, (*copy).data, self.len as usize + 1);
}
Ok(copy)
}
}
}

View File

@@ -0,0 +1,56 @@
/*!
Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs>
*/
#![cfg(windows)]
#![allow(non_snake_case)]
#![cfg_attr(
windows_debugger_visualizer,
debugger_visualizer(natvis_file = "../.natvis")
)]
#![cfg_attr(all(not(feature = "std")), no_std)]
extern crate alloc;
use alloc::string::String;
pub use windows_result::Result;
use windows_result::*;
mod bstr;
pub use bstr::*;
mod hstring;
pub use hstring::*;
mod hstring_builder;
pub use hstring_builder::*;
mod hstring_header;
use hstring_header::*;
mod bindings;
mod decode;
use decode::*;
mod ref_count;
use ref_count::*;
mod literals;
pub use literals::*;
mod pcstr;
pub use pcstr::*;
mod pcwstr;
pub use pcwstr::*;
mod pstr;
pub use pstr::*;
mod pwstr;
pub use pwstr::*;
extern "C" {
fn strlen(s: PCSTR) -> usize;
}

View File

@@ -2,7 +2,7 @@
#[macro_export]
macro_rules! s {
($s:literal) => {
$crate::PCSTR::from_raw(::std::concat!($s, '\0').as_ptr())
$crate::PCSTR::from_raw(::core::concat!($s, '\0').as_ptr())
};
}
@@ -41,16 +41,26 @@ macro_rules! h {
($s:literal) => {{
const INPUT: &[u8] = $s.as_bytes();
const OUTPUT_LEN: usize = $crate::utf16_len(INPUT) + 1;
#[allow(clippy::declare_interior_mutable_const)]
const RESULT: $crate::HSTRING = {
if OUTPUT_LEN == 1 {
unsafe { ::std::mem::transmute(::std::ptr::null::<u16>()) }
} else {
const OUTPUT: $crate::PCWSTR = $crate::w!($s);
const HEADER: $crate::HSTRING_HEADER = $crate::HSTRING_HEADER { flags: 0x11, len: (OUTPUT_LEN - 1) as u32, padding1: 0, padding2: 0, ptr: OUTPUT.as_ptr() };
const HEADER: $crate::HSTRING_HEADER = $crate::HSTRING_HEADER {
flags: 0x11,
len: (OUTPUT_LEN - 1) as u32,
padding1: 0,
padding2: 0,
ptr: OUTPUT.as_ptr(),
};
// SAFETY: an `HSTRING` is exactly equivalent to a pointer to an `HSTRING_HEADER`
unsafe { ::std::mem::transmute::<&$crate::HSTRING_HEADER, $crate::HSTRING>(&HEADER) }
unsafe {
::std::mem::transmute::<&$crate::HSTRING_HEADER, $crate::HSTRING>(&HEADER)
}
}
};
#[allow(clippy::borrow_interior_mutable_const)]
&RESULT
}};
}
@@ -110,7 +120,8 @@ pub const fn decode_utf8_char(bytes: &[u8], mut pos: usize) -> Option<(u32, usiz
if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 || (ch4 & 0xc0) != 0x80 {
return None;
}
let result = ((ch & 0x07) << 18) | ((ch2 & 0x3f) << 12) | ((ch3 & 0x3f) << 6) | (ch4 & 0x3f);
let result =
((ch & 0x07) << 18) | ((ch2 & 0x3f) << 12) | ((ch3 & 0x3f) << 6) | (ch4 & 0x3f);
if result <= 0xffff || 0x10ffff < result {
return None;
}

View File

@@ -12,12 +12,12 @@ impl PCSTR {
}
/// Construct a null `PCSTR`
pub fn null() -> Self {
Self(std::ptr::null())
pub const fn null() -> Self {
Self(core::ptr::null())
}
/// Returns a raw pointer to the `PCSTR`
pub fn as_ptr(&self) -> *const u8 {
pub const fn as_ptr(&self) -> *const u8 {
self.0
}
@@ -32,8 +32,8 @@ impl PCSTR {
///
/// The `PCSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn as_bytes(&self) -> &[u8] {
let len = super::strlen(*self);
std::slice::from_raw_parts(self.0, len)
let len = strlen(*self);
core::slice::from_raw_parts(self.0, len)
}
/// Copy the `PCSTR` into a Rust `String`.
@@ -41,7 +41,7 @@ impl PCSTR {
/// # Safety
///
/// See the safety information for `PCSTR::as_bytes`.
pub unsafe fn to_string(&self) -> std::result::Result<String, std::string::FromUtf8Error> {
pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf8Error> {
String::from_utf8(self.as_bytes().into())
}
@@ -50,24 +50,7 @@ impl PCSTR {
/// # Safety
///
/// See the safety information for `PCSTR::as_bytes`.
pub unsafe fn display(&self) -> impl std::fmt::Display + '_ {
pub unsafe fn display(&self) -> impl core::fmt::Display + '_ {
Decode(move || decode_utf8(self.as_bytes()))
}
}
impl TypeKind for PCSTR {
type TypeKind = CopyType;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_display() {
// 💖 followed by an invalid byte sequence and then an incomplete one
let s = [240, 159, 146, 150, 255, 240, 159, 0];
let s = PCSTR::from_raw(s.as_ptr());
assert_eq!("💖<EFBFBD>", format!("{}", unsafe { s.display() }));
}
}

View File

@@ -13,7 +13,7 @@ impl PCWSTR {
/// Construct a null `PCWSTR`
pub const fn null() -> Self {
Self(std::ptr::null())
Self(core::ptr::null())
}
/// Returns a raw pointer to the `PCWSTR`
@@ -26,14 +26,34 @@ impl PCWSTR {
self.0.is_null()
}
/// String length without the trailing 0
///
/// # Safety
///
/// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn len(&self) -> usize {
extern "C" {
fn wcslen(s: *const u16) -> usize;
}
wcslen(self.0)
}
/// Returns `true` if the string length is zero, and `false` otherwise.
///
/// # Safety
///
/// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn is_empty(&self) -> bool {
self.len() == 0
}
/// String data without the trailing 0
///
/// # Safety
///
/// The `PCWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn as_wide(&self) -> &[u16] {
let len = super::wcslen(*self);
std::slice::from_raw_parts(self.0, len)
core::slice::from_raw_parts(self.0, self.len())
}
/// Copy the `PCWSTR` into a Rust `String`.
@@ -41,7 +61,7 @@ impl PCWSTR {
/// # Safety
///
/// See the safety information for `PCWSTR::as_wide`.
pub unsafe fn to_string(&self) -> std::result::Result<String, std::string::FromUtf16Error> {
pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf16Error> {
String::from_utf16(self.as_wide())
}
@@ -59,11 +79,7 @@ impl PCWSTR {
/// # Safety
///
/// See the safety information for `PCWSTR::as_wide`.
pub unsafe fn display(&self) -> impl std::fmt::Display + '_ {
Decode(move || std::char::decode_utf16(self.as_wide().iter().cloned()))
pub unsafe fn display(&self) -> impl core::fmt::Display + '_ {
Decode(move || core::char::decode_utf16(self.as_wide().iter().cloned()))
}
}
impl TypeKind for PCWSTR {
type TypeKind = CopyType;
}

View File

@@ -12,12 +12,12 @@ impl PSTR {
}
/// Construct a null `PSTR`
pub fn null() -> Self {
Self(std::ptr::null_mut())
pub const fn null() -> Self {
Self(core::ptr::null_mut())
}
/// Returns a raw pointer to the `PSTR`
pub fn as_ptr(&self) -> *mut u8 {
pub const fn as_ptr(&self) -> *mut u8 {
self.0
}
@@ -32,8 +32,8 @@ impl PSTR {
///
/// The `PSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn as_bytes(&self) -> &[u8] {
let len = super::strlen(PCSTR::from_raw(self.0));
std::slice::from_raw_parts(self.0, len)
let len = strlen(PCSTR::from_raw(self.0));
core::slice::from_raw_parts(self.0, len)
}
/// Copy the `PSTR` into a Rust `String`.
@@ -41,7 +41,7 @@ impl PSTR {
/// # Safety
///
/// See the safety information for `PSTR::as_bytes`.
pub unsafe fn to_string(&self) -> std::result::Result<String, std::string::FromUtf8Error> {
pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf8Error> {
String::from_utf8(self.as_bytes().into())
}
@@ -50,11 +50,7 @@ impl PSTR {
/// # Safety
///
/// See the safety information for `PSTR::as_bytes`.
pub unsafe fn display(&self) -> impl std::fmt::Display + '_ {
pub unsafe fn display(&self) -> impl core::fmt::Display + '_ {
Decode(move || decode_utf8(self.as_bytes()))
}
}
impl TypeKind for PSTR {
type TypeKind = CopyType;
}

View File

@@ -12,12 +12,12 @@ impl PWSTR {
}
/// Construct a null `PWSTR`.
pub fn null() -> Self {
Self(std::ptr::null_mut())
pub const fn null() -> Self {
Self(core::ptr::null_mut())
}
/// Returns a raw pointer to the `PWSTR`.
pub fn as_ptr(&self) -> *mut u16 {
pub const fn as_ptr(&self) -> *mut u16 {
self.0
}
@@ -26,14 +26,31 @@ impl PWSTR {
self.0.is_null()
}
/// String length without the trailing 0
///
/// # Safety
///
/// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn len(&self) -> usize {
PCWSTR(self.0).len()
}
/// Returns `true` if the string length is zero, and `false` otherwise.
///
/// # Safety
///
/// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn is_empty(&self) -> bool {
self.len() == 0
}
/// String data without the trailing 0.
///
/// # Safety
///
/// The `PWSTR`'s pointer needs to be valid for reads up until and including the next `\0`.
pub unsafe fn as_wide(&self) -> &[u16] {
let len = super::wcslen(PCWSTR::from_raw(self.0));
std::slice::from_raw_parts(self.0, len)
core::slice::from_raw_parts(self.0, self.len())
}
/// Copy the `PWSTR` into a Rust `String`.
@@ -41,7 +58,7 @@ impl PWSTR {
/// # Safety
///
/// See the safety information for `PWSTR::as_wide`.
pub unsafe fn to_string(&self) -> std::result::Result<String, std::string::FromUtf16Error> {
pub unsafe fn to_string(&self) -> core::result::Result<String, alloc::string::FromUtf16Error> {
String::from_utf16(self.as_wide())
}
@@ -59,11 +76,7 @@ impl PWSTR {
/// # Safety
///
/// See the safety information for `PWSTR::as_wide`.
pub unsafe fn display(&self) -> impl std::fmt::Display + '_ {
Decode(move || std::char::decode_utf16(self.as_wide().iter().cloned()))
pub unsafe fn display(&self) -> impl core::fmt::Display + '_ {
Decode(move || core::char::decode_utf16(self.as_wide().iter().cloned()))
}
}
impl TypeKind for PWSTR {
type TypeKind = CopyType;
}

View File

@@ -0,0 +1,27 @@
use core::sync::atomic::{fence, AtomicI32, Ordering};
#[repr(transparent)]
#[derive(Default)]
pub struct RefCount(pub(crate) AtomicI32);
impl RefCount {
pub fn new(count: u32) -> Self {
Self(AtomicI32::new(count as i32))
}
pub fn add_ref(&self) -> u32 {
(self.0.fetch_add(1, Ordering::Relaxed) + 1) as u32
}
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
}
}