[英]Should programs check for failure on WinAPI functions that “shouldn't”, but can, fail?
[英]Why should I initialize struct members even if it shouldn't be initialized?
我切換到 Rust 因為我喜歡它的語法。 但是,由於以下原因,我無法完成我的項目:
我有一個結構,它有一個方法應該用於創建我的結構的新實例,並且該結構使用一些外部板條箱( windows
板條箱),但我無法使用任何其他結構,因為我需要初始化每個我使用的結構。
例如:
use windows::Win32::{
IUIAutomationFocusChangedEventHandler, IUIAutomationFocusChangedEventHandler_Vtbl };
// Here's my struct:
pub struct EventHandler {
// A struct member to handle the event:
event: IUIAutomationFocusChangedEventHandler,
event_vtbl: IUIAutomationFocusChangedEventHandler_Vtbl,
}
// Anyone with experiance in the windows API
// Will understand the Virtual tables, and this code.
impl EventHandler {
pub fn new() -> EventHandler {
// Here, I should return a new instance of my struct:
EventHandler {
// Now, I should initialize every struct member:
event: IUIAutomationFocusChangedEventHandler {}, // ...
event_vtbl: IUIAutomationFocusChangedEventHandler_Vtbl {
// This struct needs two members:
base__: IUnknown {}, // IUnknown requires a lot of
// methods and member initialization to initialize it.
// Also the IUIAutomationFocusChangedEvent needs too member initialization....
}
}
這些結構不應在 C++ winapi 中初始化。 我不知道我該怎么辦。
每個結構都需要成員初始化,每個成員都需要其他成員,其他成員也需要成員初始化
我覺得我在漩渦中? 我錯過了什么嗎
首先回答字面問題:你可以,但你可能不應該這樣做。
windows
crate 中的 COM 支持公開了許多類型,但並非所有類型都適合客戶端代碼立即使用。 *_Vtbl
結構專門表示 COM 在內部用於調度接口調用的原始 function 指針表。 它們由庫聲明和填充,不打算由客戶端直接使用( #[doc(hidden)]
屬性是一個提示,盡管我確信庫結構和文檔體驗可以改進)。
嘗試在客戶端代碼中填充 v-tables 會使您陷入痛苦的境地。 幸運的是,這些都不是必需的,正如常見問題解答中簡要說明的那樣:
如何實現現有的 COM 接口?
如果您需要為某個類型實現 COM 接口,則需要添加可以在項目的 Cargo.toml 文件中啟用的
implement
功能(與任何 Cargo 功能一樣)。windows = { version = "..", features = ["implement"] }
然后,您需要通過將
#[implement]
proc 宏添加到您的類型,然后為該接口編寫一個impl
塊來聲明您的類型實現了特定接口。 對於名為IMyInterface
的接口,您需要實現IMyInterface_Impl
特征(注意名稱中的尾隨_Impl
)。#[windows::core::implement(IMyInterface)] struct MyStruct; impl IMyInterface_Impl for MyStruct { fn MyMethod(&self) -> windows::core::HRESULT { todo;("Your implementation goes here") } }
0.37.0版對implement
宏進行了重大更改,使其比看起來更容易接近。 讓我們從聲明一個帶有一點 state 信息的簡單結構開始:
#[implement(IUIAutomationFocusChangedEventHandler)]
struct EventHandler {
count: Cell<u64>,
}
impl EventHandler {
fn new() -> Self {
Self {
count: Cell::new(0),
}
}
fn count(&self) -> u64 {
self.count.get()
}
fn increment(&self) {
self.count.set(self.count.get() + 1);
}
}
這會保留發生的焦點更改事件的累積計數。 請注意,實現實際上並不正確:由於可以從多個線程調用事件處理程序,我們實際上需要一個Sync
類型( Cell
不是)。 那是您需要更改的內容1 。
缺少的是IUIAutomationFocusChangedEventHandler
接口實現。 它只有一個成員,所以這很容易(庫已經為您方便地提供了IUnknown
實現):
impl IUIAutomationFocusChangedEventHandler_Impl for EventHandler {
fn HandleFocusChangedEvent(&self, _sender: &Option<IUIAutomationElement>) -> Result<()> {
self.increment();
println!("Focus changed (cumulative count: {})", self.count());
Ok(())
}
}
對於每個焦點更改事件,它首先增加累積計數,然后將消息打印到 STDOUT。
這就是實現自定義IUIAutomationFocusChangedEventHandler
接口所需的全部內容。 從程序中使用它也不是很難,即使有很多陷阱(見評論):
fn main() -> Result<()> {
// Initialize COM for the current thread. Since we are running event handlers on this
// thread, it needs to live in the MTA.
// See [Understanding Threading Issues](https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-threading)
// for more information.
unsafe { CoInitializeEx(ptr::null(), COINIT_APARTMENTTHREADED) }?;
// Instantiate a `CUIAutomation` object
let uia: IUIAutomation =
unsafe { CoCreateInstance(&CUIAutomation, None, CLSCTX_INPROC_SERVER) }?;
// Subscribe to the focus changed event; this transfers ownership of `handler` into
// `uia`, making it the sole owner
let handler = EventHandler::new();
unsafe { uia.AddFocusChangedEventHandler(None, &handler.into()) }?;
// Display a message box so that we have an easy way to quit the program
let _ = unsafe {
MessageBoxW(
None,
w!("Click OK to end the program"),
w!("UIA Focus Monitor"),
MB_OK,
)
};
// Optionally unsubscribe from all events; this is not strictly required since we have
// to assume that the `CUIAutomation` object properly manages the lifetime of our
// `EventHandler` object
unsafe { uia.RemoveAllEventHandlers() }?;
// IMPORTANT: Do NOT call `CoUninitialize()` here. `uia`'s `Drop` implementation will
// get very angry at us when it runs after COM has been uninitialized
Ok(())
}
要編譯代碼,您需要使用以下導入:
use std::{cell::Cell, ptr};
use windows::{
core::{implement, Result},
w,
Win32::{
System::Com::{
CoCreateInstance, CoInitializeEx, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED,
},
UI::{
Accessibility::{
CUIAutomation, IUIAutomation, IUIAutomationElement,
IUIAutomationFocusChangedEventHandler, IUIAutomationFocusChangedEventHandler_Impl,
},
WindowsAndMessaging::{MessageBoxW, MB_OK},
},
},
};
這個Cargo.toml文件:
[package]
name = "uia_focus_change"
version = "0.0.0"
edition = "2021"
[dependencies.windows]
version = "0.39.0"
features = [
"implement",
"Win32_Foundation",
"Win32_System_Com",
"Win32_UI_Accessibility",
"Win32_UI_WindowsAndMessaging",
]
1我相信Mutex<u64>
就足夠了。 由於 COM 實現了共享所有權,因此在復制接口指針時不會進行復制。 所有接口指針都指向同一個實例,因此不需要將Mutex
包裝在Arc
中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.