六月 11, 2015

Unity 5.0 中使用C/C++开发的dll插件使用中的问题 DllNotFoundException 问题集

Written by

Unity 中会经常用到C/C++开发的动态库(dll),Unity称为第三方插件,或plugin,这是非常厉害的扩展方法,但是Unity使用dll也非常痛苦,错误百出。

最常见的问题是 DllNotFoundException,就是找不到动态库。导致DllNotFoundException的原因很多,现把一些可能的情况,以及其解决方案一一列举:

1、DllNotFoundException: SerialPortDevice.dll
demo.Start () (at Assets/demo.cs:39)

Unity基于Mono实现的跨平台特性,但是Mono的 .net framework只充分实现了.net 2.0 subet,至于.net framework 4.5,照目前的速度,估计要等到下辈子了。

操作串口有很多问题,于是我自己用C++分装了一个dll,用于管理串口。从dll中导出的C#代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

// Wrapper of library by C#
namespace PluginImport
{
	#region delegates
	public delegate void ReceivedDataCallback(IntPtr serialPortRef, ref byte[] data, int len);
	//
	public delegate void SerialPortStatusChanged(IntPtr serialPortRef, SerialPortStatus status, string msg);
	#endregion
	
	#region Data types	
	// Serial Port Status
	public enum SerialPortStatus
	{
		PortClosed,              // 端口已关闭
		PortOpened,              // 端口已打开
		PortError,               // 端口错误
		PortInvalid,             // 端口不可用
	};
	
	// SerialPort Identity	
	public struct SerialPortRef
	{
		public IntPtr Identity;
	};
	
	#endregion
	
	#region API Wraper
	public class SerialPortDevice
	{
		// Create a serial port for lately operations. return Identity of new serial port.
		// Return NULL if create failed.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		[return: MarshalAs(UnmanagedType.Struct)]
		public static extern SerialPortRef CreateSerialPort();

		// Register handl to receive data from a SerialPort.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern void RegisterReceivedDataCallback(SerialPortRef sp, ReceivedDataCallback handle);
		
		// Register handl to receive serial port status changed event.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern void RegisterSerialPortStatusChangedCallback(SerialPortRef sp, SerialPortStatusChanged handle);
		
		// Open a serial port by name.
		// Return false if open failed.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern bool OpenSerialPort(SerialPortRef sp, string portName, int bauntRate);
		
		// Write and send data by serial port
		// Return the length sent by serial port
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern int SerialPortWrite(SerialPortRef sp, byte[] data, int len);
		
		// Read data from serial port
		// Return the length readed from serial port
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern int SerialPortRead(SerialPortRef sp, ref byte[] buffer, int bufferLen);
		
		// Check wether a serail port is openning.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern bool IsSerialPortOpenning(SerialPortRef sp);
		
		// Close a serial port.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern void CloseSerialPort(SerialPortRef sp);
		
		// Destroy a serial port.
		[DllImport("SerialPortDevice.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public static extern void DestroySerialPort(SerialPortRef sp);
	};
	#endregion
}

然后把dll拷贝到Unit Prj 的assert目录下,报错:

DllNotFoundException: SerialPortDevice.dll
demo.Start () (at Assets/demo.cs:39)

折腾了很久,发现在导入dll根本不需要后缀“.dll”,即写上dll文件名就OK了,不需要.dll后缀:

		[DllImport("SerialPortDevice", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
		[return: MarshalAs(UnmanagedType.Struct)]

但是这是VS的C#项目的默认做法。一个习惯性的动作导致了这个问题。于是把所有“.dll”删除,OK了。

 

2、Failed to load ‘Assets/PNLib.dll’ with error ‘The specified module could not be found.‘, GetDllDirectory returned ”. If GetDllDirectory returned non empty path, check that you’re using SetDirectoryDll correctly.

这种情况是,找到dll了,但是无法装载。看提示基本还是一头雾水: ‘The specified module could not be found.‘, GetDllDirectory returned ”。看似还是路径的问题。

这个问题测试了很多种情况,基本上是,只要是dll放到Assets目录下(或Assets下创建的Plugin目录下)就都会提示这个错误。而放到Assets外与Assets同目录就OK了,这难道不是Unity的某个bug吗?更奇怪的是之前在Assets下的Plugin里放的另一个dll(SerialPortDevice.dll)就不会有这样的问题。何解?

 

如果还有其他问题,后续继续补充。。。。。。

 

Category : C/C++C#Unit

Tags :

Comments

One Response

  1. 匿名说道:

    很感谢你的第二个回答,两天搞不定

发表评论

电子邮件地址不会被公开。

Proudly powered by WordPress and Sweet Tech Theme