Find a fully qualified .NET assembly name programmatically (from a simple name, for a given version of .NET)?

My goal is to display the message box of .NET Windows forms from pure C ++, a native program at the Windows API level (not managed by C ++ or C ++ / CLI).

That is, for educational purposes, I want to implement the C # code shown in the comment below in pure C ++:

/*
    // C# code that this C++ program should implement:
    using System.Windows.Forms;

    namespace hello
    {
        class Startup
        {
            static void Main( string[] args )
            {
                MessageBox.Show(
                    "Hello, world!",
                    ".NET app:",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information
                    );
            }
        }
    }
*/

#include <stdexcept>
#include <string>
#include <iostream>
#include <stdlib.h>         // EXIT_SUCCESS, EXIT_FAILURE

#undef  UNICODE
#define UNICODE
#include <windows.h>

#include <Mscoree.h>
#include <comdef.h>
_COM_SMARTPTR_TYPEDEF( ICorRuntimeHost, IID_ICorRuntimeHost );      // ICorRuntimeHostPtr

// #import is an MS extension, generates a header file. Will be replaced with #include.
#import "C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\mscorlib.tlb" \
    raw_interfaces_only rename( "ReportEvent", "reportEvent" )
typedef mscorlib::_AppDomainPtr     AppDomainPtr;
typedef mscorlib::_ObjectHandlePtr  ObjectHandlePtr;
typedef mscorlib::_AssemblyPtr      AssemblyPtr;

bool throwX( std::string const& s ) { throw std::runtime_error( s ); }

template< class Predicate >
struct Is: Predicate
{};

template< class Type, class Predicate >
bool operator>>( Type const& v, Is< Predicate > const& check )
{
    return check( v );
}

struct HrSuccess
{
    bool operator()( HRESULT hr ) const
    {
        ::SetLastError( hr );
        return SUCCEEDED( hr );
    }
};

void cppMain()
{
    ICorRuntimeHostPtr  pCorRuntimeHost;
    CorBindToRuntimeEx(
        L"v1.1.4322",           // LPWSTR pwszVersion,  // RELEVANT .NET VERSION.
        L"wks",                 // LPWSTR pwszBuildFlavor, // "wks" or "svr"
        0,                      // DWORD flags,
        CLSID_CorRuntimeHost,   // REFCLSID rclsid,
        IID_ICorRuntimeHost,    // REFIID riid,
        reinterpret_cast<void**>( &pCorRuntimeHost )
        )
        >> Is< HrSuccess >()
        || throwX( "CorBindToRuntimeEx failed" );

    pCorRuntimeHost->Start()    // Without this GetDefaultDomain fails.
        >> Is< HrSuccess >()
        || throwX( "CorRuntimeHost::Start failed" );

    IUnknownPtr     pAppDomainIUnknown;
    pCorRuntimeHost->GetDefaultDomain( &pAppDomainIUnknown )
        >> Is< HrSuccess >()
        || throwX( "CorRuntimeHost::GetDefaultDomain failed" );

    AppDomainPtr    pAppDomain  = pAppDomainIUnknown;
    (pAppDomain != 0)
        || throwX( "Obtaining _AppDomain interface failed" );

    // This fails because Load requires a fully qualified assembly name.
    // I want to load the assembly given only name below + relevant .NET version.
    AssemblyPtr     pFormsAssembly;
    pAppDomain->Load_2( _bstr_t( "System.Windows.Forms" ), &pFormsAssembly )
        >> Is< HrSuccess >()
        || throwX( "Loading System.Windows.Forms assembly failed" );    

    // ... more code here, not yet written.
}

int main()
{
    try
    {
        cppMain();
        return EXIT_SUCCESS;
    }
    catch( std::exception const& x )
    {
        std::cerr << "!" << x.what() << std::endl;
    }
    return EXIT_FAILURE;
}

Plan, after loading the assembly, go to the class MessageBoxand call Show. But this may be the wrong way to do it. Therefore, I am equally satisfied with the answer showing how to do this without finding the full name of the assembly (of course, without hard coding, this is the full name!).

The utility gacutilcan obviously find fully qualified names:

C: \ test> gacutil / l System.Windows.Forms
Microsoft (R) .NET Global Assembly Cache Utility.  Version 4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

The Global Assembly Cache contains the following assemblies:
  System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL
  System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
  System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL

Number of items = 4

C:\test> _

, , : .NET, ++, , #, , .NET .

.

+3
2

Hm, , (, ):

.NET, .NET "Fusion" ( ?) , :

AssemblyPtr     pFormsAssembly;
pAppDomain->Load_2( _bstr_t( "System.Windows.Forms, Version=1.1.4322.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" ), &pFormsAssembly )
    >> Is< HrSuccess >()
    || throwX( "Loading System.Windows.Forms assembly failed" );
std::cout << "Loaded the assembly." << std::endl;

_bstr_t     displayName;
pFormsAssembly->get_ToString( displayName.GetAddress() )
    >> Is< HrSuccess >()
    || throwX( "_Assembly::get_ToString failed" );
std::cout << "\"" << displayName.operator char const*() << "\"" << std::endl;

Loaded the assembly.
"System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

, UUID ( b77a5c561934e089) , .

, # - ?

,

+3

Hmya, , . CLR , GAC, . EXE Main(), , .

, , Fusion , , GAC. gacutil/l API Fusion API, CreateAssemblyEnum() . IAssemblyEnum, GetNextAssembly() IAssemblyName. - , , .

, . . .

+1

Source: https://habr.com/ru/post/1771025/


All Articles