从 .Net 托管代码加载 32 或 64 位 DLL

发布于 2024-07-10 17:15:40 字数 363 浏览 4 评论 0原文

我有一个非托管 DLL(Scintilla 代码编辑器的 scilexer.dll,由 CodePlex 中的 Scintilla.Net 使用)它是通过 Scintilla.Net 组件从托管应用程序加载的。 Windows 管理的应用程序在 32 位和 64 位环境中运行都没有问题,但我需要创建使用 64 或 32 scilexer.dll 的不同安装。

有没有办法以 32 位和 64 位格式分发 DLL,以便 .Net 框架的 DLL 加载器根据某些 .config 选项或某些“路径名魔术”内容加载 32 或 64 位格式的非托管 DLL?

I have a unmanaged DLL (the scilexer.dll of Scintilla code editor, used by Scintilla.Net from CodePlex) that is loaded from a managed application trough the Scintilla.Net component. The windows managed application runs without problem on both 32 and 64 bit environments, but I need to create different installations that uses the 64 or the 32 scilexer.dll.

Is there a way to distribute both DLLs in 32 and 64 bit format so that the DLL loader of the .Net framework loads the unmanaged DLL in the 32 or 64 bit format depending on some .config option or some "path name magic" stuff?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

司马昭之心 2024-07-17 17:15:40

P/Invoke 使用 LoadLibrary 加载 DLL,如果已经存在使用给定名称加载的库,LoadLibrary 将返回它。 因此,如果您可以为 DLL 的两个版本指定相同的名称,但将它们放在不同的目录中,则可以在第一次从 scilexer.dll 调用函数之前执行一次类似的操作,而无需重复 extern 声明:

    string platform = IntPtr.Size == 4 ? "x86" : "x64";
    string dll = installDir + @"\lib-" + platform + @"\scilexer.dll";
    if (LoadLibrary(dll) == IntPtr.Zero)
        throw new IOException("Unable to load " + dll + ".");

P/Invoke uses LoadLibrary to load DLLs, and if there is already a library loaded with a given name, LoadLibrary will return it. So if you can give both versions of the DLL the same name, but put them in different directories, you can do something like this just once before your first call to a function from scilexer.dll, without needing to duplicate your extern declarations:

    string platform = IntPtr.Size == 4 ? "x86" : "x64";
    string dll = installDir + @"\lib-" + platform + @"\scilexer.dll";
    if (LoadLibrary(dll) == IntPtr.Zero)
        throw new IOException("Unable to load " + dll + ".");
月下凄凉 2024-07-17 17:15:40

不幸的是,我对这个特定的 DLL 一无所知。 然而,当您自己执行 P/Invoke 时,并且您可以处理一些重复,则可以为每个平台创建一个代理。

例如,假设您有以下接口,该接口应由 32 位或 64 位 DLL 实现:

public interface ICodec {
    int Decode(IntPtr input, IntPtr output, long inputLength);
}

您创建代理:

public class CodecX86 : ICodec {
    private const string dllFileName = @"Codec.x86.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

最后

public class CodecX64 : ICodec {
    private const string dllFileName = @"Codec.x64.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

创建一个为您选择正确代理的工厂:

public class CodecFactory {
    ICodec instance = null;

    public ICodec GetCodec() {
        if (instance == null) {
            if (IntPtr.Size == 4) {
                instance = new CodecX86();
            } else if (IntPtr.Size == 8) {
                instance = new CodecX64();
            } else {
                throw new NotSupportedException("Unknown platform");
            }
        }
        return instance;
    }
}

由于 DLL 是延迟加载的,第一次调用它们时,这实际上是有效的,尽管每个平台只能加载其本机的版本。 有关更详细的说明,请参阅本文

Unfortunately, I don't know anything about this particular DLL. However, when you do the P/Invoke yourself, and you can cope with a little duplication, it's possible to create one proxy for each platform.

For instance, suppose that you have the following interface, that should be implemented by either a 32 or 64 bit DLL:

public interface ICodec {
    int Decode(IntPtr input, IntPtr output, long inputLength);
}

You create the proxies:

public class CodecX86 : ICodec {
    private const string dllFileName = @"Codec.x86.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

and

public class CodecX64 : ICodec {
    private const string dllFileName = @"Codec.x64.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

And finally make a factory that picks the right one for you:

public class CodecFactory {
    ICodec instance = null;

    public ICodec GetCodec() {
        if (instance == null) {
            if (IntPtr.Size == 4) {
                instance = new CodecX86();
            } else if (IntPtr.Size == 8) {
                instance = new CodecX64();
            } else {
                throw new NotSupportedException("Unknown platform");
            }
        }
        return instance;
    }
}

As the DLLs are loaded lazily the first time they are being invoked, this actually works, despite each platform only being able to load the version that is native to it. See this article for a more detailed explanation.

美羊羊 2024-07-17 17:15:40

我想出的最好的方法如下:

  • 使用两个名为 64 或 32 的 DLL 分发我的应用程序
  • 在主启动代码中包括以下内容:
    
    File.Delete(Application.StartupPath + @"\scilexer.dll");
    {
      // Check for 64 bit and copy the proper scilexer dll
        if (IntPtr.Size == 4)
        {
          File.Copy(Application.StartupPath + @"\scilexer32.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
        else
        {
          File.Copy(Application.StartupPath + @"\scilexer64.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
    }

The best I've come up with is the following:

  • Distribute my application with two DLLs named 64 or 32
  • In the main startup code include the following:
    
    File.Delete(Application.StartupPath + @"\scilexer.dll");
    {
      // Check for 64 bit and copy the proper scilexer dll
        if (IntPtr.Size == 4)
        {
          File.Copy(Application.StartupPath + @"\scilexer32.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
        else
        {
          File.Copy(Application.StartupPath + @"\scilexer64.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
    }
娇纵 2024-07-17 17:15:40

你可以把dll放在system32下。 syswow64中的32位和真实system32中的64位。 对于 32 位应用程序,当访问 system32 时,它们会被重定向到 Syswow64。

您可以在注册表中创建一个条目。 软件密钥有一个名为 Wow6432Node 的子项,32 位应用程序将其视为软件密钥。

这是 powershell 安装程序会执行

You can put the dll in system32. The 32 bit in syswow64 and the 64 bit in the real system32. For 32 bit application, when thay access system32 they are redirected to Syswow64.

You can create an entry in the registry. The software key has a subkey named Wow6432Node that 32 bit application see as the software key.

Here is what powershell installer does.

琉璃繁缕 2024-07-17 17:15:40

非托管 dll 可以与其托管对应项并行安装到 GAC 中。 本文 应该解释它是如何工作的。

Unmanaged dlls can be installed into the GAC side-by-side with their managed counterparts. This article should explain how it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文