runtime: Dynamic keyword not working against COM objects
I’m working on porting one of my applications (Markdown Monster) to .NET Core 3.0 and it’s going good except for my COM interop functionality that uses dynamic
to access various COM components inside of the Web Browser control.
Using raw Reflection instead of dynamic works, but it would be a lot of code in my case that has to be converted to make the Interop with with the more clumsy syntax and type casting.
Specifically I’m getting back objects from the Web Browser control and then use dynamic to call other functionality. All of that doesn’t work with .NET Core. I previously mentioned this and at the time it looked like there was already work underway to make this work in 3.0, with no fixes scheduled for 2.x. I talked about this in a blog post here. The original discussion I referenced that mentioned fixes for 3.0 where in https://github.com/dotnet/corefx/issues/32630.
But now we’re in 3.0 Preview and it’s still not working.
Here’s what I am doing:
// Get the JavaScript Ace Editor Instance
dynamic doc = WebBrowser.Document;
// fails
dynamic window = doc.parentWindow;
Replacing the code with manual reflection does work:
// this does
object window = ReflectionUtils.GetPropertyCom(doc, "parentWindow");
Status
There were previous issues open on this some time ago and at the time the word was that .NET Core 3.0 was going to fix this. It’s not working on 2.x either at the time the call was won’t fix for 2.x but will fix for 3.0.
Apparently it’s not fixed in 3.0.
Is there any definite word on whether this will get addressed?
For me this is pretty big blocker in this app. I have tons of interop calls, and using dynamic is a key language feature that makes this code much more manageable (and also more performant due dynamic’s internal caching etc) than using Reflection.
FWIW I already have an abstraction layer around the Editor/COM interop calls, but dynamic is such a core feature that it never occurred to me to abstract that further. I think if you want decent Windows Desktop support dynamic really should be working with COM objects.
Using latest .NET Core 3.0 Preview 4 SDK.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 22
- Comments: 23 (13 by maintainers)
@AaronRobinsonMSFT
This means the C# 4.0 COM interop work that implicitly uses ‘dynamic’ for IDispatch interfaces will not yet be available under .NET Core 3.0.
A consequence would be that most Office automation code cannot move from .NET Framework to .NET Core 3.0. This is because, even where Primary Interop Assemblies are used for early-bound COM access, the implicit interpretation of IDispatch as “dynamic” under C# is used extensively. For example, this code driving Excel works fine under C# 4 / NET Framework 4, but will not compile under .NET Core 3.0, even though everything seems to be early-bound and there is no ‘dynamic’ in sight:
This will be a show-stopper for moving a substantial class of Windows applications to .NET Core 3.0.
Please keep this issue open and high on your priority list for a future release.
Here’s a class I am using currently as a workaround for this:
And an example using it (in this case automating Photoshop):
@sagebind: I extended your helper class by wrapping the returned objects recursively, if they are not primitive types.
I then apply this to the result (
result = WrapIfRequired(result);
) inTryGetMember
andTryInvokeMember
. It saves you from having to wrap returned objects manually.Btw.: When
dynamic
was introduced in C# 4.0, the primary use case mentioned was to make it simpler to talk to COM objects like Word.Application. So, it seems logical to port it to .NET Core.@RickStrahl Thanks for understanding.
Indeed. I can assure you this wasn’t an easy decision. In the spirit of transparency let me elaborate on the calculus used to make this decision.
The above assumption is correct and in many cases that fact is enough to let us port code over. However, in the case of
dynamic
supporting COM the amount of code required to add was on the order of thousands of lines of customized special cases. In general this is not much of an issue but adding this much code to the Dynamic Language Runtime (DLR) for .NET Core would have also required pulling in a substantial amount of testing as well. The combined cost of product work with testing debt was too much to pay in this release. There is also the unfortunate fact that many of the people with expert knowledge about the DLR are no longer on the .NET team which makes these kind of large feature changes difficult to perform in confidence. The final reason compounds all of the above and that is customer need. To be sure there are many COM users of thedynamic
keyword but compared to other interop scenarios the desire for this one is smaller and even though the workaround is incredibly annoying and ugly - a workaround does exist.There is no question this support can be added back in the future, but for right now it is just too expensive.
If anyone is coming from a search engine to this issue, I’ve tried to copy and paste this together into a coherent solution:
@OJacot-Descombes @sagebind I took your guys work and added support to use
ComObject
s as values inset
methods:@zhusheping Implementing a general approach to indexed properties is not trivial. If you can get away with a more specific implementation, it could be as simple as the following:
@sagebind Are reference counts of returned objects being managed by the CLR, or do we need to track and release them ourselves (probably the later).
Here are two references from the C# 4.0 release in 2009 that describe the features implemented through dynamic binding support for COM and indexed properties in C# for COM libraries:
As far as I know it is still not possible to explicitly create indexed properties in C#, which makes it hard to create a library that works the same as a PIA when referenced (thus implementing the dynamic binder support in user code as a workaround).
It really would help to light up these COM interop features again for .NET 5.
While we are waiting for the official .NET 5 release for official support, I improved the
WrapIfRequired()
method:@OJacot-Descombes @sagebind @lauxjpn To make it even more complete and support the invokation of members with
COMObject
s as arguments, I’ve extended theTryInvokeMember
method accordingly.@govert we are looking into getting this support in .NET 5 now, but I’m having some trouble repro-ing the compilation error you pointed out in https://github.com/dotnet/runtime/issues/12587#issuecomment-487058891
I took the sample at https://github.com/dotnet/samples/tree/master/core/extensions/ExcelDemo and added
workbook.Sheets[1].Name = "FirstSheet";
. It compiled with no errors (failed at run time like other scenarios needing the dynamic keyword + COM object support). What am I missing here?@jzabroski @elinor-fung added this support with https://github.com/dotnet/runtime/pull/33060. The link is farther above.
@lauxjpn , nice implemtation,
IDispose
support comes in handy. I’ve now updated the code to also dispose and protect against double dispose, and calling the disposed COM object (via an Unwrap method), thanks!Very nice! For completeness, I also link to our implementation.
@elinor-fung I’m very glad to hear you’re looking at this - thank you.
I have retraced my steps to the compile error, and compare with the ExcelDemo sample project you point to. It seems there is a difference on how the PIA assembly is referenced in the project file. In my test I made a new project and added the COM reference to the “Microsoft Excel 16.0 Object Library” in Visual Studio. It works except for the
IDispatch -> dynamic
conversions, so leads to the compile error in that line. I now see in the ExcelDemo Readme.md that adding the COM reference in Visual Studio is not currently supported (I’m using VS 16.4.5 / .NET Core 3.1.1):The result of adding the COM reference in Visual Studio looked like this in the .csproj:
While the ExcelDemo sample project has the same reference looking like this:
With the ExcelDemo-style reference, the compiler errors for this go away as you found and the types correctly show the
IDispatch -> dynamic
conversion, including indexed properties. Then at runtime I get the expectedRuntimeBinderException
that you’re working on.I experimented a bit, and the only issue is that
EmbedInteropTypes
is not set by default (theWrapperType
does not seem to matter). As far as I can see, that has the same effect as with the .NET Framework, which only supports theIDispatch->dynamic
magic with the Embed Interop Types option.So I think the compiler problem I report is really the known (small) tooling issue with COM references in Visual Studio / new-style .csproj files.
Ideally the tooling should be fixed to have this work (i.e. recognize as a COM reference and add the EmbedInteropTypes: true) for the case of a PIA embedded in a NUGet package too (this works fine for .NET Framework):
(
Microsoft.Office.Interop.Excel
is an unoffial NuGet package that contains the Excel 2013 PIA assembly.) Currently this package reference in .NET Core results in the same compiler error, but without the option to fix with a property on the reference in the UI or .csproj.I should say that all these tooling issues are secondary to the internal runtime support for the COM binder. Maybe you can help report these to the right teams.