簡體   English   中英

為什么我應該初始化結構成員,即使它不應該被初始化?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM