Найти полное имя сборки .NET программным способом (от простого имени для данной версии .NET)? - PullRequest
3 голосов
/ 23 октября 2010

Моя цель - отобразить окно сообщения форм .NET Windows из чистой программы уровня API Windows на чистом C ++ (не управляемой C ++ или C ++ / CLI).

То есть, для целей обучения я хочу реализовать код C #, показанный в комментарии ниже, на чистом 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;
}

План, загрузив сборку, перейти к классу MessageBox и вызвать Show. Но это может быть ошибочным способом сделать это. Поэтому я в равной степени доволен ответом, показывающим, как это сделать, не находя полностью квалифицированное имя сборки (конечно, без жесткого кодирования этого полностью квалифицированного имени!).

Утилита gacutil, очевидно, способна найти полностью квалифицированные имена:

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, жестко закодированная в коде C ++, должна быть не больше, чем в исходном коде C #, показанном в комментарии сверху, плюс минимальная поддерживаемая версия .NET .

ТИА.,

Ответы [ 2 ]

3 голосов
/ 23 октября 2010

Хм, хорошо, частичный ответ на мой собственный вопрос (достаточно, чтобы двигаться вперед):

Я предоставляю составное полное имя с версией, установленной как минимальная поддерживаемая версия .NET, и .NET«Fusion» (но задокументировано ли это?) Находит явно совместимую сборку, хотя указанная указанная сборка отсутствует в GAC:

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) - это просто часть общего независимого от версии идентификатора сборки, но в идеале я бы также взял это динамически в коде.

В конце концов, это не такНе нужно указывать в исходном коде C # - это можно сделать?

Приветствия,

1 голос
/ 23 октября 2010

Хм, это не так, как обычно. После инициализации CLR вы затем загрузите сборку, которая не в GAC, созданную управляемым компилятором. Обычно это EXE-файл с методом Main (), но у вас, конечно, есть альтернативные варианты.

Если вы хотите продолжить это, то вам действительно нужно создать полностью определенное имя, Fusion требует, чтобы он знал, какую сборку выбрать из GAC. Эквивалентом gacutil / l является Fusion API, вы должны использовать CreateAssemblyEnum () для его итерации. Получает вам IAssemblyEnum, его метод GetNextAssembly () возвращает вам IAssemblyName. Должен быть какой-то жестко заданный алгоритм выбора, чтобы точно решить, какую версию вы бы предпочли.

Это не проблема, если вы используете сборку, созданную управляемым компилятором. Сборочные ссылки в проекте выбирают нужную версию. Я бы порекомендовал вам голову таким образом.

...