【问题标题】:Unity C# how to load a managed DLL properly?Unity C#如何正确加载托管DLL?
【发布时间】:2021-01-28 12:27:27
【问题描述】:

基于这里Using OpenH264 DLL in C# Project 的另一个问题,我基本上是在尝试使用统一库https://github.com/secile/OpenH264Lib.NET 实时编码H264 视频文件,使用统一的JINT 本机库用于JavaScript 交互

我在统一引用 DLL 时遇到了相当大的困难。

我按照 GitHub 站点的说明并设法编译为 OPenH264Lib.dll(以及示例项目,并让示例项目在 windows x64 上与 Visual Studio 2019 一起工作,并且它对所有内容进行编码等) ,但是当我只是将编译后的OpenH264Lib.dll拖到Unity项目的Assets文件夹中的Plugins文件夹中时,我无法访问命名空间OpenH264Lib,它在标准C#示例中用于访问管理的DLL功能,它只是在统一控制台中说找不到命名空间。

好的,所以我决定用 Visual Studio 构建一个 C# DLL,它本身加载到 OpenH264Lib.dll 中,然后从那里调用它的函数,然后统一使用 C# DLL,所以这是我在 visuaal Studio 中的 C# 代码(之后已将已编译的(使用 .NET 版本 4.6.1)OpenH264Lib.dll 放入 Debug 文件夹中,就像在示例中一样,并且我在 Visual Studio 2019 中没有收到任何参考错误):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sayfir
{
    public class Class1
    {
        public static OpenH264Lib.Encoder ok(string path)
        {
            var code = new OpenH264Lib.Encoder(path);
            return code;
        }
    }
}

好的,所以它可以完美构建,没有错误,并且它似乎与示例文件夹(在 Assets/Plugins 中)一样。

然后,当我将解决方案的 x64/Debug 文件夹中生成的 DLL 文件(OpenH264Lib.dll 和 Sayfir.dll)复制到 Unity 中的 Plugins 文件夹中时,然后尝试统一访问它,例如所以

public class Yetzirah {
        public static void Wellz() {
            var k = Sayfir.Class1.ok("2");
            Debug.Log(k);
        }
}

我在 Unity 控制台中遇到错误

Assets\scripts\Yetzirah.cs(45,12): error CS0012: The type 'Encoder' is defined in an assembly that is not referenced. You must add a reference to assembly 'OpenH264Lib, Version=1.0.7598.38705, Culture=neutral, PublicKeyToken=null'.

所以它似乎可以识别从上面的源代码编译的 Sayfir.dll 文件,但不是它所依赖的 OpenH264Lib.dll,即使两者都在同一个文件夹中(在 Assets/Plugins 中)

我听说过 .rsp 文件,但我尝试制作一个名称为 csc.rsp 的文件,并将其与内容一起添加到 Assets -r:OpenH264Lib

什么都没有

我也听说过.asmdef 文件,但是当我用内容制作了一个文件时

{
    "references":  "OpenH264Lib" 
}

但仍然一无所获,我对 asmdef 文件知之甚少,我也不太了解这个错误,或者总体上如何统一使用托管 C++ DLL。

我也见过an article that mentions referencing external DLL files in unity,但它说要去

Project > Edit References > .NET Assembly

问题是,我不知道如何在 UnityEditor 2020 中到达该屏幕,或者是否有办法到达那里或完成相同的事情,如果是这样,我不知道如何也不是能找到它搜索

我尝试使用标准 dllexports 制作另一个 DLL,头文件内容为

extern "C" __declspec(dllexport) int well();

然后在 .cpp 文件中

#include "deeall.h"
__declspec(dllexport) int well() {
    return 634;
}

然后在统一 C# 中

DllImport("deeall.dll", EntryPoint="well")]
public static extern int well();

然后它可以工作,但是当我在 dll extern int 中包含任何类型的托管库时,我收到错误__declspec(dllexport) cannot be applied to a function with the __clrcall calling convention.

如果我想为 C# 包装器使用 __declspec 导出,这是必需的

作为 hack,我尝试将以下内容添加到 Encoder.cpp 文件本身(来自 OpenH264Lib .dll 的源代码)

    __declspec(dllexport) int well() {
        String ^h = "ok";
        Encoder
        ^enc = 
        gcnew Encoder(
            h
        );
        return 4;
    }

尝试直接调用Encoder函数,(连同下面对Encoder.h的头文件修改:

namespace OpenH264Lib {
    extern "C" __declspec(dllexport) int well();
    //etc.. rest of code in namespace.....

) 然后团结起来

[DllImport("OpenH264Lib6.dll", EntryPoint="well")]
public static extern int well();

但是当我打电话给well() 时,unity 挂了一段时间然后崩溃了,没有任何错误

那么我怎样才能让 OpenH264Lib.dll 统一工作,或其他类似的托管编译 C++ DLL 统一工作?如果这是不可能的,有没有其他方法可以在 Unity 中使用 OpenH264?如果没有,还有其他解决方案可以统一实时编码 H264 视频(来自 byte[]s)吗?

【问题讨论】:

  • 请考虑编辑您的问题并添加代码片段,没有代码您可能只会得到一些猜测而没有实际的解决方案
  • @cleptus 之前添加了相关的代码部分,还需要添加什么
  • 您很可能会留下一些未使用的非托管资源,这将不允许释放类。您可以使用 using statement 代替 Dispose 但这不能解决您的问题(这只是一个异常安全的 Dispose 语句)
  • 你暴露了一个问题,但是只添加了一行代码,查看mcve帮助页面
  • @cleptus 基本上我使用的是 JINT native,我调用 ac# 函数返回原生数组,并调用 AsyncGPUReadback 函数读取数组,并带有回调,然后在回调中我创建一个新的包装类来保存本机数组,在 JINT s JavaScript 中对其进行操作,然后在 JavaScripts 中我设置了一个计时器来调用 .Dispose 从包装类中处理本机数组..只是不确定为什么 dispose 本身会使它崩溃

标签: javascript c# unity3d dll


【解决方案1】:

这是从内存中编写的,未经测试,但我认为它会起作用。

  • 确保正确加载 OpenH264Lib.dll,否则手动加载,例如在你的 Wrapper 中使用 Assembly.Load/Assembly.LoadFrom
  • 确保 openh264-1.7.0-win32.dll 位于可执行文件旁边或指定完整路径
  • 确保不要混合使用 32 位和 64 位
  • 像您一样构建您的 Sayfir 包装程序集,但不要公开 Encoder 类本身,而是包装您要使用的方法。这样 Unity 就不需要知道任何关于 OpenH264Lib.dll 的信息

例如


public class Wrapper 
{
  private Encoder _encoder;
  public Wrapper() 
  {
    // Be sure the openh264-1.7.0-win32.dll can be found! 
    // Native dlls should be located next to the actual executable if you call it without a full path!
    _encoder = new OpenH264Lib.Encoder("openh264-1.7.0-win32.dll");
  }

  public void Setup(/* ... */)
  {
    //_encoder.Setup(...)
  }
}

如果仍然无法正常工作,请在尝试加载 OpenH264Lib.dll 之前尝试使用此方法重定向 DLL 查找路径。在编码器类的源代码中,我们看到对 LoadLibrary 的调用,然后它将使用您在此处提供的路径来搜索 DLL。通过以null为参数再次调用函数,加载后恢复正常搜索顺序。

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);

https://www.pinvoke.net/default.aspx/kernel32.LoadLibrary

【讨论】:

  • 很有趣,谢谢你的回答,你知道是否可以在运行时调用本机 DLL 函数,它可以简化事情
  • 是的,使用你已经写过的DllImport。看看github.com/secile/OpenH264Lib.NET/blob/master/OpenH264Lib/…。您必须完全匹配签名或自己进行编组。
  • 谢谢,虽然我认为 dllimport 仅在 c# 代码中编译时才有效,但如果我想从他们的计算机中选择一个 dll 文件以在运行时加载它怎么办
  • 好的,那么您将使用标准的 WinAPI GetProcAddress pinvoke.net/default.aspx/kernel32.getprocaddress 并使用 GetDelegateForFunctionPointer docs.microsoft.com/de-de/dotnet/api/… 进行转换
  • 好的,我试过了,但 #1 是一个跨平台解决方案,而 #2 如何为每种加载的函数自动生成委托
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-25
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多