runtime
runtime copied to clipboard
Is it correct way to check that interface is from .NET?
Our application supports scripting and we had a special code that does special processing if interface came from .NET. Old code in .NET Framework call QueryInterface with 65074F7F-63C0-304E-AF0A-D51741CB4A8D GUID that came from Object interface from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.tlb
It works great with .NET Framework but stopped working in .NET 6. Then I found that GUID for System.Object is generated in GenerateClassItfGuid in file interoputil.cpp. And now that GUID is fbec27f0-fc44-396c-8a8c-4c1993516f2d.
Is it correct way to check if interface is implemented on .NET side? If yes, will it will work in future versions of .NET?
Thank you
You can do
static bool DefinedInCorelib(Type type)
{
return type.Assembly == typeof(object).Assembly;
}
Thank you, but I need to check IUnknown from native application.
Is it correct way to check if interface is implemented on .NET side?
No. There is no supported way to do this and it violates some core principles of COM to be able to determine the language/runtime that is providing the server implementation.
If yes, will it will work in future versions of .NET?
It is not something we are likely to change, but the idea of using it in this manner isn't something we are incline to guarantee.
Can you please elaborate on the need to know if a COM object is implemented in a .NET language?
This issue has been marked needs-author-action and may be missing some important information.
@AaronRobinsonMSFT
Can you please elaborate on the need to know if a COM object is implemented in a .NET language?
Scripting Code completion in editor. If this is .NET object we can call .NET to get list of supported methods/properties to show in editor. Object that implements interface doesn't know anything about editor.
Debugger We have script debugger, so we can evaluate variables and show customer content of that object from interface.
COM We have public interfaces that exposed to outside world via native code using COM. The public interface is frozen and it is a wrapper around internal interface that may change. Internal interface can be implemented using one of 3 languages we are using. One of them is C#.
Just in case, it is not real COM object. We register root COM object via RegisterActiveObject and later it will be accessed from our test runner (different process) and its use limited by test runner only.
Anyway, then test runner calls any methods of that public interface and it must be marshalled from test runner to our process. Normally COM will marshal these calls via main thread which is correct. But some objects are thread safe and they can be called from any thread. These objects supports IAgileObject and in this case we would like COM to marshal these call using any thread. So when COM calls QueryInterface for IMarshal interface and internal interface support IAgileObject we return free threaded marshaller. One example is cancelling test itself. Test can call native function that in turn calls waiting function that does not process COM calls while waiting. For example WaitForMultipleObjects. And our test runner can call free threaded object to cancel wait and signal one of the handles.
But because all .NET objects now support IAgileObject and most of them are not thread safe we need to distinguish case when object is real agile object. And because none of our .NET objects supports IAgileObject, we added check that object is not from .NET.
Perhaps there are better ways to do these things but from what I see it will require a lot of effort without many benefits.
Vlad
If this is .NET object we can call .NET to get list of supported methods/properties to show in editor. Object that implements interface doesn't know anything about editor.
I see. This is unfortunate .NET Framework encouraged this type of inspection. An option here would be to create a check API for managed objects in the runtime.
Usage of ComWrappers for COM interop makes using ComWrappers.TryGetObject() the best choice. If the built-in COM interop is what is being used, the check is much harder and can be done with Marshal.GetObjectForIUnknown() and Marshal.IsComObject(). Note that the built-in COM interop path won't always work. If the managed COM instance was activated using COM, then the below approach will not work. The only way that is possible, but not advisable, is to do the QueryInterface you've mentioned.
[UnmanagedCallersOnly]
int IsManaged(IntPtr ptr)
{
// Fast path using ComWrappers.
if (ComWrappers.TryGetObject(ptr, object _))
return 1;
// Slow path for built-in COM interop.
var objMaybe = Marshal.GetObjectForIUnknown(ptr);
return Marshal.IsComObject(objMaybe) ? 1 : 0;
}
But because all .NET objects now support IAgileObject and most of them are not thread safe we need to distinguish case when object is real agile object.
All .NET objects are free threaded and as such "thread safe" with respect to their IUnknown implementation and COM definition. It is possible that a .NET object is written in a non-thread safe manner, but that is not indicated by applying the IAgileObject interface.
Thank you @AaronRobinsonMSFT for example.
Our application right now is on .NET Framework and we are moving it to .NET 6 so first IF statement will not work for us as it is for NET8 only. As far as I know we don't use any COM activation. We just pass objects that implements IUknown between native and managed worlds. Our app is native app that loads .NET runtime and then creates .NET objects that marshalled to native world as COM interfaces. After that native code can call these interfaces or .NET can call native code using native code interfaces passed to it.
I need to test how much slower it will work compared to QueryInterface. Obviously speed will be fine for editor, inspection and interprocess marshalling. But after I replied you, I found that we also used QueryInterface approach in scripting itself when we decide what type of wrapper create for scripting system. For native objects we create one type of wrapper and for .NET objects we create different type. I don't want our customers to complain that their scripts became much slower :) Some of our customers wrote quite complicated scripts that works ten's of minutes.
As for IAgileObject, we already used this interface on our native objects to tell .NET that this native interface is free threaded and to avoid switching to original thread when .NET calls methods of that interface via RCW. And then it seems logical to use that approach for marshalling COM calls. Perhaps it wasn't correct decision.
I think I can close this one. I got my answer. Thank you Aaron