-
Notifications
You must be signed in to change notification settings - Fork 994
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
Review COM interface declarations for conflicting ComImport/ComVisible attributes #1878
Comments
a quick search for However considering that the codebase comes from Desktop Framework I wonder if there should be any ComVisible(true) attributes left in place at all, even for classes and interfaces actually owned by WinForms. The ComVisible WinForms GUIDs registered globally always used to be Desktop Framework, I wonder how many of the ComVisible classes/interfaces owned by WinForms actually need to be ComVisible in .NET Core for supported interop scenarios. Maybe most (or all) of them can be removed? |
@hughbe @RussKie I don't think this is fixed, I still find plenty of wrong [ComVisible(true)] attributes. Needs to be reopened or creating a follow-up issue for proper cleanup. I also noticed new [ComVisible(true)] attributes were being added in accessibility PRs, flagged them, but I think this needs a bit care in reviews, at least for the current time being. |
If classes had The auditing is a must for 5.0, I don't want to see us inadvertently exposing this attribute in unintended places. |
I scrolled through all remaining ComVisible which can be found by "search references" in VS and they all (except one) are specified without GUID, meaning they get a generated GUID. I don't remember if I checked previously, but the generated GUID seems to be different from Desktop Framework, so having these ComVisible left over is probably acceptable since they cause no conflicts, but I still think most (if not all) ComVisible attributes are redundant. They certainly don't contribute to compatibility with Desktop Framework since there is no shared TLB (Desktop Framework has a TLB and primary interop assembly for WinForms, you'd have to be compatible with those if you wanted that level of compatibility). So users of WinForms via COM have to rewrite all their interop code anyways and this time they don't have a TLB to import either. So while I don't think its harmful it also doesn't seem to provide any value, and it will make AOT efforts like corert potentially more complicated if they have to predeclare CCWs for objects never intended to be accessible from COM. That said its probably still good to remove ComVisible later if you don't feel confident doing it for 5.0 - unless people start taking a dependency on them, but I don't know why anyone would be doing that.
The only negative impacts I currently can think of involve sharing a CLSID/IID with Desktop Framework, those cases should definitely be fixed. There is one ComVisible + GUID remaining on IWin32Window which is shared with Desktop Framework, the registry has a TLB entry pointing to the Desktop Framework version. .NET Core should probably have its own GUID or switch to ComImport, .NET Core is not the owner of that IID. |
@JeremyKuhne 我们的每个用户,都需要与当地的医疗保险系统或卫生信息系统对接,多使用COM互操作来实现。正是由于.NetCore3.1目前没有解决此问题,我们的对接模块不得不仍然停留在.NetFramwork4.6上。 |
By the way, what is the criteria for including them. I noticed that we do so for |
Surely having it on |
Also, would the fix be to simply have // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
[ComImport]
[Guid("458AB8A2-A1EA-4d7b-8EBE-DEE5D3D9442C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IWin32Window
{
/// <summary>
/// Gets the handle to the window represented by the implementor.
/// </summary>
IntPtr Handle { get; }
}
} |
As far as I'm aware the only thing left is to allow COM to call CoCreateInstance (call the default constructor). You still have to do some registration or manifest declarations for this case besides using the attribute. I might be missing other arcane use cases. Previously the most important usecase was to indicate "this class/interface/struct/enum is owned by this assembly and needs to be registered as such", including marking the types for TLB generation, but neither is currently implemented in .NET Core, you have to do it manually. If you do it manually then its a supported scenario of .NET Core, they brought that part of COM back with .NET Core 3.0 I think.
This was before I noticed that ComVisible without GUID is safe to use because you get a different GUID generated. Its still useless because AccessibleObject instances have no default constructor to be called from COM, and for exposing an existing instance you don't need that attribute.
Specifying Note that having a reference to WinForms is enough to require registration of the assembly, they don't need to have written explicit code for registering .NET Core Framework assemblies. This is how the default logic of regasm rolled, and everyone who has written custom ones will have oriented themselves on its logic.
You can do that as a "quick fix" to become safe, but it would give you a dependency on the Desktop Framework being installed if you ever call that interface across apartments. (This interface GUID is not a Windows API, its owned and installed by the .NET Desktop Framework WinForms TLB - yes they install a TLB with the Desktop Framework.) You probably should use [ComVisible(true)] and use a new GUID. |
@199621616 if you have a specific WinForms COM scenario that is broken can you please file an issue with details so we can track it? If it is an issue with .NET Core COM interop in general you should create the issue in https://github.com/dotnet/runtime. |
@AaronRobinsonMSFT do we have any best practices for porting code from .NET Framework to .NET Core that used to use |
@JeremyKuhne This is a declarative mechanism and depending on how .NET controls are being passed around/consumed this could result in some very complicated/nasty scenarios. As mentioned, if the interface is something that is new and desired to be provided to COM clients then For declaring an existing COM interface in .NET, the If one declares a type I suggest marking interfaces following the intent of each of the aforementioned attributes. I prefer to always provide a |
@AaronRobinsonMSFT thanks! When it comes to GUIDs and
Is this the right summary? |
This is the safest approach. There are some caveats here that permit having the same Guids in most theoretical cases. One of the most important is that all .NET objects are free threaded and don't need apartment marshalling. However, there are issues with COM aggregation scenarios that make this far more complicated in practice. |
A follow up question - our accessibility object implementations are decorated with winforms/src/System.Windows.Forms/src/System/Windows/Forms/AccessibleObject.cs Lines 21 to 46 in e3f5000
Is this correct? |
It doesn't cause problems because there's no Given that most AccessibleObject implementations are not createable from COM (they have a constructor requiring arguments) I personally would go for removing |
FYI in dotnet/runtime#37269
|
This comment has been minimized.
This comment has been minimized.
Ah found it under dotnet/runtime#37039 |
I'd prefer removing the |
We had a private discussion with Jeremy, and concluded that we should remove all @weltkante please let me know if you want to do it, or I'll set it up for grabs. |
@JeremyKuhne //创建地纬嵌入式接口对象,调用com组件 我们使用.NetFramework4.6.1调用方式: 使用同样的方式,在.NetCore3.1.4上运行返回异常: Example 1: com interoperability of medical insurance system docking: The insurance system interface specification provides Pb call examples: //Create the ground latitude embedded interface object and call COM component int vi Oleobject sei SEI = create oleobject / / create OLE object seiproxy vi= sei.connecttonewobject ('sei3 ') / / connect COM component if VI < > 0 then MessageBox ('error ',' failed to create SEI object of ground latitude embedded interface! ') return end if We use. Netframework 4.6.1 to call: Type type = Type.GetTypeFromProgID ("sei3", true); dynamic COM = Activator.CreateInstance (type); Using the same method, run return exception on. NETCORE 3.1.4: Creating an instance of the COM component with CLSID {D5D8D5B2-CCD5-4EEC-BE61-0CA13A1940C7} from the IClassFactory failed due to the following error: ffffdf8f 0xFFFFDF8F. |
@RussKie I'll put a PR up later today |
Awesome 👍 |
@199621616 I've created dotnet/runtime#37362 to follow up on your comment as it appears that you have a general COM interop issue. |
During review of PR #1868 I noticed
IOleInPlaceActiveObject
was declared with both[ComImport]
and[ComVisible(true)]
. This is wrong, ComVisible(true) is for interfaces owned by the assembly, ComImport is for external interfaces.In desktop framework this probably has little consequence since the assemblies come preinstalled, but now that its possible to deploy WinForms with an application it may more likely be processed by tools like regasm or TLB generators, which attempt to register COM interop owned by the assembly.
When ComVisible(true) is present this will cause registry corruption by overwriting the true owners registry entries (usually owned by Windows OS) and replacing them by .NET based values. For interfaces I believe this mostly replaces the ProxyStubClsid32 pointing to another implementation than the original entries used to have.
There should be a review for conflicting ComImport/ComVisible(true) entries, or better, just review ComVisible(true) in general and make sure it is only placed on classes/interfaces owned by WinForms.
The text was updated successfully, but these errors were encountered: