之前移植过DLNA的库,这个库是C++写的,然后我们的项目是C#的。接着很郁闷的事情发生了,主项目引用一个C#的DLL,然后这个DLL引用这个C++/CX封装的库。如果有C++的源代码的话,做项目依赖就很简单。如果是引用DLL的话,切换平台,这些DLL就要被覆盖一遍,而且经常会出现DLL加载失败的错误。
我最初的做法就是把这些DLL 都添加到主项目里面,在属性页中设置复制到输出目录。这种做法有个问题,就是这个DLL无法被添加到引用中。
后来,受到C++项目条件编译的启发,改写了一下C#的项目配置文件,在里面添加Choose节点。有个这个节点,就可以根据不同的平台,引用不同的DLL
<Choose>
<When Condition ="'$(Platform)' == 'ARM'">
<ItemGroup>
<Reference Include="PLTWinRt">
<HintPath>>..Win8ReferenceDllARMPLTWinRt.winmd</HintPath>
</Reference>
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="PLTWinRt">
<HintPath>..Win8ReferenceDllX86PLTWinRt.winmd</HintPath>
</Reference>
</ItemGroup>
</Otherwise>
</Choose>
对于主项目中添加的DLL,我采用预先生成事件命令行,把对应的DLL覆盖一下。
<PropertyGroup Condition=" '$(Platform)' == 'x86'">
<PreBuildEvent>Copy "$(ProjectDir)ReferenceDllX86*.*" "$(ProjectDir)"</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'ARM'">
<PreBuildEvent>Copy "$(ProjectDir)ReferenceDllARM*.*" "$(ProjectDir)"</PreBuildEvent>
</PropertyGroup>
这样做法的好处就是可以让所有的DLL和EXE在同一个目录下面,如果采用前面的方式,那这些DLL会在子文件夹中。就可能出现加载不正常的问题。
如何诊断DLL加载是否正常,在Win8这里没有太好的工具去看,depends.exe看不了C#项目的引用,lldasm.exe会看到引用,但是不能确定是否正确加载,Fuslogvw.exe似乎看不到win8项目的DLL引用过程。
一个C++/CX的WinRt组件会有两个文件,一个PpboxRT.winmd和PpboxRT.dll。引用的时候,VS会输出加载winmd文件,如果对应.dll文件引用其他dll文件,其他的dll文件找不到了就会抛出FileNotFoundException 错误。
这个时候,我们要去项目的目录中去找对应的DLL有没有复制到指定目录里 Win8inx86DebugAppX
另外,在这个目录里面还有AppxManifest.xml 文件,这个文件中记录着winmd文件和dll文件中的类的关联信息
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>PLTWinRt.dll</Path>
<ActivatableClass ActivatableClassId="PLTWinRt.DMR_PositionInfo" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="PLTWinRt.DMR_ConnectionInfo" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="PLTWinRt.DMR_MediaInfo" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="PLTWinRt.MediaController" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="PLTWinRt.DMR_TransportSettings" ThreadingModel="both" />
<ActivatableClass ActivatableClassId="PLTWinRt.DMR_TransportInfo" ThreadingModel="both" />
</InProcessServer>
</Extension>
当然这个节点也可以强行添加到Package.appxmaniest文件中。