亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++/CLI中有效使用非托管并列緩存

2019-11-17 05:49:11
字體:
來源:轉載
供稿:網友

  Visual Studio安裝程序會把Visual Studio的共享庫放在一個稱為"并列緩存(side-by-side cache)"的地方,那怎樣才能有效地利用它呢?

  在文章開頭,先看一個示例。在命令行中,創建一個C++源文件,輸入例1中的代碼。(雖然此處使用的是C++/CLI語法,但不管你是用C++/CLI、托管C++、或本地C++,都不影響要講解的主題。)

  例1:lib.cpp

using namespace System;
public ref class Test
{
 public:
  void CallMe()
  { 
   Console::WriteLine("called me");
  }
};
  將其編譯為一個托管庫程序集:

cl /clr /LD lib.cpp
  在此要多留意,我們是使用了混合模式(/clr)來編譯此代碼,當然了,假如適當修改,也能以舊式托管C++語法(/clr:oldsyntax)來編譯。

  下一步,創建一個調用此庫的C#程序(例2),當然也可以使用Visual Basic.NET,不過C#更好一點。再與庫一起編譯:

  例2:

using System;

class App
{
 static void Main()
 {
  Test test = new Test();
  test.CallMe();
 }
}

csc app.cs /r:lib.dll
  運行此程序,會拋出一個異常:

Unhandled Exception:
System.IO.FileNotFoundException:
The specified module could not be found.
(Exception from HRESULT: 0x8007007E)
at App.Main()
  怎么會這樣呢?打開程序所在的目錄,庫也在那啊。HRESULT的高位字為0x8007,其代表FACILITY_WIN32,也就是說,這是一個Win32錯誤;低位字以十進制表示為126,在winerror.h中列明其代表ERROR_MOD_NOT_FOUND。假如LoadLibrary不能查找到某個模塊,才會返回這個錯誤結果,因此,現在非常清楚了,這個錯誤表示不能查找到一個非托管的DLL。

  為找出庫所使用的模塊列表,可在ILDASM中加載它,并查看MANIFEST。假如庫是通過平臺調用加載DLL的,那這些DLL會作為.module條目列出,然而,對這個庫來說,你將會發現,它只用到了托管程序集mscorlib與Microsoft.VisualC,兩者都在.NET全局程序集緩存(GAC)中。另有一種可能性,在程序集中,還存在著非托管代碼,由它調用了非托管庫(例如,那些使用托管C++ It Just Works的代碼)。

  為調查清楚,從ILDASM的View菜單中選項Headers,這將會列出庫中的PE文件頭。向下滾動直至找到導入表(IAT),會得到一份所有從非托管庫引入的方法列表。因為庫是以混合模式編譯的,因此庫用到了C運行時庫(CRT),從導入表中也確認了這點--它列出了msvcr80.dll及msvcm80.dll,前者是CRT的DLL多線程版本,后者是一個包含了一些CRT托管版本的混合模式庫。這下非常清楚了,錯誤產生的原因是Windows找不到這兩個庫、或其一。

  最后,查看%systemroot%/system32目錄下是否有這些庫--但它們不會在那的,此時,你可能會指責Visual Studio安裝程序沒有把最新版本的CRT安裝在自己的電腦上,但實際上,安裝程序已經安裝了這些CRT庫--只是不在你原先期待的地方。

  并列緩存

  Visual Studio安裝程序會把Visual Studio的共享庫放在一個稱為"并列緩存(side-by-side cache)"的地方,目錄位于%systemroot%/WinSxS,且只有SYSTEM及Administrators組成員有寫訪問權限,其他用戶只有讀取和運行權限。并列緩存中包含了"程序集"--不是托管程序集,而是非托管的等價物。

  在WinSxS目錄下,每個程序集都會有一個目錄,另外,還有兩個目錄分別是Manifests和Policies,其中包含了版本的相關信息。以下兩個目錄與CRT有關:

x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd
x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c
  顯而易見,一個是發布版(Release Build),而另一個是調試版(Debug Build),但重點是,版本號與一個公有密鑰權標也是目錄名的一部分。假如你查看前一個目錄的內容,可看到有msvcm80.dll、 msvcp80.dll、及msvcr80.dll,它們是被稱為"Microsoft.VC80.CRT"非托管程序集8.0.50727.42版本的內容。一個非托管程序集可包含一個或多個文件,而這些文件也可為包含本地代碼或COM對象的DLL。一個非托管程序集通常被作為一個單獨的單元部署,且其中的所有文件由一個被稱為"清單(manifest)"的xml文件來描述。

  清單文件存儲在Manifest目錄中,且與程序集同名,但是后綴名為 .manifest。這個文件列出了程序集中的所有文件;此外,還有一個文件的文件名也與程序集同名,但是后綴名為 .cat,這是一個已簽名的安全編目文件,其包含了程序集中文件的hash值,正是因為它已簽名,所以可以防止被篡改,且Windows也能利用這些hash值來檢查程序集的任一部分是否在部署后已被篡改。進入討論組討論。
客戶清單文件

  一個客戶文件(程序或庫)能依靠于程序集中的某個文件來構建,但客戶文件只會依靠于程序集的某個特定版本來構建,Windows也只會加載所需的特定版本。為標出所需共享程序集的版本,一個可執行文件(程序或庫)也必須有一個清單文件(manifest)。鏈接器在可執行文件生成時,會為其創建一個包含清單信息的文件,因此,假如回過頭來看一下前面生成的庫的目錄,會找到一個名為"lib.dll.manifest"的文件,例3是其的內容。

  例3:

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<dependency>
<dependentAssembly>
<assemblyIdentity type='win32' name='Microsoft.VC80.CRT'
version='8.0.50608.0' publicKeyToken='1fc8b3b9a1e18e3b' />
</dependentAssembly>
</dependency>
</assembly>
  正如大家所看見的,它說明托管程序集lib.dll依靠于Microsoft.VC80.CRT共享并列程序集中的某些文件。盡管這個文件位于庫lib.dll的同一目錄中,但Windows明顯不會用到它,而這與MSDN中寫明的有點背道而馳:

 ?。∕SDN):你可在應用程序二進制可執行頭文件中包含應用程序清單文件……,作為備選方案,也可把一個單獨的清單文件放在應用程序可執行文件的同一目錄中,
操作系統會首先從文件系統中加載此清單文件,并檢查可執行文件的資源節。文件系統的版本具有優先權。

  然而,完全不是這么回事。對庫而言,Windows會忽略清單文件,盡管如此,文檔還是給出了怎樣解決這個問題的一個線索,清單文件一定要以資源ID為2的非托管資源RT_MANIFEST形式綁定到可執行文件。

  在此有兩種方法:第一種方法是創建一個包含對清單文件引用的資源腳本文件:

#include <winuser.h>
2 RT_MANIFEST lib.dll.manifest
  它會在以后通過Windows資源編譯器rc.exe編譯為一個資源,并通過鏈接器限定為一個非托管資源。這種方法的問題之處在于,是鏈接器創建了這個清單文件,因此必須運行兩次鏈接器:一次是為生成清單文件,一次是把資源鏈接到最終生成文件。例4是一個范例makefile,演示了如何進行操作:

  例4:

# The main target
all: app.exe

# A C# process that depends upon a Managed C++ library
app.exe : app.cs lib.dll
csc app.cs /r:lib.dll
# This is the second invocation of the linker, so the object file and
# manifest will already exist, so they do not need to be rebuilt.
lib.dll : lib.cpp lib.res lib.obj
link /DLL /manifest:no /machine:x86 lib.res lib.obj
lib.res : lib.rc
rc lib.rc
# Create a temporary resource script that binds the manifest file to the DLL
lib.rc : lib.dll.manifest
type <<$@
#include <winuser.h>
2 RT_MANIFEST lib.dll.manifest
<<KEEP
# Create the object file, and invoke the linker to create the manifest file
lib.dll.manifest lib.obj : lib.cpp
cl /LD /clr lib.cpp
  另一個方法是使用mt.exe未公開的隱藏選項把資源綁定到最終生成文件上,這也是Visual Studio 2005創建加載的C++庫(托管混合模式或非托管模式)時所使用的方法。兩個隱藏選項分別為/manifest和/outputresource:前者用于指定清單文件名,而后者用于指出將要修改的PE文件及清單資源的資源ID。一般而言,對庫來說,資源ID應為2;對程序來說,應為1。請看以下示例:

mt /manifest lib.dll.manifest
/outputresource:lib.dll;#2

  注重此處的區別:/manifest選項后跟的參數是用空格分隔的;而/outputresource選項后跟的參數是用冒號分隔的。明顯看得出,這兩個選項是由不同的程序員開發的。

  一旦你把清單文章綁定到庫,Windows就可以判定需加載程序集的正確版本。假如在作出這些修改之后,運行前面的C#程序,也會發現程序運行正常。

  假如你生成了一個混合模式(/clr)或純媒介語言模式(/clr:pure)的托管C++程序,來使用這個混合模式的庫,鏈接器也創建了一個相應的程序清單文件,當此程序運行時,Windows會查找資源ID為1的清單文件,或查找名為manifest的相應文件。因為混合模式或純媒介語言模式程序都用到了CRT,意味著將會在清單文件中提及CRT程序集,所以,在這個特例中,庫不需要清單文件。然而,你不應該依靠這個機制,因為在本例中,程序使用同一個非托管程序集作為庫是一個偶然情況。進入討論組討論。
版本重定向

  回過頭來再看一下為庫創建的清單文件,注重程序集所需的版本號給定為8.0.50608.0,再次提醒,Visual Studio 2005安裝的程序集是8.0.50727.42,這個叫策略版本重定向。在并列緩存的同級Policies目錄中,可找到下面這個文件夾:

x86_policy.8.0.Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_x-ww_77c24773
  注重,除了版本部分,它有著程序集的全名。此文件夾中分別包含了一個策略及安全編目文件,文件名基于將要重定向至的版本號:

  8.0.50727.42.policy

  這是一個XML文件(見例5)。這個策略文件是針對版本8.0.50727.42的,其也是Visual Studio安裝程序所安裝的版本。它在<bindingRedirect>中指明了所有將要被重定向至本版本的版本號,例5中表明,對版本號8.0.41204.256至8.0.50608.0程序集的所有請求,都會被重定向至8.0.50727.42這個版本。與Fusion(混淆: .NET中的程序集加載技術)不同的是,對并列共享程序集的版本重定向只能是那些生成或修訂的版本值之間的變化,不能用于主、副版本值的變化。

  例5:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (r) 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity type="win32-policy" name="policy.8.0.Microsoft.VC80.CRT"
version="8.0.50727.42" processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT"
processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0"
newVersion="8.0.50727.42"/>
</dependentAssembly>
</dependency>
</assembly>
  那就又帶出了一個問題:那為什么需要重定向呢?為什么鏈接器不在清單文件中直接指定由安裝程序安裝的程序集版本呢?原因在于,鏈接器是從導入靜態庫中獲得所需的程序集版本。這又引出了另外一個問題:為什么鏈接器要為DLL的不同版本使用導入庫,而不是安裝的那個?原因是,這些安裝的都是重要的庫。

  目前為止的討論都是針對托管C++編譯器(C++/CLI及舊式語法),然而,即便本地C++開發技巧再高,也有可能被這些新"特性"所影響。假如你的代碼使用了某個共享的Visual Studio本地庫(MFC、ATL或CRT),那么,必須有一個單獨的.manifest清單文件,要么綁定至可執行文件,要么只綁定至一個 .exe文件。

  結論

  以前Microsoft C++編譯器及鏈接器的各個版本所生成的庫,都能被Windows加載并運行,但Visual Studio 2005中的版本14,生成的庫卻無法運行。

  此處有兩個解決方法:第一種方法是運行鏈接器兩次,一次是生成清單文件,其能編譯進非托管資源,接著一次是把這個清單綁定至PE文件。這也是本文所推薦的方法,因為假如在構造一個具有"強名稱"的程序集,在第二次調用時,就能提供密鑰文件或容器的名稱。

  另一個方法是,使用mt.exe未公開的選項來修改程序集,然而,假如使用鏈接器來生成一個"強名稱"的程序集,mt.exe的動作會使強名稱簽名無效,且程序集也不會加載。進入討論組討論。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产福利视频| 亚洲va久久久噜噜噜| 国内外成人免费激情在线视频网站| 中文字幕亚洲无线码a| 国产欧美一区二区三区四区| 欧洲日韩成人av| 夜夜嗨av一区二区三区四区| 欧美性xxxxx极品娇小| 亚洲第一天堂av| 亚洲韩国青草视频| 91免费视频网站| 久久亚洲精品一区二区| 日韩av在线电影网| 亚洲一区二区久久久| 在线亚洲午夜片av大片| 国产在线视频一区| 国产精品美女久久久久av超清| 久久精品亚洲国产| 欧美日韩国产在线| 国产美女久久精品| 91久久久久久国产精品| 中文字幕日韩在线观看| 中文字幕日韩欧美精品在线观看| 久久亚洲精品一区| 欧美亚洲国产成人精品| 亚洲男人的天堂在线播放| 成人动漫网站在线观看| 国产精品国模在线| 午夜精品久久久久久久久久久久久| 久久久久久久久久久亚洲| 成年人精品视频| 中文字幕久热精品在线视频| 久久久电影免费观看完整版| 亚洲性av网站| 欧美国产日韩一区二区在线观看| 亚洲影院色无极综合| 日韩大片免费观看视频播放| 91久久精品国产91久久| 亚洲成人免费网站| 九九热在线精品视频| 亚洲欧美日韩爽爽影院| 日韩国产精品一区| 欧美激情亚洲自拍| 亚洲欧美激情精品一区二区| 久久国内精品一国内精品| 久久人体大胆视频| 国产精品私拍pans大尺度在线| 日韩欧美亚洲国产一区| 国产视频精品xxxx| 日韩美女福利视频| 亚洲999一在线观看www| 欧美日韩国产一区二区三区| 亚洲第一福利在线观看| 蜜臀久久99精品久久久无需会员| 亚洲精品网站在线播放gif| 日韩精品小视频| 亚洲欧美精品中文字幕在线| 欧美影院成年免费版| 欧美一级黑人aaaaaaa做受| 日韩美女免费视频| 亚洲欧美激情精品一区二区| 欧美韩日一区二区| 美女黄色丝袜一区| 国产精品扒开腿做爽爽爽男男| 欧美有码在线视频| 亚洲欧美三级在线| 国产亚洲精品久久久久久777| 中文字幕日韩高清| 久久精品国产久精国产思思| 日韩有码在线播放| 久久国产视频网站| 按摩亚洲人久久| 国产精品久久久久福利| 欧美乱人伦中文字幕在线| 91久久国产综合久久91精品网站| 久久av在线看| 欧美xxxx综合视频| 亚洲欧美日本另类| 亚洲福利小视频| 国产日韩在线看| 亚洲精品在线视频| 一夜七次郎国产精品亚洲| 黄色成人av在线| 最新91在线视频| 日韩高清av一区二区三区| 亚洲国产成人在线视频| 国产精品视频最多的网站| 成人黄色大片在线免费观看| 国产精彩精品视频| 日本午夜精品理论片a级appf发布| 欧美日韩国产麻豆| 日韩网站免费观看高清| 国产国语videosex另类| 亚洲人精选亚洲人成在线| 久久亚洲国产精品成人av秋霞| 日韩成人高清在线| 亚洲国产精彩中文乱码av| 精品香蕉一区二区三区| 亚洲情综合五月天| 亚洲成在人线av| 精品久久久久久久久久| 日韩欧美精品网站| 亚洲天堂男人天堂女人天堂| 久久久精品国产一区二区| 中文字幕亚洲无线码在线一区| 色综合伊人色综合网站| 国产欧美精品一区二区三区-老狼| 国产一区二区三区在线观看视频| 日本久久亚洲电影| 亚洲欧美激情一区| 久久综合伊人77777| 欧美视频13p| 伊人久久大香线蕉av一区二区| 日韩免费在线观看视频| 日韩av电影免费观看高清| 亚洲最大成人在线| 欧美丰满老妇厨房牲生活| 日韩专区中文字幕| 4p变态网欧美系列| 欧美最猛性xxxxx亚洲精品| 久久综合久久88| 国产亚洲视频在线| 国产一区二区欧美日韩| 一区二区成人精品| 日韩视频在线一区| 高清在线视频日韩欧美| 亚洲精品小视频在线观看| www.欧美视频| 国产日韩欧美综合| 久久久久久com| 成人欧美一区二区三区黑人| 亚洲人精品午夜在线观看| 92裸体在线视频网站| 大胆欧美人体视频| 国产精品视频白浆免费视频| 热久久免费视频精品| 色av中文字幕一区| 久久久久久网站| 色七七影院综合| 日韩欧美有码在线| 91精品国产综合久久香蕉922| 国产午夜精品一区二区三区| 欧美理论电影在线观看| 欧美另类老肥妇| 久久久www成人免费精品张筱雨| 91精品国产色综合久久不卡98| 亚洲精品欧美日韩| 久久精品国产69国产精品亚洲| 国产精品小说在线| 最近2019年手机中文字幕| 久久久久久久久久久免费| 欧美日本在线视频中文字字幕| 91av中文字幕| 亚洲新中文字幕| 欧美大尺度在线观看| 欧美激情一区二区久久久| 亚洲欧美日韩第一区| 久久精品视频中文字幕| 91精品国产高清久久久久久| 中文字幕亚洲欧美一区二区三区| 精品日本美女福利在线观看| 国产视频综合在线| 国产精品爱啪在线线免费观看| 久久亚洲国产精品|