Jump to content
Sign in to follow this  
Memnarch

Possible error in generated Units in "Windows API from WinMD"

Recommended Posts

Hello,
I need a second opinion on this. Either I am drunk or there is a huge error in the generated Windows units one can install through GetIT from Emarcadero called "Windows API from WinMD". If the following is an error, I'll create a ticket. If not, someone needs to explain this to me.

 

I am trying to use IMMDeviceEnumerator from Windows.Media.Audio.pas. Calling GetDefaultAudioEndpoint, I was unable to get the defaultdevice. This is how the interface is declared:
 

  ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nn-mmdeviceapi-immdeviceenumerator</summary>
  ///<remarks>
  ///<para>Supported since: <i>windows6.0.6000</i></para>
  ///</remarks>
  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdeviceenumerator-enumaudioendpoints</summary>
    function EnumAudioEndpoints(dataFlow: EDataFlow; dwStateMask: Cardinal; out ppDevices: IMMDeviceCollection): HRESULT; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdeviceenumerator-getdefaultaudioendpoint</summary>
    function GetDefaultAudioEndpoint(dataFlow: EDataFlow; role: ERole; out ppEndpoint: IMMDevice): HRESULT; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdeviceenumerator-getdevice</summary>
    function GetDevice(pwstrId: PWSTR; out ppDevice: IMMDevice): HRESULT; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdeviceenumerator-registerendpointnotificationcallback</summary>
    function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-immdeviceenumerator-unregisterendpointnotificationcallback</summary>
    function UnregisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

Suspecting something in the method declaration (i.e. out) was wrong. I copied it over to my code...and it started to work? Like, I didn't change a thing or did I? So after being confused for a moment, I looked at the interafce it inherits from.
When the Interface is in my code, due to the units in my uses, it derives from System.IUnknown which is declared like this:
 

  IInterface = interface
    ['{00000000-0000-0000-C000-000000000046}']
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

  IUnknown = IInterface;

However, in the generated code, it derives from Windows.Foundation.IUnknown which is declared like this:
 

  IUnknown = interface
  ['{00000000-0000-0000-C000-000000000046}']
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(q)</summary>
    ///<remarks>
    ///<para>Can return errors as success</para>
    ///</remarks>
    function QueryInterface(riid: PGuid; out ppvObject: Pointer): HRESULT; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/unknwn/nf-unknwn-iunknown-addref</summary>
    function AddRef: Cardinal; stdcall;
    ///<summary>Documentation: https://docs.microsoft.com/windows/win32/api/unknwn/nf-unknwn-iunknown-release</summary>
    function Release: Cardinal; stdcall;
  end;

So unless I am missing something crucial, this IUnknown-Interface is adding the same 3 methods it already has thorugh the IInterface-Baseinterface and offsets the entire VMT by 3 entries across the board.
And by adding/removing Windows.Foundation from my uses I can make the copy of the interface work/break at will. So this seems like an oversight and that IUnknown should just inherit from IInterface or be an alias, or am I wrong?

Share this post


Link to post

Hi,

6 hours ago, Memnarch said:

So unless I am missing something crucial, this IUnknown-Interface is adding the same 3 methods it already has thorugh the IInterface-Baseinterface and offsets the entire VMT by 3 entries across the board.
And by adding/removing Windows.Foundation from my uses I can make the copy of the interface work/break at will. So this seems like an oversight and that IUnknown should just inherit from IInterface or be an alias, or am I wrong?

VMT is not a problem here, VMT for such interfaces are different beast from VMT for objects/classes, VMT tables with COM interfaces (interfaces with GUID which i like to call "named interfaces" or "IDed interfaces") are separated into their own tables identified by their GUID, even for one object/interface, so VMT will be alright no matter what inheritance is there, also VMT for each interface are agnostic for other GUID declaration.

 

As for the interfaces you listed IInterface and IUnknown, this might be a problem as they declared with the same GUID (TGUID) hence they will compete to replace one another, they are identical in structure but different in parameters (declaration), so they will work unless the compiler will complain about stuff like Integer vs Cardinal or TGUID vs PGUID..., the problem here is how compiler see them and when.

 

I might be wrong here, but the fix should be removing Windows.Foundation.IUnknown , in other words, the already known interfaces should not be redeclared/generated.

Share this post


Link to post

There were other discussion about WINMD, that seems to be buggy, look there: https://en.delphipraxis.net/topic/13720-bugs-on-winmd-who-can-clarify/?do=findComment&comment=105350

 

In one post, @pcoder suggest the see others metadata provider (like this https://www.winsoft.sk/win32api.htm ).

If you look insede of those sources, you will look that more types were derivered form SYSTEM unit, like IUNKNOWN for example.

Try this instead WINMD.

 

There are in QP some open issues about WINMD.

 

 

Share this post


Link to post
15 hours ago, Kas Ob. said:

VMT is not a problem here, VMT for such interfaces are different beast from VMT for objects/classes, VMT tables with COM interfaces (interfaces with GUID which i like to call "named interfaces" or "IDed interfaces") are separated into their own tables identified by their GUID, even for one object/interface, so VMT will be alright no matter what inheritance is there, also VMT for each interface are agnostic for other GUID declaration.

That is not right. Interfaces inherit from each other, and while an class only ever implements explicitly used interfaces, if you inherit IBar from IFoo, IBar has all methods from IFoo. if I do this:

 

IFoo = interface
  procedure DoSomething;
end;

DoSomething is not the first but fourth method in the table as it derives from IInterface, which already has 3 methods.

If I do this:

IFoo = interface(IUnknown)
  procedure DoSomething;
end;

My method, when using the IUnknown from System.pas, is still the fourth method in the table. All those interfaces declared in the API deriving from IUnknown expect to have their first method, being the fourth in the table, too. And on the Windows implementing side, it is exactly this. They derive from IUnknown, which has 3 methods and does not derive from anything else. So the windows interfaces start with 4 as their first method slot, just like any interface you write in Delphi.

 

However, given that IUnknown was just copied from the metadata during generation, in Delphi it derives from IInterface, which is Delphis "IUnknown". That way, it introduces 3 more methods to the table, making DoSomething of IFoo the 7th method. Therefore on the delphi side if you call DoSomething on the interface coming from a windows api class, you're off by 3 and call something entirely else. 

 

15 hours ago, DelphiUdIT said:

In one post, @pcoder suggest the see others metadata provider (like this https://www.winsoft.sk/win32api.htm ).

If you look insede of those sources, you will look that more types were derivered form SYSTEM unit, like IUNKNOWN for example.

Try this instead WINMD. 

Looked into it. That one looks correct! Thanks for sharing this!

Edited by Memnarch

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×