2010-10-23 1 views
3

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

То есть, для целей, которые я хочу реализации кода 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.

ТИА.,

+0

Это очень интересное использование 'operator >>'. –

+0

определенно проверьте это [предложение] (http://area51.stackexchange.com/proposals/11464/code-review?referrer = aWNm_PdciyFqjFW8CUacGw2 «Обзор кода»). Там почти все, нужно немного больше поддержки. – greatwolf

ответ

1

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

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

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

+0

Спасибо, пока. По существу (если не найден другой способ достижения более высокого уровня доступа к классу MessageBox), я, вероятно, хочу сделать то, что делает этот компилятор для выбора сборки. Если, как вы говорите, он просто выбирает из ссылок на сборку проектов, тогда я хочу, чтобы выбор был сделан для создания ссылок на сборку. Cheers, –

+0

Компилятор тоже этого не делает. Программист делает, когда он создает свой проект. Или, возможно, более точно, шаблон проекта. Этот материал встроен в среду IDE. Механизм выбора не существует так, как вы сейчас приближаетесь к нему. –

+0

Еще раз спасибо. Но на самом деле, мы, старые динозавры, не пользуемся ими воображаемыми IDE. :-) Клавиатура! Или, для реальных верхних динозавров, изменяя бит в ОЗУ, направляя космические лучи через магические заклинания! Cheers, –

3

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

Я поставляем полное имя, построенное с версией, установленной как минимальная версия .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# - это можно сделать?

Cheers,

+1

@downvoter: просьба указать причину downvote, чтобы другие могли понять, почему это глупо. –

Смежные вопросы