调用非托管dll文件的方法

之前在做xx工具的时候,引用到一个叫XZ.NET的库,但是这个库需要用到liblzma.dll的文件,这个由于是使用C++编写的,因此没有办法直接打包到可执行文件中,那么如果要打包成单文件需要嵌入调用liblzma.dll的时候该如何解决呢?

解决这个方法有两种: - 第一种是可以将这类dll文件释放到临时目录中再通过DllImport调用,不过这个方法不太优雅,有一些问题不好处理; - 第二种是利用costura.fody去处理这类dll文件调用的问题

如果使用第二种方法,流程如下:

  1. 需要安装costura.fody
  2. liblzma.dll放在Costura32Costura64文件夹中,如果不存在这样的文件夹需要手动创建,至于放在哪个文件夹,是根据dll库的版本以及需要编译出来的可执行文件的类型来定
  3. 编写FodyWeavers.xml,内容如下
    XML
    <?xml version="1.0" encoding="utf-8"?>
    <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
        <Costura>
            <Unmanaged32Assemblies>
                liblzma
            </Unmanaged32Assemblies>
        </Costura>
    </Weavers>
    
    如果在第2步的时候你将dll文件放在Costura64文件夹中,需要将Unmanaged32Assemblies改成Unmanaged64Assemblies 编译一下,这个dll文件就被集成进去了
备注

如果确定这个dll库被编译进去了?有两种判断方法: 1. 检查文件是否变大,不过有些情况下无法准确确定 2. 通过dnSpy等工具反编译检查 检查dll文件 可以看到,编译后的liblzma变成了costura.liblzma.dll,这样也可以确定liblzma被编译进去了

  1. 如果需要调用这个dll文件,需要通过DllImport导入,参考代码如下
    C#
    class Demo
    {
    const string DllName = "liblzma.dll";
    
    [DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]
    public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string fileName);
    
    public void Test()
    {
        IntPtr libHandle = LoadLibrary(dllPath);
        if (libHandle == IntPtr.Zero)
        {
            Console.WrileLine("无法导入liblzma.dll");
            return;
        }
        //
    }
    }
    
备注

每个dll库的调用方法可能会有所区别,具体请参考对应的项目文档和源码去编写