服务器托管:C++怎样有效利用非托管并列缓存



  VisualStudio安装会把VisualStudio共享库放在个称为\"并列缓存Cache(side-by-sidecache)\"地方那怎样才能有效地利用它呢?

  在文章开头先看个举例在命令行中创建个源文件输入例1中代码(虽然此处使用是C/CLI语法但不管你是用C/CLI、托管C、或本地C都不影响要讲解主题)

  例1:lib.cpp

using;
publicrefTest
{
 public:
  voidCallMe
  { 
   Console::WriteLine(\"calledme\");
  }
};

  将其编译为个托管库集:

cl/clr/LDlib.cpp

  在此要多留意我们是使用了混合模式(/clr)来编译此代码当然了如果适当修改也能以旧式托管C语法(/clr:oldsyntax)来编译

  下创建此库C#(例2)当然也可以使用VisualBasic.NET不过C#更好再和库起编译:

  例2:

using;

App
{
 voidMain
 {
  Testtest=Test;
  test.CallMe;
 }
}

cscapp.cs/r:lib.dll

  运行此会抛出个异常:

UnhandledException:
.IO.FileNotFoundException:
Thespeciedmodulecouldnotbefound.
(ExceptionfromHRESULT:0x8007007E)
atApp.Main

  如何会这样呢?打开所在目录库也在那啊HRESULT高位字为0x8007其代表FACILITY_WIN32也就是说这是个Win32;低位字以十进制表示为126在winerror.h中列明其代表ERROR_MOD_NOT_FOUND如果LoadLibrary不能查找到某个模块才会返回这个结果因此现在非常清楚了这个表示不能查找到个非托管DLL

  为找出库所使用模块列表可在ILDASM中加载它并查看MANIFEST如果库是通过平台加载DLL那这些DLL会作为.module条目列出然而对这个库来说你将会发现它只用到了托管集mscorlib和Microsoft.VisualC两者都在.NET全局集缓存Cache(GAC)中另有种可能性集中还存在着非托管代码由它了非托管库(例如那些使用托管CItJustWorks代码)

  为调查清楚从ILDASMView菜单中选项Headers这将会列出库中PE文件头向下滚动直至找到导入表(IAT)会得到份所有从非托管库引入思路方法列表库是以混合模式编译因此库用到了C运行时库(CRT)从导入表中也确认了这点--它列出了msvcr80.dll及msvcm80.dll前者是CRTDLL多线程版本后者是个包含了些CRT托管版本混合模式库这下非常清楚了产生原因是找不到这两个库、或其

  最后查看%systemroot%\\system32目录下是否有这些库--但它们不会在那此时你可能会指责VisualStudio安装没有把最新版本CRT安装在自己电脑上但实际上安装已经安装了这些CRT库--只是不在你原先期待地方

  并列缓存Cache

  VisualStudio安装会把VisualStudio共享库放在个称为\"并列缓存Cache(side-by-sidecache)\"地方目录位于%systemroot%\\WinSxS且只有SYSTEM及Administrators组成员有写访问权限其他用户只有读取和运行权限并列缓存Cache中包含了\"集\"--不是托管而是非托管等价物 [Page]

  在WinSxS目录下每个集都会有个目录另外还有两个目录分别是Manests和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

  显而易见个是发布版(ReleaseBuild)而另个是调试版(DebugBuild)但重点是版本号和个公有密钥权标也是目录名部分如果你查看前个目录内容可看到有msvcm80.dll、msvcp80.dll、及msvcr80.dll它们是被称为\"Microsoft.VC80.CRT\"非托管集8.0.50727.42版本内容个非托管集可包含个或多个文件而这些文件也可为包含本地代码或COM对象DLL个非托管集通常被作为个单独单元部署且其中所有文件由个被称为\"清单(manest)\"文件来描述

  清单文件在Manest目录中且和集同名但是后缀名为.manest这个文件列出了集中所有文件;此外还有个文件文件名也和集同名但是后缀名为.cat这是个已签名安全编目文件其包含了集中文件hash值正是它已签名所以可以防止被篡改且Windows也能利用这些hash值来检查部分是否在部署后已被篡改
客户清单文件

  个客户文件(或库)能依赖于集中某个文件来构建但客户文件只会依赖于某个特定版本来构建也只会加载所需特定版本为标出所需共享版本个可执行文件(或库)也必须有个清单文件(manest)链接器在可执行文件生成时会为其创建个包含清单信息文件因此如果回过头来看下前面生成目录会找到个名为\"lib.dll.manest\"文件例3是其内容

  例3:

<?xmlversion=’1.0’encoding=’UTF-8’standalone=’yes’?>
<assemblyxmlns=’urn:schemas-microsoft-com:asm.v1’manestVersion=’1.0’>
<dependency>
<dependentAssembly>
<assemblyIdentitytype=’win32’name=’Microsoft.VC80.CRT’
version=’8.0.50608.0’processorArchitecture=’x86’
publicKeyToken=’1fc8b3b9a1e18e3b’/>
</dependentAssembly>


</dependency>
</assembly>

  正如大家所看见它介绍说明托管集lib.dll依赖于Microsoft.VC80.CRT共享并列集中某些文件尽管这个文件位于库lib.dll目录中但Windows明显不会用到它而这和MSDN中写明有点背道而驰:

  (MSDN):你可在应用 2进制可执行头文件中包含应用清单文件……作为备选方案也可把个单独清单文件放在应用可执行文件目录中会首先从文件系统中加载此清单文件并检查可执行文件资源节文件系统版本具有优先权

  然而完全不是这么回事对库而言Windows会忽略清单文件尽管如此文档还是给出了怎样解决这个问题个线索清单文件定要以资源ID为2非托管资源RT_MANIFEST形式绑定到可执行文件 [Page]

  在此有两种思路方法:第种思路方法是创建个包含对清单文件引用资源脚本文件:

#<winuser.h>
2RT_MANIFESTlib.dll.manest

  它会在以后通过Windows资源编译器rc.exe编译为个资源并通过链接器限定为个非托管资源这种思路方法问题的处在于是链接器创建了这个清单文件因此必须运行两次链接器:次是为生成清单文件次是把资源链接到最终生成文件例4是个范例makefile演示了如何进行操作:

  例4:

#Thetarget
all:app.exe

#AC#processthatdependsuponaManagedlibrary
app.exe:app.cslib.dll
cscapp.cs/r:lib.dll
#Thisisthesecondinvocationofthelinker,sotheobjectfileand
#manestwillalreadyexist,sotheydonotneedtoberebuilt.
lib.dll:lib.cpplib.reslib.obj
link/DLL/manest:no/machine:x86lib.reslib.obj
lib.res:lib.rc
rclib.rc
#CreateatemporaryresourcescriptthatbindsthemanestfiletotheDLL
lib.rc:lib.dll.manest
type<<$@
#<winuser.h>
2RT_MANIFESTlib.dll.manest
<<KEEP
#Createtheobjectfile,andinvokethelinkertocreatethemanestfile
lib.dll.manestlib.obj:lib.cpp
cl/LD/clrlib.cpp

  另个思路方法是使用mt.exe未公开隐藏选项把资源绑定到最终生成文件上这也是VisualStudio2005创建加载C库(托管混合模式或非托管模式)时所使用思路方法两个隐藏选项分别为/manest和/outputresource:前者用于指定清单文件名而后者用于指出将要修改PE文件及清单资源资源ID般而言对库来说资源ID应为2;对来说应为1请看以下举例:

mt/manestlib.dll.manest
/outputresource:lib.dll;#2

  注意此处区别:/manest选项后跟参数是用空格分隔;而/outputresource选项后跟参数是用冒号分隔明显看得出这两个选项是由区别员开发

  旦你把清单文章绑定到库Windows就可以判断需加载正确版本如果在作出这些修改的后运行前面C#也会发现运行正常

  如果你生成了个混合模式(/clr)或纯媒介语言模式(/clr:pure)托管C来使用这个混合模式链接器也创建了个相应清单文件当此运行时Windows会查找资源ID为1清单文件或查找名为manest相应文件混合模式或纯媒介语言模式都用到了CRT意味着将会在清单文件中提及CRT所以在这个特例中库不需要清单文件然而你不应该依赖这个机制在本例中使用同个非托管集作为库是个偶然情况 [Page]
版本重定向

  回过头来再看下为库创建清单文件注意集所需版本号给定为8.0.50608.0再次提醒VisualStudio2005安装集是8.0.50727.42这个叫策略版本重定向在并列缓存Cache同级Policies目录中可找到下面这个文件夹:

x86_policy.8.0.Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_x-ww_77c24773

  注意除了版本部分它有着全名此文件夹中分别包含了个策略及安全编目文件文件名基于将要重定向至版本号:

  8.0.50727.42.policy

  这是个文件(见例5)这个策略文件是针对版本8.0.50727.42其也是VisualStudio安装所安装版本它在<bindingRedirect>中指明了所有将要被重定向至本版本版本号例5中表明对版本号8.0.41204.256至8.0.50608.0所有请求都会被重定向至8.0.50727.42这个版本和Fusion(混淆:.NET中集加载技术)区别对并列共享版本重定向只能是那些生成或修订版本值的间变化不能用于主、副版本值变化

  例5:

<?xmlversion=\"1.0\"encoding=\"UTF-8\"standalone=\"yes\"?>
<!--Copyright(r)1981-2001MicrosoftCorporation-->
<assemblyxmlns=\"urn:schemas-microsoft-com:asm.v1\"manestVersion=\"1.0\">

<assemblyIdentitytype=\"win32-policy\"name=\"policy.8.0.Microsoft.VC80.CRT\"
version=\"8.0.50727.42\"processorArchitecture=\"x86\"
publicKeyToken=\"1fc8b3b9a1e18e3b\"/>
<dependency>
<dependentAssembly>
<assemblyIdentitytype=\"win32\"name=\"Microsoft.VC80.CRT\"


processorArchitecture=\"x86\"publicKeyToken=\"1fc8b3b9a1e18e3b\"/>
<bindingRedirectoldVersion=\"8.0.41204.256-8.0.50608.0\"
Version=\"8.0.50727.42\"/>
</dependentAssembly>
</dependency>
</assembly>

  那就又带出了个问题:那为什么需要重定向呢?为什么链接器不在清单文件中直接指定由安装安装集版本呢?原因在于链接器是从导入静态库中获得所需集版本这又引出了另外个问题:为什么链接器要为DLL区别版本使用导入库而不是安装那个?原因是这些安装都是重要

  目前为止讨论都是针对托管编译器(C/CLI及旧式语法)然而即便本地C开发再高也有可能被这些新\"特性\"所影响如果你代码使用了某个共享VisualStudio本地库(、ATL或CRT)那么必须有个单独.manest清单文件要么绑定至可执行文件要么只绑定至个.exe文件

  结论

  以前MicrosoftC编译器及链接器各个版本所生成都能被加载并运行但VisualStudio2005中版本14生成库却无法运行

  此处有两个解决思路方法:第种思路方法是运行链接器两次次是生成清单文件其能编译进非托管资源接着次是把这个清单绑定至PE文件这也是本文所推荐思路方法如果在构造个具有\"强名称\"在第 2次就能提供密钥文件或容器名称 [Page]

  另个思路方法是使用mt.exe未公开选项来修改然而如果使用链接器来生成个\"强名称\"mt.exe动作会使强名称签名无效集也不会加载
Tags:  清除dns缓存 主机托管 二级缓存 服务器托管

延伸阅读

最新评论

发表评论