开发者

Accessing underlying managed object through a COM interface

I have a third party assembly with a public abstract class implementing a certain COM interface. Something to the effect of

[ComVisible(true)]
public abstract class SomeClass: ISomeInterface
{
  ....
  public void Method1() {...}
}

The actual object is an internal object extending the SomeClass and is instantiated by the third party code

Is there a way to access public methods of this class if all I have is the CCW to the 开发者_Python百科ISomeInterface?

To clarify my question:

I am building a flavored Visual Studio project extending the F# project system. Both my code and F# project system are managed, but there is a lot of unmanaged code between us. In my project manager I am getting a pointer (IntPtr) to the F# project manager and I can cast it to a number of interfaces F# project manager implements, but I need to call a (public) method on the project manager itself and so far I could not find a way to do that


It depends on whether the unpublished methods you're trying to call have an internal COM (or C++) implementation that you can't see. COM interfaces require the implementing objects to provide fixed-location vtables which can generally be accessed and interpreted from managed code even if they are not meant to be publicly consumed.

For native COM libraries, the COM vtable scheme generally corresponds with a binary image of the way things are set up at runtime internally anyway. Because the tables are non-movable and tied to the well-understood COM requirements, unsafe managed code can navigate through and figure out how to get to just about anything. To illustrate, you might define an interface IFoo in your code which parallels a secret interface of the COM library--perhaps containing a single method like so:

[ComImport, SuppressUnmanagedCodeSecurity, Guid("00000000-0000-0000-0000-123456789ABC")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IFoo
{
    int FooFunc(uint x);
}

Note that if you want to enable IUnknown and other CLR support for COM, then [ComImport] should be used here, even if this is your own unique interface declaration that isn't "importing" from anywhere. Now assuming this interface matches the runtime vtable of some external COM object, or even a non-COM native C++ object (given the appropriate adjustments), you can do stuff like:

var vtbl = *(IntPtr**)pObj; // ptr to a C++ style object, perhaps from ICustomMarshaller
// ...or...
var vtbl = *(IntPtr**)((IntPtr*)pUnk - 1); // or an IUnknown ptr instead

// since IUnknown methods are 0, 1, 2, vtable slot for FooFunc is likely '3'
// but to fetch it in a more "official way"...
var mi = typeof(IFoo).GetMethod(nameof(IFoo.FooFunc));
int slot_ix = Marshal.GetComSlotForMethodInfo(mi);

// fetch unmanaged function pointer to 'FooFunc'
IntPtr pfn = vtbl[slot_ix];

At this point, you'll need to declare a managed delegate in order to call through this function pointer. Because FooFunc is an instance call, the delegate needs to be bound with a relevant this instance, which in this case is pObj, so that it will insert that value as an 'invisible' first argument whenever FooFunc is called, as the function expects and requires. Note that you can't leverage the normal CLR mechanism of storing the relevant instance in the my_delegate.Target property, because that's only for managed objects, which the target in question here is not. Because of this, the extra argument becomes somewhat less than "invisible":

// important... note this crucial attribute and its argument ---v
[ComVisible(true), UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int __this_FooFunc([In] IntPtr _this, [In] uint x);

// create managed delegate for unmanaged function pointer from above
var __FooFunc = Marshal.GetDelegateForFunctionPointer<__this_FooFunc>(pfn);

// call it...      v--- but don't forget the 'this'
int i = __FooFunc(pObj, 1234U);   // call vtable-based function from managed code

Anyway, it all works, and it's not even that sketchy or controversial; we're basically just walking structures that are well-established in the COM specification, exactly like the CLR's default marshaling code does.

Now having said all this, if the COM functionality you're interested in originates from a managed environment, such as perhaps F# as you indicated, then very little or none of the above applies. Managed runtime environments have the opportunity to provide COM vtables on a more sophisticated basis, e.g.:

  • only for objects that need them,
  • only for the specific interfaces that are intended,
  • only on-the-fly or when demanded, and/or
  • only for as long as required or authorized.

Although I'm not sure about which of these points apply to the case you describe, it seems like some or all of them could vary depending on implementation details of the CLR host. Unfortunately, deployment by the host of any one of these optimizations is sufficient to foil the type of vtable snooping described above.


Generally, no. You can only get at methods in this COM interface and other COM interfaces on the same object.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜