tags:

views:

93

answers:

4

Is there any way in C# to access a COM object's virtual method table in order to get a function's address?

A: 

Are you sure you are asking about C#?

You shouldn’t need to get function addresses in C#, irrespective of whether a method is virtual or not. You can instantiate a delegate, which holds a managed reference to the method. If you instantiate a delegate with a virtual method, then the method will be called virtually, i.e. it will call the most derived override.

There is no need in C# to read system internals like the raw vtable.

Timwi
A: 

The closest I can think of is Marshal.GetFunctionPointerForDelegate, although this is at least one level removed from the unmanaged COM method (since the COM call will be wrapped in a .NET delegate).

What do you need this information for?

Tim Robinson
+2  A: 

You can't dig the native interface pointer out of the RCW. But you can call Marshal.GetComInterfaceForObject(), should be good as long as you ask for the right interface. From there, you can Marshal.ReadIntPtr() to get the v-table entries. Offset 0 is QueryInterface, 4 is AddRef, 8 is Release, 12 is the first interface method, etc. Double that for x64 code. Marshal.GetComSlotForMethodInfo() is an option.

Actually calling the method requires Marshal.GetDelegateForFunctionPointer(), you need to declare the delegate with the exact signature of the COM interface method. That won't be the signature it has when you call this method normally, it is the [PreserveSig] signature. In other words, a function that returns HRESULT and a ref argument if it returns a value.

Opportunities to bomb your program are plentiful.


After update: you need Marshal.GetFunctionPointerForDelegate and Marshal.WriteIntPtr to patch the v-table slot entry.

Hans Passant
This almost gets me there, but I'm stuck trying to figure out how to tell GetComInterfaceForObject the interface I want. I'm trying with the DirectSound library and I've tried passing in typeof(DirectX.PrivateImplementationDetails.IDirectSoundBuffer8), but that gives me an ArgumentException.
lfalin
I dunno, what's the object type? What kind API is this?
Hans Passant
The object is a DirectSound Buffer (or SecondaryBuffer). The COM object provides the IDirectSoundBuffer8 interface.
lfalin
This sounds like the old .NET 1.1 Managed DirectX wrappers. It's SecondaryBuffer class is *not* a COM interop type. There is no type library for DirectX.
Hans Passant
+1  A: 

After a lot of searching and piecing together different partial solutions, I figured out how to do it.

First you need to define the COM coclass for the object you're trying to access:

[ComImport, Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISomeCOMInterface
{
   // Define interface methods here, using PInvoke conversion between types
}

Next you need to instantiate the COM object. There are a couple of ways to do that. Since I was interested in DirectSound, I used:

[DllImport("dsound.dll", EntryPoint = "DirectSoundCreate", ...]
static extern void DirectSoundCreate(IntPtr GUID, [Out, MarshalAs(UnmanagedType.Interface)] out IDirectSound directSound, IntPtr pUnkOuter);

IDirectSound directSound;
DirectSoundCreate(IntPtr.Zero, out directSound, IntPtr.Zero);

Since I now had my COM object, I could then use Hans' suggestion of Marshal.GetComInterfaceForObject():

IntPtr comPtr = Marshal.GetComInterfaceForObject(directSound, typeof(IDirectSound));
IntPtr vTable = Marshal.ReadIntPtr(comPtr);

As an added bonus, you can then iterate through the vtable functions like this:

int start = Marshal.GetStartComSlot(typeof(IDirectSound));
int end = Marshal.GetEndComSlot(typeof(IDirectSound));

ComMemberType mType = 0;
for (int i = start; i < end; i++)
{
    System.Reflection.MemberInfo mi = Marshal.GetMethodInfoForComSlot(typeof(IDirectSound), i, ref mType);
    Console.WriteLine("Method {0} at address 0x{1:X}", mi.Name, Marshal.ReadIntPtr(vTable, i * Marshal.SizeOf(typeof(IntPtr))).ToInt64());
}

Extra Reading / References:

lfalin