我目前没有代码,但我可以向您描述我为解决此问题所做的工作。问题是,虽然 p4api.net 库在设置为针对 Any CPU 时编译得很好,但底层的本机 C++ 库 (p4bridge.dll) 是针对 x86 或 x64 的,并且无法同时为这两种架构编译它动态链接库。因此,我必须变得聪明!
为了完成这项工作,我将两个版本的 p4bridge.dll 添加到 p4api.net 项目中,将它们重命名为 p4bridge86.dll 和 p4bridge64.dll,并将它们标记为包含为程序集资源。接下来,我在 p4api.net 库中编写了一个静态函数,该函数找出机器正在运行的架构,获取正确的 p4bridge.dll 资源,将其保存到当前正在执行的 p4api.net.dll 旁边的磁盘上,最后P/在提取的 p4bridge.dll 上调用 Windows LoadLibrary 函数。
难题的最后一部分是确保您编写的此函数在 p4api.net 中的任何类型被实例化之前运行,因为此时加载器将看到引用 p4bridge.dll 的类型并尝试从磁盘加载它,如果您'从来没有运行过提取函数,它不会在那里,你会抛出一个异常。为了解决这个问题,我不得不对 .NET 进行一些修改:我下载了 Einar Egilsson 的出色小工具InjectModuleInitializer并在 p4api.net 项目上设置了一个后期构建步骤,该项目运行该工具并让它插入指令以调用静态提取器/我在执行模块中的任何其他代码之前编写的加载器函数。
通过这个设置,我有一个为任何 CPU 编译的 p4api.net 程序集,但可以自动处理所需的本机 p4bridge.dll 必须单独存在于 x86 和 x64 架构的事实。
当我稍后回到家时,我将看到添加源代码以准确显示我是如何编写提取和加载函数的,以及可能需要更清楚的任何其他内容。抱歉,这个答案是在您最初询问一年多之后出现的,但我也需要在几天前找到解决这个问题的方法,因为我设法做到了,我认为值得分享给任何可能遇到这个问题的人未来非常复杂的问题!
编辑:这是提取和加载正确 p4bridge.dll 的类的实现。它仅在未提取 DLL 或找到的 DLL 加载失败时才提取 DLL(因为出于某种原因,它可能是错误的体系结构)。此外,p4bridge.dll 的大小为几兆字节,每次加载 p4api.net 时执行不必要的 IO 并没有多大意义!
internal static class P4BridgeLoader
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
private static void ExtractResource(string resourceName, string outPath)
{
using (System.IO.Stream dllStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
try
{
// Copy the assembly to the temporary file
using (System.IO.Stream outFile = System.IO.File.Create(outPath))
{
dllStream.CopyTo(outFile);
}
}
catch
{
}
}
}
/// <summary>
/// Loads the correct version of p4bridge.dll, based on the bit with of the current architecture.
/// Note that this is called by the module initializer, which gets called just after this module
/// is loaded but before any other code inside it is executed.
/// </summary>
internal static void LoadP4BridgeDLL()
{
// Figure out where we are going to put the p4bridge.dll once we've extracted it
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string assemblyPath = Uri.UnescapeDataString(uri.Path);
string assemblyDir = Path.GetDirectoryName(assemblyPath);
string dllPath = Path.Combine(assemblyDir, "p4bridge.dll");
// Extract the correct architecture version of p4bridge.dll from our assembly's resources
string resourceName = Environment.Is64BitProcess ? "Perforce.P4.p4bridge64.dll" : "Perforce.P4.p4bridge86.dll";
// If the dll already exists, then we shouldn't have to try extracting it again unless it fails to load
if (System.IO.File.Exists(dllPath))
{
// Attempt to load the DLL
if (LoadLibrary(dllPath) != IntPtr.Zero)
return;
}
// DLL either wasn't already extracted, or failed to load. Try again!
ExtractResource(resourceName, dllPath);
// Attempt to load the DLL
IntPtr h = LoadLibrary(dllPath);
System.Diagnostics.Debug.Assert(h != IntPtr.Zero, "Unable to load library " + dllPath);
}
}
这是应该用来连接到 .net 模块初始化程序的命令行。请注意,该/k:MyKey.snk
参数允许在修改程序集后对其进行强签名。
InjectModuleInitializer.exe /k:MyKey.snk /m:Perforce.P4.P4BridgeLoader::LoadP4BridgeDLL p4api.net.dll