Windows-rs: Support COM interfaces inheritance

Created on 22 Jan 2021  路  5Comments  路  Source: microsoft/windows-rs

It appears that COM interfaces that inherit from other interfaces don't properly express it in their vtable layout. Take ID3D11Texture2D for example:

#[repr(C)]
pub struct ID3D11Texture2D_abi(
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        iid: &::windows::Guid,
        interface: *mut ::windows::RawPtr,
    ) -> ::windows::ErrorCode,
    pub unsafe extern "system" fn(this: ::windows::RawPtr) -> u32,
    pub unsafe extern "system" fn(this: ::windows::RawPtr) -> u32,
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        p_desc: *mut D3D11_TEXTURE2D_DESC,
    ),
);

In order to call GetDesc correctly, I had to define a custom vtable:

#[repr(C)]
pub struct ID3D11Texture2DCustom_abi(
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        iid: &::windows::Guid,
        interface: *mut ::windows::RawPtr,
    ) -> ::windows::ErrorCode,
    pub unsafe extern "system" fn(this: ::windows::RawPtr) -> u32,
    pub unsafe extern "system" fn(this: ::windows::RawPtr) -> u32,

    // ID3D11DeviceChild
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        pp_device: *mut ::std::option::Option<ID3D11Device>,
    ),
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        guid: *const ::windows::Guid,
        p_data_size: *mut u32,
        p_data: *mut ::std::ffi::c_void,
    ) -> ::windows::ErrorCode,
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        guid: *const ::windows::Guid,
        data_size: u32,
        p_data: *const ::std::ffi::c_void,
    ) -> ::windows::ErrorCode,
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        guid: *const ::windows::Guid,
        p_data: ::std::option::Option<::windows::IUnknown>,
    ) -> ::windows::ErrorCode,

    // ID3D11Resource
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        p_resource_dimension: *mut bindings::windows::win32::direct3d11::D3D11_RESOURCE_DIMENSION,
    ),
    pub unsafe extern "system" fn(this: ::windows::RawPtr, eviction_priority: u32),
    pub unsafe extern "system" fn(this: ::windows::RawPtr) -> u32,

    // ID3D11Texture2D
    pub  unsafe extern "system" fn(
        this: ::windows::RawPtr,
        p_desc: *mut D3D11_TEXTURE2D_DESC,
    ),
);
enhancement

Most helpful comment

I just got it working. A bit more polish needed, but it's looking good. 馃槈 Here's an example:

use bindings::windows::win32::direct2d::*;

use windows::Abi;
use windows::Interface;

fn main() -> windows::Result<()> {
    let factory = create_factory()?;

    let mut dpi_x = 0.0;
    let mut dpi_y = 0.0;
    factory.GetDesktopDpi(&mut dpi_x, &mut dpi_y);

    println!("DPI x={} y={}", dpi_x, dpi_y);
    Ok(())
}

fn create_factory() -> windows::Result<ID2D1Factory7> {
    let mut options = D2D1_FACTORY_OPTIONS::default();

    if cfg!(debug_assertions) {
        options.debug_level = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_INFORMATION;
    }

    let mut result = None;

    unsafe {
        D2D1CreateFactory(
            D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED,
            &ID2D1Factory7::IID,
            &options,
            result.set_abi(),
        )
        .and_some(result)
    }
}

All 5 comments

Hey Robert, this is a known issue. I haven't got around to chaining the base interfaces yet. I plan to address this asap.

I just got it working. A bit more polish needed, but it's looking good. 馃槈 Here's an example:

use bindings::windows::win32::direct2d::*;

use windows::Abi;
use windows::Interface;

fn main() -> windows::Result<()> {
    let factory = create_factory()?;

    let mut dpi_x = 0.0;
    let mut dpi_y = 0.0;
    factory.GetDesktopDpi(&mut dpi_x, &mut dpi_y);

    println!("DPI x={} y={}", dpi_x, dpi_y);
    Ok(())
}

fn create_factory() -> windows::Result<ID2D1Factory7> {
    let mut options = D2D1_FACTORY_OPTIONS::default();

    if cfg!(debug_assertions) {
        options.debug_level = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_INFORMATION;
    }

    let mut result = None;

    unsafe {
        D2D1CreateFactory(
            D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED,
            &ID2D1Factory7::IID,
            &options,
            result.set_abi(),
        )
        .and_some(result)
    }
}

I'm trying to call seek on an IStream interface, and get a crash (access violation.) If I use the same pointer and call seek from C++ it works correctly.

Is this due to IStream inheriting from ISquentialStream and thus the vtable being incorrectly laid out?

Yes, I should have a PR out tomorrow with support for this. Hang tight.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rylev picture rylev  路  4Comments

kennykerr picture kennykerr  路  5Comments

wbuck picture wbuck  路  3Comments

13r0ck picture 13r0ck  路  4Comments

rylev picture rylev  路  4Comments