从Unity3d调用本机DLL的最佳方式是什么



大家好,我在unity3d pro上遇到了一些问题,也许有人使用过DLL原生插件,可以告诉我我做错了什么。

首先,我将解释我的目标是什么。我有一个来自西门子PLCSim软件的COM对象,我已经用Visual Studio很好地工作了。下面是测试代码。

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public S7PROSIMLib.S7ProSim PLCSimConn = new S7PROSIMLib.S7ProSim();
        public Form1()
        {
            InitializeComponent();
        }
        private void button_Connect_Click(object sender, EventArgs e)
        {
            PLCSimConn.Connect();
            label_CPUState.Text = PLCSimConn.GetState();
            label_ScanMode.Text = PLCSimConn.GetScanMode().ToString();
        }
    }
}

我创建了一个unity3d项目来测试同样的东西。我将dll导入到我的资产中,并且能够从c#脚本中调用S7PROSIMLib的方法和实例。COM Api在这里,https://cache.industry.siemens.com/dl/files/855/1139855/att_29424/v1/S7WSPSCB.pdf

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using S7PROSIMLib;
public class TestNative : MonoBehaviour {
    public S7ProSimClass ps; 
    // Use this for initialization
    void Start () {
        ps = new S7ProSimClass ();
        ps.Connect ();
        Debug.Log (ps.GetState());
    }
}

现在在运行时,我得到以下内容:

COMException

System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (Int32 errorCode) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs:1031)
System.__ComObject.Initialize (System.Type t) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/__ComObject.cs:103)
(wrapper remoting-invoke-with-check) System.__ComObject:Initialize (System.Type)
Mono.Interop.ComInteropProxy.CreateProxy (System.Type t) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/Mono.Interop/ComInteropProxy.cs:108)
System.Runtime.Remoting.RemotingServices.CreateClientProxyForComInterop (System.Type type) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs:588)
System.Runtime.Remoting.Activation.ActivationServices.CreateProxyForType (System.Type type) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Remoting.Activation/ActivationServices.cs:234)
TestNative.Start () (at Assets/Scripts/TestNative.cs:13)

我看过unity3d插件教程https://www.youtube.com/watch?v=DfRYLwG1Bug它不调用第三方DLL。

我已阅读以下内容:https://msdn.microsoft.com/en-us/library/ms973872.aspx它有很多关于非托管DLL和托管DLL的好信息。

基于上面链接中的以下片段:

调用COM API有两种方法可以从托管代码调用COM组件:通过COM互操作(在所有托管语言中都可用)或通过C++互操作(可在C++中使用)。对于调用与OLE Automation兼容的COM组件,建议使用COM互操作。CLR将负责COM组件的激活和参数封送处理

对于基于接口定义语言(IDL)调用COM组件,建议使用C++互操作。C++层可能非常薄,其余托管代码可以用任何托管语言编写。COM互操作依赖于类型库中的信息来进行正确的互操作调用,但类型库通常不包含IDL文件中存在的所有信息。使用C++互操作通过允许直接访问这些COM API来解决这个问题

我相信我已经完成了上面unity c#脚本中显示的COM互操作。但这并没有奏效。我的另一个选择是通过C++互操作,但我还没有在网上找到任何例子。大多数示例都很简单,不需要调用COM对象或任何其他第三方DLL。如果有人能以正确的方式指导我,我将不胜感激。基本上,它可以归结为什么是实现这一点的最佳方法,只需最少地重写方法?非常感谢。

第1版:我在youtube上看了这个视频,有人把它带到了工作中,但我一直没能得到他的回应。至少我知道它应该在unity3d上工作。https://www.youtube.com/watch?v=EGFMjUJN7ZU

Edit2:Unity3d-未能加载';Assets/Plugin/QCARWrapper.dll';将尝试使用32位编辑器,如本文所示。

对于任何可能需要帮助的人。西门子PLCSim COM类型库仅适用于32位Unity Editor。

经过大量的尝试和错误,以及通过论坛阅读的更多内容,这个解决方案实际上非常容易。我使用tlbimp.exe生成了一个类型库DLL,然后我将其放入assets/plugins文件夹中,并在unity3d编辑器属性中注意到,新的DLL被视为托管代码。请注意,这个特定的DLL在64位Unity Editor上不起作用,它抛出了一个COMException。然而,新的类型库DLL在32位Unity Editor中运行良好,我想DLL中的某个地方有一条指令指定只能在32位中工作。

我在这里的存储库中记录了所有内容https://github.com/fredz0003/myS7ProSimLib我还放置了托管DLL以备将来使用,这样你就不会遇到我遇到的麻烦。

下面是一个示例代码:

using UnityEngine; using myS7ProSimLib;
public class TestNative : MonoBehaviour {
    /*
    * Used tlbimp.exe to generate library DLL
    * place new generated DLL on assets/plugins
    * also note that the DLL is treated as managed code
    */
    public S7ProSimClass ps;
    public bool input_0_0;

    void Start () {
        ps = new S7ProSimClass();
        ps.Connect();
        print("State " + ps.GetState());
        ps.SetScanMode(ScanModeConstants.ContinuousScan);
        // Here we pass the ref as an obj, since WriteInputPoint method
        // can take bit, word, dwords, as addresses ref obj can take the
        // for of bool, int, float, etc.
        object refInput0_0 = input_0_0;
        ps.WriteInputPoint(0, 0, ref refInput0_0);  }    }

最新更新