Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the type System.__ComObject claim (sometimes) to be public when it is not?

Tags:

Just an oddity I happened to discover when I was reflecting over all types to check something else out of curiosity.

Why does the class System.__ComObject of the assembly mscorlib.dll (sometimes?) claim to be public when in fact it seems to be non-public? If I run the following code in a simple C# console application:

var t = Type.GetType("System.__ComObject");
Console.WriteLine(t.IsPublic);   // "True"   ?!
Console.WriteLine(t.IsVisible);  // "False"

the output seems conflicting. A non-nested type (t.IsNested is false) should give the same truth value for IsPublic and IsVisible. When I look at the assembly with IL DASM I see:

.class private auto ansi beforefieldinit System.__ComObject
       extends System.MarshalByRefObject
{
} // end of class System.__ComObject

which, to me, looks very much like a non-public class, something which would correspond to the C# code below:

namespace System
{
    // not public
    internal class __ComObject : MarshalByRefObject
    {
        ...
    }
}

When I compare to another type which has a similar name, System.__Canon, and similar IL modifiers, both IsPublic and IsVisible return false as expected.

Does anyone know why (and when) Type.GetType("System.__ComObject").IsPublic gives true?

like image 972
Jeppe Stig Nielsen Avatar asked Aug 06 '13 21:08

Jeppe Stig Nielsen


1 Answers

Mono's implementation of __ComObject may give some clues. It is indeed declared as internal, but the comments say "It has no public methods, its functionality is exposed trough System.Runtime.InteropServices.Marshal". I haven't dug into Marshal, but I would assume it is responsible for the implementation of GetType(), so it could customize IsPublic property too.

In my experience, Type.GetType("System.__ComObject").IsPublic is always true. Regarding GetType("System.Net.Mail.MSAdminBase"), I believe it is a COM class exposed via a customized primary interop assembly, where the visibility of a type can be explicitly controlled (although it's just my thinking, no research has been done).

[UPDATE]

I've got the latest Framework source code and found I was wrong about my assumption that customization of IsPublic property for __ComObject Type is done by Marshal. Actually, it is done by unmanaged code. Object.GetType() is defined inside Object.cs like this:

[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern Type GetType(); 

The unmanaged source code for its implementation in .NET 4.x is not available, but it is available for .NET 2.0. There's an excellent answer about Object.GetType implementation in .NET 2.0. I'd just add, IsPublic is defined by System.Runtime.InteropServices._Type interface which System.Type derives from, and can be overridden by any of System.Type-descendent classes. Apparently, an implementation of such class is returned by Object.GetType, and that's happening inside ObjectNative::GetClass (sscli20\clr\src\vm\comobject.cpp), as shown in the answer:

if (!objRef->IsThunking())
    refType = typeHandle.GetManagedClassObject();
else
    refType = CRemotingServices::GetClass(objRef);

I confirm that the behavior of IsPublic depends on the Framework version. I tried the following PowerShell script in different VMs:

Write-Host ([System.Environment]::Version) ([Type]::GetType("System.__ComObject")).IsPublic

The output is:

2.0.50727.3643 False (WinXP)
4.0.30319.18052 True (Win7)
4.0.30319.19079 True (Win8)

Apparently, it has changed from False to True since .NET 2.0. I agree that True is inconsistent with the declaration of __ComObject (internal class __ComObject ...). I personally see no reason for such change, because the only way to get an instance of __ComObject is through interop.

like image 175
noseratio Avatar answered Oct 04 '22 22:10

noseratio