Skip to content
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

Microsoft.VisualBasic.Information.TypeName returns wrong name for COM objects #35937

Closed
Nukepayload2 opened this issue May 7, 2020 · 19 comments · Fixed by #40584
Closed

Microsoft.VisualBasic.Information.TypeName returns wrong name for COM objects #35937

Nukepayload2 opened this issue May 7, 2020 · 19 comments · Fixed by #40584

Comments

@Nukepayload2
Copy link

** Environment **

.NET Core SDK (reflecting any global.json):
Version: 5.0.100-preview.3.20216.6
Commit: 9f62a32109

Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362

Office 365 Installed

** Reproducible on .NET Framework 4.8? **
No

** Steps to reproduce **

  1. Create a new .NET 5 VB project
  2. Run the following code:
Dim xl = CreateObject("Excel.Application")
Console.WriteLine(TypeName(xl))
xl.Quit()

** Expected behavior **
Output:

ApplicationClass

** Actual behavior **
Output:

__ComObject

** Remarks **
I have some VB.NET code snippets that was migrated from VBScript. They don't work correctly when running on .NET 5 or .NET Core 3.1, because TypeName doesn't return correct result for COM objects.

I believe this bug is caused by this function:

Friend Function LegacyTypeNameOfCOMObject(ByVal VarName As Object, ByVal bThrowException As Boolean) As String

The .NET Framework version of this function:

https://github.com/microsoft/referencesource/blob/a7bd3242bd7732dec4aebb21fbc0f6de61c2545e/Microsoft.VisualBasic/runtime/msvbalib/Information.vb#L181

If Microsoft.VisualBasic.Core is not a good place to use COM interop, you can put implementation of TypeNameOfCOMObject in Microsoft.VisualBasic.Forms . When Microsoft.VisualBasic.Forms is not found at runtime, return "__ComObject" .

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-Microsoft.VisualBasic untriaged New issue has not been triaged by the area owner labels May 7, 2020
@ghost
Copy link

ghost commented May 7, 2020

Tagging subscribers to this area: @cston
Notify danmosemsft if you want to be subscribed.

@AaronRobinsonMSFT
Copy link
Member

The change in

Friend Function LegacyTypeNameOfCOMObject(ByVal VarName As Object, ByVal bThrowException As Boolean) As String

Seems to be due to the lack of support for ITypeInfo in .NET Core. Some of this support has been returned. I don't know if it will work as expected since the support for ITypeInfo has many limitations in .NET Core, but it is worth a shot to try the reference source version in .NET Core and see if it works.

@cston cston added this to the 5.0.0 milestone Jul 6, 2020
@cston cston removed the untriaged New issue has not been triaged by the area owner label Jul 6, 2020
@ehasis
Copy link
Contributor

ehasis commented Jul 7, 2020

I tried to do the implementation based on the referencesource, declaring a IDispatch interface locally to test, but when call pDispatch.GetTypeInfo, it throws System.Runtime.InteropServices.COMException: A null reference pointer was passed to the code fragment. (0x800706F4) (original message was in my language). Tried to debug, not I could not find the root cause.

@danmoseley
Copy link
Member

@ehasis if you have a branch to share, perhaps @AaronRobinsonMSFT could have a quick look and maybe have a suggestion? nearly out of time to fix this for 5.0 release.

@AaronRobinsonMSFT
Copy link
Member

perhaps @AaronRobinsonMSFT could have a quick look and maybe have a suggestion? nearly out of time to fix this for 5.0 release.

Agree. I am close to vacation, next Monday, but if I can get a repo or branch to continue with I can take a look today or tomorrow.

@ehasis
Copy link
Contributor

ehasis commented Aug 6, 2020

@AaronRobinsonMSFT here is the branch: https://github.com/ehasis/runtime/tree/vb-typename
I tried another implementation inside TARGET_WINDOWS define, but not sure how to test it.

@AaronRobinsonMSFT
Copy link
Member

@ehasis Thanks for the project. The first issue here is the function we are updating (i.e. Microsoft.VisualBasic.Information.TypeName) isn't the function being called. I am observing the following callstack:

>	microsoft.visualbasic.core.dll!Microsoft.VisualBasic.CompilerServices.Versioned.TypeName(Object Expression) Line 108	Basic
 	ComName.dll!ComName.Program.Main(String() args) Line 7	Basic

We are calling Microsoft.VisualBasic.CompilerServices.Versioned.TypeName instead.

My application is

Imports System

Module Program
    Sub Main(args As String())
        Console.WriteLine("Hello World!")
        Dim xl = CreateObject("Excel.Application")
        Console.WriteLine(TypeName(xl))
        xl.Quit()
    End Sub
End Module

@danmosemsft Who is our resident Visual Basic expert? I have no idea how this language is supposed to work or how method lookup occurs. Based on Visual Studio, I would assume Information.TypeName is called.

@AaronRobinsonMSFT
Copy link
Member

@ehasis I think the function we actually need to update is the one in Versioned.vb - the reference source is at https://github.com/microsoft/referencesource/blob/a7bd3242bd7732dec4aebb21fbc0f6de61c2545e/Microsoft.VisualBasic/runtime/msvbalib/Helpers/Versioned.vb#L115-L135.

I think @cston is going to have to be the authority here since I really don't understand this layering at all. More than willing to help with any COM related issues though.

@danmoseley
Copy link
Member

@cston is it

@ehasis
Copy link
Contributor

ehasis commented Aug 7, 2020

Digging more about this problem, the thing got a bit confusing.

In the reference source, the Microsoft.VisualBasic.Information.TypeName calls Microsoft.VisualBasic.Information.LegacyTypeNameOfCOMObject that retrieve the name from the IDispatch.

But when compiled with .NET Framework, the code calls to Microsoft.VisualBasic.CompilerServices.Versioned.TypeName. Same happens when compiled with .NET Core, as noted by @AaronRobinsonMSFT.

The details is that in the reference source Microsoft.VisualBasic.CompilerServices.Versioned.TypeName calls Microsoft.VisualBasic.Information.TypeNameOfCOMObject that have a bit more complex implementation, but is very similar to Microsoft.VisualBasic.Information.LegacyTypeNameOfCOMObject.

The actual core implementation does not have this round trip.

@cston
Copy link
Member

cston commented Aug 7, 2020

But when compiled with .NET Framework, the code calls to Microsoft.VisualBasic.CompilerServices.Versioned.TypeName. Same happens when compiled with .NET Core, as noted by @AaronRobinsonMSFT.

Yes, the VisualBasic compiler remaps calls to Information.TypeName() to Versioned.TypeName().

The details is that in the reference source Microsoft.VisualBasic.CompilerServices.Versioned.TypeName calls Microsoft.VisualBasic.Information.TypeNameOfCOMObject

Yes, it will be necessary to implement Information.TypeNameOfCOMObject() and call that method from Versioned.TypeName().

@ehasis
Copy link
Contributor

ehasis commented Aug 7, 2020

Ok, I'll implement on that way.

@ehasis
Copy link
Contributor

ehasis commented Aug 8, 2020

@cston I have fully implemented the TypeName to match the reference source in the branch https://github.com/ehasis/runtime/tree/vb-typename, but it's still not working as expected. Still returns _ComObject for net5.0. Also, when the test runs with net461, expected for Scripting.Dictonary is Dictionary, while the actual is IDictionary.

@AaronRobinsonMSFT
Copy link
Member

@ehasis This is failing at https://github.com/ehasis/runtime/blob/d9ceef9814040a3f7a7037c418532859c2209314/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft/VisualBasic/Information.vb#L196.

The HRESULT is 0x800706F4 (RPC_X_NULL_REF_POINTER). This is coming from the actual COM stub not the runtime. Someone is going to have to debug through this more and root cause the why.

@AaronRobinsonMSFT
Copy link
Member

@ehasis Good news first - the code in your branch works as expected. Bad news, the IL Linker is being a pain and breaking the COM definitions for IDispatch and ITypeInfo. The Linker is removing "unused" methods and therefore breaks the vtable layouts for these types which results in all sorts of random A/Vs.

In order to fix this the Linker needs to be told to leave these types alone. Add the below XML to a file call "ILLinkTrim.xml", place that file next to src\libraries\Microsoft.VisualBasic.Core\src\Microsoft.VisualBasic.Core.vbproj, and rebuild. Once the Linker stops removing methods the new code does what is expected.

<linker>
  <assembly fullname="Microsoft.VisualBasic.Core">
    <!-- Required for type querying via IDispatch -->
    <type fullname="Microsoft.VisualBasic.CompilerServices.UnsafeNativeMethods/*"/>
  </assembly>
</linker>

@danmosemsft @stephentoub @cston Unsure what the policy is on adding new linker trim files. I am heading on vacation soon so will be unable to shepherd this work further.

@danmoseley
Copy link
Member

@eerhardt can you provide guidance

@eerhardt
Copy link
Member

There are a few tracking issues for COM + ILLinker:

The workaround for .NET 5 is what @AaronRobinsonMSFT has above - Add an ILLinkTrim.xml file preserving the types, so the ILLinker doesn't remove them. We have the same thing for CSharp and in CoreCLR's CoreLib:

<assembly fullname="Microsoft.CSharp">
<!-- Required for COM event dispatch -->
<type fullname="System.Runtime.InteropServices.ComEventsSink"/>
</assembly>

<!-- Accessed via native code. -->
<type fullname="System.Runtime.InteropServices.ComTypes.IEnumerable" />
<type fullname="System.Runtime.InteropServices.ComTypes.IEnumerator" />
<type fullname="System.Runtime.InteropServices.CustomMarshalers.*" />
<!-- Workaround for https://github.com/mono/linker/issues/378 -->
<type fullname="System.Runtime.InteropServices.IDispatch" />
<type fullname="System.Runtime.InteropServices.IMarshal" />
<type fullname="Internal.Runtime.InteropServices.IClassFactory2" />

@agocke
Copy link
Member

agocke commented Aug 10, 2020

@MichalStrehovsky Can you confirm that this is the appropriate fix for these COM scenarios in the framework?

@MichalStrehovsky
Copy link
Member

Yes. The other option would be to opt out the assembly from trimming - I assume the trimming that's problematic here is the trimming that happens at the framework build where everything is forced to trim, no matter if it's compatible or not.

COM is linker unfriendly and is supposed to get detected as such (I hope there's warning for this somewhere).

danmoseley pushed a commit that referenced this issue Aug 11, 2020
* Initial try of TypeName for ComObjects on Windows

* Implemented TypeNameOfCOMObject to use in Versioned.TypeName

* Separated tests of the TypeName for COM objects

* Utils.VBFriendlyName now matches reference source

* Moved TypeName for COM objects tests to VersionedTests

* Added ILLinkTrim.xml as a temporary solution as comented on #35937

* UnsafeNativeMethods are only available on Windows

* Test refactoration

* Skip COM interop test on Mono

* Explicit types in ILLinkTrim.xml

* Call GetTypeFromProgID with throwOnError true

* Disabled TypeName_ComObject test on Windows Nano
@ghost ghost locked as resolved and limited conversation to collaborators Dec 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants