Is there any purpose of ComDefaultInterface for a COM Callable Wrapper? - c#

What is the purpose of ComDefaultInterfaceAttribute attribute, if the managed object with ClassInterfaceType.None is marshaled as either IUnknown or IDispatch, anyway?
Consider the following C# class AuthenticateHelper, which implements COM IAuthenticate:
[ComImport]
[Guid("79eac9d0-baf9-11ce-8c82-00aa004ba90b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAuthenticate
{
[PreserveSig]
int Authenticate(
[In, Out] ref IntPtr phwnd,
[In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pszUsername,
[In, Out, MarshalAs(UnmanagedType.LPWStr)] ref string pszPassword);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IAuthenticate))]
public class AuthenticateHelper: IAuthenticate
{
public int Authenticate(ref IntPtr phwnd, ref string pszUsername, ref string pszPassword)
{
phwnd = IntPtr.Zero;
pszUsername = String.Empty;
pszPassword = String.Empty;
return 0;
}
}
I've just learnt that .NET interop runtime separates its implementation of IUnknown from IAuthenticate for such class:
AuthenticateHelper ah = new AuthenticateHelper();
IntPtr unk1 = Marshal.GetComInterfaceForObject(ah, typeof(IAuthenticate));
IntPtr unk2 = Marshal.GetIUnknownForObject(ah);
Debug.Assert(unk1 == unk2); // will assert!
I've learn that while implementing IServiceProvder, because the following did not work, it was crashing inside the client code upon returning from QueryService:
[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out, MarshalAs(UnmanagedType.Interface, IidParameterIndex=1)] out object ppvObject
}
// ...
public readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
AuthenticateHelper ah = new AuthenticateHelper();
int IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out object ppvObject)
{
if (guidService == typeof(IAuthenticate).GUID && (riid == IID_IUnknown || riid == guidService))
{
ppvObject = this.ah; // same as ppvObject = (IAuthenticate)this.ah
return S_OK;
}
ppvObject = null;
return E_NOINTERFACE;
}
I naively expected the instance of AuthenticateHelper would be marshaled as IAuthenticate because the class declares [ComDefaultInterface(typeof(IAuthenticate))], so IAuthenticate is the only and the default COM interface implemented by this class. However, that did not work, obviously because the object still gets marshaled as IUnknown.
The following works, but it changes the signature of QueryService and makes it less friendly for consuming (rather than providing) objects:
[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out] out IntPtr ppvObject);
}
// ...
int IServiceProvider.QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
if (guidService == typeof(IAuthenticate).GUID && (riid == IID_IUnknown || riid == guidService))
{
ppvObject = Marshal.GetComInterfaceForObject(this.ah, typeof(IAuthenticate));
return S_OK;
}
ppvObject = IntPtr.Zero;
return E_NOINTERFACE;
}
So, why would I specify ComDefaultInterface at all, if it doesn't affect marshaling? The only other use I see is for type library generation.
It's unmanaged client COM code that calls my managed implementation of IServiceProvider::QueryService. Is there a way to make QueryService work in my example without resorting to low-level stuff like GetComInterfaceForObject?

The ComDefaultInterface attribute is only really useful if you have more than one interface implemented on a single object. The "first" interface exposed by an object can be important in certain cases, but the order is not actually specified by the language. The attribute forces the interface you specify to be emitted first, with any others coming in a non-specified order.
It is also meant for classes that you are exporting from managed code to COM, so that clients who get your class returned to them in ways other than CoCreateObject get the correct 'default' interface (e.g. if your class is marked as [ClassInterface(ClassInterfaceType.None)]).
For imported classes that you work with via managed code, or classes that only implement a single interface, the attribute is harmless but essentially useless.
Also, as far as your last question, you rarely have to resort to low-level interface querying when using COM objects in fully managed code. The C# compiler will automatically handle the QueryInterface calls if you use the normal as and is type coercion keywords. In your case, AuthenticationHelper is being created as a managed AuthenticationHelper class because that's what you asked for; if you know what interface you want and you know it's implemented, ask for that:
AuthenticateHelper ah = new AuthenticateHelper();
IAuthenticate ia = ah as IAuthenticate;

Related

How to use D3D12GetDebugInterface API from c#?

I am currently trying to use Direct3D from c# and I've started by using the D3D12GetDebugInterface API.
The C++ syntax of the function is as follows (copied from the Microsoft documentation):
HRESULT D3D12GetDebugInterface(
REFIID riid,
void **ppvDebug
);
I'm having trouble importing the function into C#. I thought that maybe riid should be a pointer to a Guid struct, and I'm just using an IntPtr for **ppvDebug. Since **ppvDebug is a pointer to a pointer to an ID3D12Debug interface, I tried reimplementing the ID3D12Debug interface in C# code and using Marshal.PtrToStructure() to resolve the IntPtr to a usable ID3D12Debug interface instance, but that won't work. I remember reading about the ID3D12Debug interface being a COM object, but don't you need an ID for a COM object so you can import it? I haven't found any sort of COM ID anywhere in the documentation.
Anyway here's my latest attempt at getting something out of the function:
[DllImport("D3D12.dll")]
static extern int D3D12GetDebugInterface(IntPtr riid, IntPtr ppvDebug);
void func() {
unsafe
{
IntPtr DebugControllerPtr = IntPtr.Zero;
Type InterfaceType = typeof(ID3D12Debug);
Guid ID = InterfaceType.GUID;
IntPtr ptr = Marshal.AllocHGlobal(sizeof(Guid));
Marshal.StructureToPtr(ID, ptr, false);
D3D12GetDebugInterface(ptr, DebugControllerPtr);
Marshal.FreeHGlobal(ptr);
ID3D12Debug DebugController = null;
Marshal.PtrToStructure(DebugControllerPtr, DebugController);
DebugController.EnableDebugLayer();
}
}
In case you want to see my ID3D12Debug interface:
interface ID3D12Debug
{
void EnableDebugLayer();
}
As I said, I think Direct3D makes use of COM, which I am completely missing here, so maybe that's why it doesn't work.
There are usually many ways to declare interop code. Here is one that should work:
public static void Main()
{
D3D12GetDebugInterface(typeof(ID3D12Debug).GUID, out var obj);
var debug = (ID3D12Debug)obj;
debug.EnableDebugLayer(); // for example
}
[DllImport("D3D12")]
public static extern int D3D12GetDebugInterface([MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppvDebug);
[Guid("344488b7-6846-474b-b989-f027448245e0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ID3D12Debug
{
[PreserveSig]
void EnableDebugLayer();
}
You must add interface Guid and InterfaceType attributes for COM interfaces.
Guid can be passed easily by reference using UnmanagedType.LPStruct
You don't need unsafe code here.
You should check for errors (undone in my code)
If you need .NET interface definitions with DirectX, you can use this open source project here: https://github.com/smourier/DirectN, for example: https://github.com/smourier/DirectN/blob/master/DirectN/DirectN/Generated/ID3D12Debug.cs

COM interop and marshaling of VARIANT(VT_PTR)

We use a 3rd party COM object, one of which methods under certain conditions returns a VARIANT of VT_PTR type. That upsets the default .NET marshaler, which throws the following error:
Managed Debugging Assistant 'InvalidVariant' : 'An invalid VARIANT was
detected during a conversion from an unmanaged VARIANT to a managed
object. Passing invalid VARIANTs to the CLR can cause unexpected
exceptions, corruption or data loss.
Method signatures:
// (Unmanaged) IDL:
HRESULT getAttribute([in] BSTR strAttributeName, [retval, out] VARIANT* AttributeValue);
// C#:
[return: MarshalAs(UnmanagedType.Struct)]
object getAttribute([In, MarshalAs(UnmanagedType.BStr)] string strAttributeName);
Is there an elegant way to bypass such marshaler's behavior and obtain the underlying unmanaged pointer on the managed side?
What I've considered/tried so far:
A custom marshaler:
[return: MarshalAs(UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(IntPtrMarshaler))]
object getAttribute([In, MarshalAs(UnmanagedType.BStr)] string strAttributeName);
I did implement IntPtrMarshaler, just to find the interop layer crashing the process even before any of my ICustomMarshaler methods gets called. Perhaps, the VARIANT* argument type is not compatible with custom marshalers.
Rewrite (or clone) the C# interface definition with getAttribute method redefined (like below) and do all the marshaling for output VARIANT manually:
void getAttribute(
[In, MarshalAs(UnmanagedType.BStr)],
string strAttributeName,
IntPtr result);
This doesn't seem nice (the interface itself has 30+ other methods). It'd also break existing, unrelated pieces of code which already make use of getAttribute without issues.
Obtain an unmanaged method address of getAttribute from vtable (using Marshal.GetComSlotForMethodInfo etc), then do the manual invocation and marshaling against my own custom delegate type (using Marshal.GetDelegateForFunctionPointer etc).
So far, I've taken this approach and it seem to work fine, but it feels as such an overkill for what should be a simple thing.
Am I missing some other feasible interop options for this scenario? Or, maybe there is a way to make CustomMarshaler work here?
What I would do is define a simple VARIANT structure like this:
[StructLayout(LayoutKind.Sequential)]
public struct VARIANT
{
public ushort vt;
public ushort r0;
public ushort r1;
public ushort r2;
public IntPtr ptr0;
public IntPtr ptr1;
}
And the interface like this;
[Guid("39c16a44-d28a-4153-a2f9-08d70daa0e22"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface MyInterface
{
VARIANT getAttributeAsVARIANT([MarshalAs(UnmanagedType.BStr)] string strAttributeName);
}
Then, add an extension method somewhere in a static class like this, so the caller can have the same coding experience using MyInterface:
public static object getAttribute(this MyInterface o, string strAttributeName)
{
return VariantSanitize(o.getAttributeAsVARIANT(strAttributeName));
}
private static object VariantSanitize(VARIANT variant)
{
const int VT_PTR = 26;
const int VT_I8 = 20;
if (variant.vt == VT_PTR)
{
variant.vt = VT_I8;
}
var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf<VARIANT>());
try
{
Marshal.StructureToPtr(variant, ptr, false);
return Marshal.GetObjectForNativeVariant(ptr);
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
}
This will do nothing for normal variants, but will just patch it for VT_PTR cases.
Note this only works if the caller and the callee are in the same COM apartement.
If they are not, you will get the DISP_E_BADVARTYPE error back because marshaling must be done, and by default, it will be done by the COM universal marshaler (OLEAUT) which only support Automation compatible data types (just like .NET).
In this case, theoratically, you could replace this marshaler by another one (at COM level, not at NET level), but that would mean to add some code on C++ side and possibly in the registry (proxy/stub, IMarshal, etc.).
For my own future reference, here's how I ended up doing it, using the 3rd option mentioned in the question:
[ComImport, Guid("75A67021-058A-4E2A-8686-52181AAF600A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInterface
{
[return: MarshalAs(UnmanagedType.Struct)]
object getAttribute([In, MarshalAs(UnmanagedType.BStr)] string strAttributeName);
}
private delegate int IInterface_getAttribute(
IntPtr pInterface,
[MarshalAs(UnmanagedType.BStr)] string name,
IntPtr result);
public static object getAttribute(this IInterface obj, string name)
{
var ifaceType = typeof(IInterface);
var ifaceMethodInfo = ((Func<string, object>)obj.getAttribute).Method;
var slot = Marshal.GetComSlotForMethodInfo(ifaceMethodInfo);
var ifacePtr = Marshal.GetComInterfaceForObject(obj, ifaceType);
try
{
var vtablePtr = Marshal.ReadIntPtr(ifacePtr);
var methodPtr = Marshal.ReadIntPtr(vtablePtr, IntPtr.Size * slot);
var methodWrapper = Marshal.GetDelegateForFunctionPointer<IInterface_getAttribute>(methodPtr);
var resultVar = new VariantClass();
var resultHandle = GCHandle.Alloc(resultVar, GCHandleType.Pinned);
try
{
var pResultVar = resultHandle.AddrOfPinnedObject();
VariantInit(pResultVar);
var hr = methodWrapper(ifacePtr, name, pResultVar);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
if (resultVar.vt == VT_PTR)
{
return resultVar.ptr;
}
try
{
return Marshal.GetObjectForNativeVariant(pResultVar);
}
finally
{
VariantClear(pResultVar);
}
}
finally
{
resultHandle.Free();
}
}
finally
{
Marshal.Release(ifacePtr);
}
}

Returned managed object method not called from C++ in COM interop

This is a follow up from my previous post. Read that post for context. Note that it is not strict COM interop - but the C++ interfaces are COM compatible.
Im trying to implement this C++ interface in C#
class IPluginFactory : public FUnknown
{
virtual tresult PLUGIN_API createInstance (FIDString cid, FIDString iid, void** obj) = 0;
};
My C# code looks like this:
[ComImport]
[Guid(Interfaces.IPluginFactory)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginFactory
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown), In, Out] ref object instance);
}
The implementation assigns a new object instance to the 'instance' parameter and returns 0 (S_OK). I even cast to the expected (managed) interface.
instance = (IPluginBase)new PluginBase();
return 0;
The object returned is represented by this C++ interface:
class IPluginBase: public FUnknown
{
public:
virtual tresult PLUGIN_API initialize (FUnknown* context) = 0;
virtual tresult PLUGIN_API terminate () = 0;
};
Which looks like this in my C# implementation:
[ComImport]
[Guid(Interfaces.IPluginBase)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginBase
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 Initialize([MarshalAs(UnmanagedType.IUnknown), In] object context);
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 Terminate();
}
In the unmanaged C++ test application I wrote, I can successfully call createInstance and receive a non-null pointer the code uses as a IPluginBase*.
The problem comes when I try to call the 'initialize' method on this IPluginBase* pointer. It never reaches the managed code (no breakpoint is hit - other breakpoint work fine) and the return code is 0x80004003. It looks like the wrapper does some intercepting here...
My question is: is the declaration of the managed representation of CreateInstance correct?
What am I missing here? (this should be plain vanilla interop, should it not?)
Other suggestions on declaration 'style' are also welcome.
Thanx, Marc.
EDIT: The problem seems to lie with the createInstance method. I am unable to get the interface returned that is asked for by the iid parameter.
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 1), In, Out] ref object instance);
I have also tried UnmanagedType.Interface in combination with the IidParameterIndex but both result in a IUnknown being marshaled back. If I re-query the IPluginBase interface the IPluginBase::initialize method works (breakpoint in managed code hits).
EDIT: The problem seems to lie with the createInstance method. I am
unable to get the interface returned that is asked for by the iid
parameter.
Specifying IidParameterIndex doesn't help here. Your implementation of createInstance should look like this:
public int createInstance(ref Guid classId, ref Guid riid, ref IntPtr instance)
{
if (instance != IntPtr.Zero)
return E_POINTER;
// substitute your actual object creation code for CreateObject
object obj = CreateObject(classId)
// return the correct interface
IntPtr unk = Marshal.GetIUnknownForObject(obj);
try
{
return Marshal.QueryInterface(unk, ref riid, out instance);
}
finally
{
Marshal.Release(unk);
}
}

Exposing indexer like properties to COM

I have in existing COM-interface. I wan't to create a .net assembly that exposes a new interface as COM (with a new GUID), but the structure of the interface needs to be the same.
How can i create a .net class (C#) that exposes this interface?
[
odl,
uuid(1ED4C594-DDD7-402F-90DE-7F85D65560C4),
hidden,
oleautomation
]
interface _IFlashPhase : IUnknown {
[propget]
HRESULT _stdcall ComponentName(
[in] short i,
[out, retval] BSTR* pVal);
[propput]
HRESULT _stdcall ComponentName(
[in] short i,
[in] BSTR pVal);
[propget]
HRESULT _stdcall ComponentMolePercent(
[in] short i,
[out, retval] double* pVal);
[propput]
HRESULT _stdcall ComponentMolePercent(
[in] short i,
[in] double pVal);
[propget]
HRESULT _stdcall ComponentFugacity(
[in] short i,
[out, retval] double* pVal);
[propput]
HRESULT _stdcall ComponentFugacity(
[in] short i,
[in] double pVal);
};
Your IDL isn't valid, an interface that is attributed with [oleautomation] should derive from IDispatch, not IUnknown. I'll give the proper declarations and hint where you need to modify them to get yours.
You cannot declare indexed properties in C#, the C# team refuses to implement them. Version 4 has support for indexed properties that are declared in a COM type library but still doesn't allow declaring them yourself. The workaround is to use the VB.NET language, it has no qualms about it. Add a VB.NET class library project to your solution. Make it look similar to this:
Imports System.Runtime.InteropServices
Namespace Mumble
<ComVisible(True)> _
<Guid("2352FDD4-F7C9-443a-BC3F-3EE230EF6C1B")> _
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface IExample
<DispId(0)> _
Property Indexer(ByVal index As Integer) As Integer
<DispId(1)> _
Property SomeProperty(ByVal index As Integer) As String
End Interface
End Namespace
Note the use of <DispId>, dispid 0 is special, it is the default property of an interface. This corresponds to the indexer in the C# language.
All you need VB.NET for is the declaration, you can still write the implementation of the interface in the C# language. Project + Add Reference, Projects tab and select the VB.NET project. Make it look similar to this:
using System;
using System.Runtime.InteropServices;
namespace Mumble {
[ComVisible(true)]
[Guid("8B72CE6C-511F-456e-B71B-ED3B3A09A03C")]
[ClassInterface(ClassInterfaceType.None)]
public class Implementation : ClassLibrary2.Mumble.IExample {
public int get_Indexer(int index) {
return index;
}
public void set_Indexer(int index, int Value) {
}
public string get_SomeProperty(int index) {
return index.ToString();
}
public void set_SomeProperty(int index, string Value) {
}
}
}
You need to run Tlbexp.exe on both the VB.NET and the C# assembly to generate the type libraries. The C# one with the implementation includes the VB.NET one.
To get the interface to derive from IUnknown instead of IDispatch, edit the interface declaration. Remove the DispId attributes and use ComInterfaceType.InterfaceIsUnknown.

Strange consequences of "ref" keyword in C# to COM interop

Consider an excerpt from code that can be found here:
namespace WinSearchFile
{
public class Parser
{
[DllImport("query.dll", CharSet = CharSet.Unicode)]
private extern static int LoadIFilter (string pwcsPath, ref IUnknown pUnkOuter, ref IFilter ppIUnk);
[ComImport, Guid("00000000-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IUnknown
{
[PreserveSig]
IntPtr QueryInterface( ref Guid riid, out IntPtr pVoid );
[PreserveSig]
IntPtr AddRef();
[PreserveSig]
IntPtr Release();
}
private static IFilter loadIFilter(string filename)
{
IUnknown iunk = null;
IFilter filter = null;
// Try to load the corresponding IFilter
int resultLoad = LoadIFilter( filename, ref iunk, ref filter );
if (resultLoad != (int)IFilterReturnCodes.S_OK)
{
return null;
}
return filter;
}
}
Parser::loadIFilter() in that code basically calls LoadIFilter() function. The latter looks up the registry, finds which class id corresponds to the specified file extension, instantiates a corresponding COM class (calls CoCreateInstance()) and calls IPersistFile::Load() from it.
Now the problem is that the signature for LoadIFilter() is the following:
HRESULT __stdcall LoadIFilter( PCWSTR pwcsPath, __in IUnknown *pUnkOuter, __out void **ppIUnk );
so the second parameter is IUnknown* of the aggregating object. If the COM class for the extension of interest doesn't support aggregation and the IUnknown* passed is not null CoCreateInstance() returns CLASS_E_NOAGGREGATION and so does LoadIFilter().
If I remove the ref keyword from the pUnkOuter parameter in the declaration and at the site of LoadIFilter() call the function is called with null IUnknown*. If I retain the ref keyword the function is called with non-null IUnknown* and returns CLASS_E_NOAGGREGATION for classes that don't support aggregation.
My question is - why is non-null IUnknown* passed when the keyword is retained? IUnknown iunk local variable is initialized to null so where does non-null IUnknown* come from in the invoked unmanaged code?
When you use the ref, you're not actually sending in the null, you're sending in a reference to where that null is stored, but when you're sending in without the ref you're sending in the actual value, which is null.
So:
With ref it's a reference to a null pointer.
Without it it's just a null pointer.
Edit: If I understand your question correctly, which I'm not 100% certain off...
Using ref is simply wrong, your IUnknown is already passed as a pointer since it is an interface. Passing ref would be equivalent to IUnknown**
The non-null bit comes from inside the method - it is instantiating an object onto the reference you've provided. Using the ref keyword on a reference type will pass the callers reference to the object, not creating another reference to the object (which is what happens when you pass by reference normally). Try this:
static void Main()
{
object foo = null;
SetMyObject(ref foo);
bool test = foo == null;
}
public static void SetMyObject(ref object foo)
{
foo = new object();
}
The variable test will be false.

Categories

Resources