Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Pointers for using COM #491

Closed
prasannavl opened this issue Aug 20, 2017 · 5 comments
Closed

Pointers for using COM #491

prasannavl opened this issue Aug 20, 2017 · 5 comments

Comments

@prasannavl
Copy link

Hi,

Fantastic efforts on putting this together. As I understand, from briefly glancing over the code, there are a lot of macros that help in using COM interfaces. It seems like these interfaces are built into structures with the interface methods as Traits, conforming to the COM ABI. That looks great. And there seems to be UUIDs defined for some.

I was wondering though, if you could perhaps, write a small section on how you had intended this to be used? I was trying to use the SAPI, but I was bit confused, starting with not being able to find where the UUIDs were defined, and so, I ended up pretty much manually implementing what I needed. But it looks redundant since I could clearly see a lot of the interfaces in the code. Little pointers as example would go a long way in helping what's the structure with which you had designed it, so it's helpful to people who also implement this and can contribute efforts back, for the missing components from winapi-rs, like for example the structure where, the CLSID, and the interface IDs are defined, and where exactly the structures go. I see some to be directly in the winapi-rs crate (sapi) for example. But for some, I see only structs and constants in it, and the functions in dedicated crates.

Let's take for instance, a simple example straight out of MSDN:

#include <stdafx.h>
#include <sapi.h>

int main(int argc, char* argv[])
{
    ISpVoice * pVoice = NULL;

    if (FAILED(::CoInitialize(NULL)))
        return FALSE;

    HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice;);
    if( SUCCEEDED( hr ) )
    {
        hr = pVoice->Speak(L"Hello world", 0, NULL);
        pVoice->Release();
        pVoice = NULL;
    }

    ::CoUninitialize();
    return TRUE;
}

Would be great if you could convert this using winapi-rs. :)

@retep998
Copy link
Owner

I will look into adding an example of this for winapi 0.3

@prasannavl
Copy link
Author

prasannavl commented Aug 20, 2017

Thanks! I was just working with the stable. From what I see in #316, it looks like structuring is one of the issues that it's aims to solve there. That was the primary source of my confusion - having to do grep guesswork to figure out if something is actually there. I would be happy to add some samples as well, if a consistent convention can be followed, for where the constants such as CLSID, interface IDs, etc. and where the structs, and traits are.

It just feels like it's all over the place on the stable (which perhaps, would be okay if RLS/auto-complete provided a hand - but pragmatically, I would presume it's going to be a while before macros are resolved by RLS to a reliable extent) - So, I feel strong and documented conventions are the key to making this more usable.

Looking forward to some form of documentation for the COM apis.

@retep998
Copy link
Owner

retep998 commented Aug 20, 2017

Generally if you want to know where a given symbol is defined in winapi 0.2 and it's sys crates, you'd just use the search bar on https://retep998.github.io/doc/winapi/index.html

For winapi 0.3 everything is simply in the module directly corresponding to the header it is from.

For winapi 0.3 you can get the IID of any interface through the Interface::uuidof trait method, mainly because it allows for some very smart pointers able to perform QueryInterface safely. I haven't added an equivalent for the CLSID, because there's no class to attach the CLSID to and a plain constant is just as useful. Smart pointer for winapi 0.3 example: https://github.com/retep998/msvc-bunny/blob/bed064e07190f615fb78a973a1600b51cd925032/src/util.rs#L50-L56

@Eh2406
Copy link
Contributor

Eh2406 commented Aug 21, 2017

I don't know if this is helpful but this is from my first commit to my sapi project using winapi 0.2

extern crate winapi;
extern crate ole32;

use std::mem;
use std::ptr;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;

#[inline]
fn failed(hr: winapi::HRESULT) -> bool {
    hr < 0
}

#[inline]
fn succeeded(hr: winapi::HRESULT) -> bool {
    !failed(hr)
}

pub trait ToWide {
    fn to_wide(&self) -> Vec<u16>;
    fn to_wide_null(&self) -> Vec<u16>;
}

impl<T> ToWide for T where T: AsRef<OsStr> {
    fn to_wide(&self) -> Vec<u16> {
        self.as_ref().encode_wide().collect()
    }
    fn to_wide_null(&self) -> Vec<u16> {
        self.as_ref().encode_wide().chain(Some(0)).collect()
    }
}

struct Com {
    hr: winapi::HRESULT,
}

impl Com {
    fn new() -> Com {
        println!("new for Con");
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
        let hr = unsafe {ole32::CoInitialize(ptr::null_mut())};
        if failed(hr) {
            panic!("failed for Con");
        }
        Com {hr: hr}
    }
}

impl Drop for Com {
    fn drop(&mut self) {
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms688715(v=vs.85).aspx
        if self.hr != winapi::RPC_E_CHANGED_MODE {
            unsafe {
                ole32::CoUninitialize();
            }
        }
        println!("drop for Con");
    }
}

struct SpVoice<'a> {
    // https://msdn.microsoft.com/en-us/library/ms723602(VS.85).aspx
    voice: &'a mut winapi::ISpVoice,
}

impl<'a> SpVoice<'a> {
    fn new() -> SpVoice<'a> {
        println!("new for SpVoice");
        let mut hr;
        let mut voice: *mut winapi::ISpVoice = ptr::null_mut();
        let sp_voice = "SAPI.SpVoice".to_wide_null();

        unsafe {
            let mut clsid_spvoice: winapi::CLSID = mem::uninitialized();

            hr = ole32::CLSIDFromProgID(&sp_voice[0], &mut clsid_spvoice);
            if failed(hr) {
                panic!("failed for SpVoice at CLSIDFromProgID");
            }

            hr = ole32::CoCreateInstance(
                &clsid_spvoice,
                ptr::null_mut(),
                winapi::CLSCTX_ALL,
                &winapi::UuidOfISpVoice,
                &mut voice as *mut *mut winapi::ISpVoice as *mut *mut winapi::c_void
            );
            if failed(hr) {
                panic!("failed for SpVoice at CoCreateInstance");
            }
            SpVoice {
                voice: &mut *voice,
            }
        }
    }

    fn speak (&mut self, string: &str) {
        unsafe {
            println!("befor speak");
            self.voice.Speak(string.to_wide_null().as_ptr(), 19, ptr::null_mut());
            println!("after speak");
        }
    }

    fn wait (&mut self) {
        unsafe {
            self.voice.WaitUntilDone(winapi::INFINITE);
        }
    }

    fn speak_wait (&mut self, string: &str) {
        self.speak(string);
        self.wait();
    }

}

impl<'a> Drop for SpVoice<'a> {
    fn drop(&mut self) {
        unsafe {
            self.voice.Release();
        }
        println!("drop for SpVoice");
    }
}

fn main() {
    let com = Com::new();
    let mut voice = SpVoice::new();

    voice.speak_wait("Hello, world!");
    voice.speak_wait("Hello, you!");
    voice.speak_wait("Hello, me!");

}

@prasannavl
Copy link
Author

@Eh2406 - Thank you! I believe this is going to be very helpful. This is rather a nice abstraction. I had written something very similar in the end, though I ended up implementing the SpVoice's VTable manually, since macros don't expand on Racer or RLS - Guess I'm addicted to auto-complete, and the future looks even bleaker with feature gates in 0.3 :/

I guess the last two comments address the issue for 0.2, while 0.3, I suppose this is resolved naturally, since it just follows the C headers. Closing :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants