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

Support richer COM interface hierarchies #1608

Merged
merged 5 commits into from
Mar 17, 2022
Merged

Conversation

rylev
Copy link
Contributor

@rylev rylev commented Mar 16, 2022

As part of #1486, this allows for COM interfaces that inherit from something other than IUnknown.

This also adds a test with a simple but rich COM hierarchy.

@kennykerr
Copy link
Collaborator

kennykerr commented Mar 16, 2022

IPeristMemory is a simple existing interface that inherits from IPersist so should make a great test for the interface macro, much like the existing IUri test. Here's a simple example of implementing and exercising it. You could then add a declaration using the interface macro and test the matrix of implementations as before. That should ensure we have consistency between the interface macro and existing metadata-based interfaces.

use std::ffi::*;
use std::sync::*;
use windows::{core::*, Win32::Foundation::*, Win32::System::Com::*};

#[implement(IPersistMemory, IPersist)]
#[derive(Default)]
struct Persist(RwLock<PersistState>);

#[derive(Default)]
struct PersistState {
    memory: [u8; 10],
    dirty: bool,
}

impl IPersist_Impl for Persist {
    fn GetClassID(&self) -> Result<GUID> {
        Ok("117fb826-2155-483a-b50d-bc99a2c7cca3".into())
    }
}

impl IPersistMemory_Impl for Persist {
    fn IsDirty(&self) -> Result<()> {
        let reader = self.0.read().unwrap();
        if reader.dirty {
            Ok(())
        } else {
            Err(S_FALSE.into())
        }
    }
    fn Load(&self, input: *const c_void, size: u32) -> Result<()> {
        unsafe {
            let mut writer = self.0.write().unwrap();
            if size <= writer.memory.len() as _ {
                std::ptr::copy(input, writer.memory.as_mut_ptr() as _, size as _);
                writer.dirty = true;
                Ok(())
            } else {
                Err(E_OUTOFMEMORY.into())
            }
        }
    }
    fn Save(&self, output: *mut c_void, clear_dirty: BOOL, size: u32) -> Result<()> {
        unsafe {
            let mut writer = self.0.write().unwrap();
            if size <= writer.memory.len() as _ {
                std::ptr::copy(writer.memory.as_mut_ptr() as _, output, size as _);
                if clear_dirty.as_bool() {
                    writer.dirty = false;
                }
                Ok(())
            } else {
                Err(E_OUTOFMEMORY.into())
            }
        }
    }
    fn GetSizeMax(&self) -> Result<u32> {
        let reader = self.0.read().unwrap();
        Ok(reader.memory.len() as _)
    }
    fn InitNew(&self) -> Result<()> {
        let mut writer = self.0.write().unwrap();
        writer.memory = Default::default();
        writer.dirty = false;
        Ok(())
    }
}

fn main() -> Result<()> {
    unsafe {
        let p: IPersistMemory = Persist::default().into();
        assert_eq!(
            p.GetClassID()?,
            "117fb826-2155-483a-b50d-bc99a2c7cca3".into()
        );
        // TODO: can't test IsDirty until this is fixed: https://github.com/microsoft/win32metadata/issues/838
        assert_eq!(p.GetSizeMax()?, 10);
        p.Load(&[0xAA, 0xBB, 0xCC])?;
        let mut memory = [0x00, 0x00, 0x00, 0x00];
        p.Save(&mut memory, true)?;
        assert_eq!(memory, [0xAA, 0xBB, 0xCC, 0x00]);
        Ok(())
    }
}

@kennykerr
Copy link
Collaborator

Perhaps something like this:

#[interface("0000010c-0000-0000-C000-000000000046")]
pub unsafe trait ICustomPersist : IUnknown {
    unsafe fn GetClassID(&self, clsid: *mut GUID) -> HRESULT;
}

#[interface("BD1AE5E0-A6AE-11CE-BD37-504200C10000")]
pub unsafe trait ICustomPersistMemory : ICustomPersist {
    unsafe fn IsDirty(&self) -> HRESULT;
    unsafe fn Load(&self, input: *const c_void, size: u32) -> HRESULT;
    unsafe fn Save(&self, output: *mut c_void, clear_dirty: BOOL, size: u32) -> HRESULT;
    unsafe fn GetSizeMax(&self, len: *mut u32) -> HRESULT;
    unsafe fn InitNew(&self) -> HRESULT;
}

@kennykerr kennykerr merged commit 86e8088 into microsoft:master Mar 17, 2022
@rylev rylev deleted the rich-com2 branch March 17, 2022 16:48
@kennykerr kennykerr mentioned this pull request Mar 30, 2022
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

Successfully merging this pull request may close these issues.

2 participants