2010-10-23 50 views
3

我的目標是從純C++本機Windows API級程序(不是託管的C++或C++/CLI)顯示.NET Windows窗體消息框。以編程方式查找完全限定的.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> _ 

然而,如前所述,我不想硬編碼任何東西:在C++代碼硬編碼了.NET的信息不應該超過在頂部註釋中顯示的C#源代碼中,加上支持的最低.NET版本。

TIA,

+0

這是一個非常有趣的運算符>>用法。 – 2010-10-23 04:52:51

+0

絕對看看這[提案](http://area51.stackexchange.com/proposals/11464/code-review?referrer = aWNm_PdciyFqjFW8CUacGw2「代碼評論」)。它幾乎在那裏,只需要一點點支持。 – greatwolf 2011-01-17 22:53:09

回答

1

Hmya,這是不是它通常所做的那樣。在初始化CLR之後,您接下來會在GAC中加載一個組件,其編號爲而不是,由託管編譯器生成。通常使用Main()方法的EXE,當然你在這裏有其他的選擇。

如果您想追求這一點,那麼您確實必須生成一個完全限定的名稱,Fusion要求它知道從GAC中選擇哪個程序集。相當於gacutil/l是Fusion API,您可以使用CreateAssemblyEnum()來迭代它。獲取IAssemblyEnum,其GetNextAssembly()方法爲您提供IAssemblyName。必須有一些硬編碼的選擇算法來確定你喜歡哪個版本。

當您使用託管編譯器創建的程序集時,這不是問題。項目中的程序集引用選擇所需的版本。我不得不推薦你這樣。

+0

謝謝,迄今。本質上(除非找到其他方法來達到更高層次的目標),我可能想要做那些編譯器爲程序集選擇做的事情。如果你說它只是從項目組件引用中選擇,那麼我想要做的選擇來生成那些程序集引用。乾杯, – 2010-10-23 04:32:29

+0

編譯器也沒有這樣做。程序員在創建他的項目時會這樣做。或者更準確地說,項目模板可以。這些東西內置於IDE中。目前沒有「選擇」機制。 – 2010-10-23 05:05:11

+0

到目前爲止再次感謝。但是,真的,我們的舊恐龍不會使用它們的花式IDE。 :-)鍵盤!或者,對於真正的頂級恐龍來說,通過指揮宇宙射線通過魔術咒語來改變RAM中的位!乾杯, – 2010-10-23 05:38:27

3

嗯,OK,部分答案我自己的問題(夠前進):

我提供一個構建的全名與設置爲.NET的最低版本所支持的版本,和.NET「融合」(?但此記錄)發現一個顯然兼容組件,雖然指定組件不在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

@downvoter:請說明downvote的原因,以便其他人可以看到爲什麼它很愚蠢。 – 2010-12-15 23:17:29

相關問題