-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic keyword not working against COM objects #12587
Comments
@jkotas Any chance we can move this issue to the coreclr repo? |
@RickStrahl Given the current priorities and the possible workarounds this isn't support we are planning on adding in 3.0. However, this is something we will consider in a future release. |
That's disappointing... but I get it. This encompasses a large set of functionality, but it's not like that code doesn't exist already in full framework. For COM this should probably even live in Windows.Compatibility most likely. |
@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 There is no question this support can be added back in the future, but for right now it is just too expensive. |
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: using Microsoft.Office.Interop.Excel;
class Program
{
static void Main(string[] args)
{
Application app = new Application();
app.Visible = true;
Workbook wb = app.Workbooks.Add();
wb.Sheets[1].Name = "FirstSheet";
// ^^^^ Compiler error under .NET Core 3.0 as wb.Sheets[...] returns "object"
// Under C# 4 it would have been compiled and run as 'dynamic'
}
} 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. |
cc @jaredpar for FYI. I now realize this issue should be in corefx (dynamic COM binder), but keeping it here since I already moved it once - boo. |
Specific error message for COM and |
Here's a class I am using currently as a workaround for this: using System;
using System.Dynamic;
using System.Reflection;
// A small wrapper around COM interop to make it more easy to use.
private class COMObject : DynamicObject
{
private readonly object instance;
public static COMObject CreateObject(string progID)
{
return new COMObject(Activator.CreateInstance(Type.GetTypeFromProgID(progID, true)));
}
public COMObject(object instance)
{
this.instance = instance;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = instance.GetType().InvokeMember(
binder.Name,
BindingFlags.GetProperty,
Type.DefaultBinder,
instance,
new object[] { }
);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
instance.GetType().InvokeMember(
binder.Name,
BindingFlags.SetProperty,
Type.DefaultBinder,
instance,
new object[] { value }
);
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = instance.GetType().InvokeMember(
binder.Name,
BindingFlags.InvokeMethod,
Type.DefaultBinder,
instance,
args
);
return true;
}
} And an example using it (in this case automating Photoshop): dynamic application = COMObject.CreateObject("Photoshop.Application");
application.DisplayDialogs = 3; // transparent dynamic dispatch |
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. |
@sagebind: I extended your helper class by wrapping the returned objects recursively, if they are not primitive types. private static object WrapIfRequired(object obj)
{
if (obj != null && !obj.GetType().IsPrimitive) {
return new COMObject(obj);
}
return obj;
} I then apply this to the result ( Btw.: When |
@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 #12587 (comment) I took the sample at https://github.com/dotnet/samples/tree/master/core/extensions/ExcelDemo and added |
@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
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 I experimented a bit, and the only issue is that 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):
( 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. |
Property with indexer not work.such as: |
Thanks a lot for the details @govert. I think these are the issues tracking the tooling problems you hit:
For the |
@OJacot-Descombes @sagebind I took your guys work and added support to use public override bool TrySetMember(SetMemberBinder binder, object value)
{
_instance.GetType()
.InvokeMember(
binder.Name,
BindingFlags.SetProperty,
Type.DefaultBinder,
_instance,
new[]
{
value is ComObject comObject
? comObject._instance
: value
}
);
return true;
} @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: public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
result = WrapIfRequired(
_instance.GetType()
.InvokeMember(
"Item",
BindingFlags.GetProperty,
Type.DefaultBinder,
_instance,
indexes
));
return true;
} @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). |
@OJacot-Descombes @sagebind @lauxjpn To make it even more complete and support the invokation of members with public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
for (int i = 0; i < args.Length; i++)
{
if(args[i] is COMObject co)
args[i] = co.instance;
}
result = instance.GetType().InvokeMember(
binder.Name,
BindingFlags.InvokeMethod,
Type.DefaultBinder,
instance,
args
);
result = WrapIfRequired(result);
return true;
} |
While we are waiting for the official .NET 5 release for official support, I improved the private static object WrapIfRequired(object obj)
=> obj != null &&
obj.GetType().IsCOMObject
? new ComObject(obj)
: obj; |
If anyone is coming from a search engine to this issue, I've tried to copy and paste this together into a coherent solution:
|
Very nice! For completeness, I also link to our implementation. |
@lauxjpn , nice implemtation, |
@AaronRobinsonMSFT This issue is closed but I don't see a resolution. I see you marked it as done but I can't track back to what the resolution was. #12587 (comment) In my case, I am frankly just trying to figure out the right place to file bugs for the dynamic keyword, as well as cross-reference old Microsoft Connect DLR bugs I see mentioned on StackOverflow that seem related to a problem I'm experiencing. |
@jzabroski @elinor-fung added this support with #33060. The link is farther above. |
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:
Replacing the code with manual reflection does work:
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.
The text was updated successfully, but these errors were encountered: