使用PInvoke从同一个托管dll中调用32位和64位dll

3
我正在使用pinvoke从.NET(C#)dll调用到本地dll。现在我想将我的.NET dll编译为“AnyCPU”,但是在“pinvoking”时,我必须知道是否要调用32位或64位的dll。我将两个版本的本地dll安装到子文件夹bin32和bin64中。现在当加载我的DLL时,我想检查我们是否处于32位或64位模式,并使用适当的路径调用SetDllDirectory。这种方法似乎没有问题,除了一个好的“入口点”来调用SetDllDirectory。在本地DLL中,有一个DllMain条目,当将DLL附加到进程时会调用它。在.NET DLL中是否有类似的入口点? 您认为动态调用SetDllDirectory是一个好主意吗?
2个回答

4

.NET确实有一个类似于DllMain()函数的等效函数,它被称为模块初始化器。但是,它无法从C#和VB.NET代码中访问,您只能在IL或C++/CLI中创建它。C++/CLI本身具有位数依赖性,因此只剩下IL。您可以在这个答案中找到一个示例代码。将其链接到您的程序集中相当麻烦,构建系统不直接支持运行程序集链接器。

下一个最好的选择是"类型初始化器",如同在同一篇文章中提到的,在C#中称为静态构造函数。您需要在代码中进行某种组织,以使它们得到回报,例如一个在您的'成千上万个方法'之前保证会被使用的类。考虑到有这么多的方法,这可能会很困难。

那只剩下一个初始化方法需要在应用程序的Main()方法中调用。当然,标准解决方案是使用两个安装程序,一个用于32位机器,另一个用于64位机器。这也确保您的应用程序最终位于“正确”的目录中,即c:\program files vs c:\program files (x86)。
更新:.NET 5现在支持C# v9中的模块初始化器,使用[ModuleInitializer]属性

我认为这个链接对你来说应该是一个不错的解决方案:http://msdn.microsoft.com/en-us/library/windows/desktop/ff951640%28v=vs.85%29.aspx,至少在Windows上是这样。我曾经在我的问题中使用了一些魔法来处理注册表,所以我采用了模块初始化器方法。如果你将托管对象的创建包装在类似工厂的类中,在那里进行初始化或尝试将其放入静态类(构造函数)中,其中保留您的PInvoke定义,也可以避免这种情况。 - user629926

1
基本上您要问的是是否可以编写在程序集首次加载时执行的代码。该问题在此处得到解答:.Net: Running code when assembly is loaded 在您的位置上,我会将责任放在库的用户身上。提供一个初始化库的函数,并要求库的用户在调用任何其他函数之前调用它。
如果您愿意,可以使用延迟初始化将该初始化嵌入到您的库中。因此,所有的方法都可以像这样:
private static void EnsureInitialized()
{
    if (!MyLibraryInitialized)
        InitializeMyLibrary();
}

public static void DoSomething()
{
    EnsureInitialized();
    .... // implementation of DoSomething
}

我建议不要使用SetDllDirectory。有一种更简单的方法可以实现。由于可以获得需要加载的DLL的完整路径,所以只需在InitializeMyLibrary()中调用LoadLibrary()即可加载它。一旦DLL被加载,您的p/invokes将自动绑定到已加载的DLL。

谢谢,LoadLibrary在许多情况下确实比SetDllDirectory更好,但不幸的是,在我的情况下并非如此。这里有数十个DLL可能会被加载,具体取决于用户调用的方法。如果不需要,我不想占用这些内存空间。 - Gerhard
1
@Gerhard 我并不是建议在启动时全部加载它们。只需加载所需的内容即可。但是,如果这些 DLL 之间存在依赖关系,则需要修改 DLL 搜索路径,以便适当地解决依赖关系。 - David Heffernan
你的回答并没有明确说明托管 DLL 中没有 "DllMain",但你提供了一个很好的解决方法。不幸的是,我的 DLL 中有成千上万个方法,所以我不能使用 "Initialized" 预检查。如果没有人提出托管代码中的 DllMain,我将把你的答案标记为被接受的答案。再次感谢。 - Gerhard
如果您无法使用延迟初始化,则可以要求用户调用您的初始化函数。但是,我不相信您的库中真的有数千个pinvoke调用。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接