C# dll work fine in Visual Studio ,but not in unity3D

Hi, everyone, Happy new year!

I use a C# dll library to read data from my USB(HID) device. (See http://www.codeproject.com/Articles/18099/A-USB-HID-Component-for-C )
The dll works very well in Visual Studio 2008(.Net 2.0, Windows7 32 bit), but not in Unity3D(Pro, version 4.3). I kept getting an exception System.IO.IOException: Invalid handle in my application. The error message is:

System.IO.IOException: Invalid handle.
  at System.IO.FileStream..ctor (IntPtr handle, FileAccess access, Boolean ownsHandle, Int32 bufferSize, Boolean isAsync, Boolean noBuffering) [0x0007d] in /Users/builduser/buildslave/monoAndRuntimeClassLibs/build/mcs/class/corlib/System.IO/FileStream.cs:97 
  at System.IO.FileStream..ctor (IntPtr handle, FileAccess access, Boolean ownsHandle, Int32 bufferSize, Boolean isAsync) [0x00000] in <filename unknown>:0 
  at System.IO.FileStream..ctor (Microsoft.Win32.SafeHandles.SafeFileHandle handle, FileAccess access, Int32 bufferSize, Boolean isAsync) [0x00000] in <filename unknown>:0 
  at (wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (Microsoft.Win32.SafeHandles.SafeFileHandle,System.IO.FileAccess,int,bool)
  at UsbLibrary.HIDDevice.Initialise (System.String strPath) [0x00000] in <filename unknown>:0 
UnityEngine.Debug:Log(Object)
Test:Update() (at Assets/Test.cs:59)

My partial program is as follows:

/// <summary>
/// Initialises the device
/// </summary>
/// <param name="strPath">Path to the device</param>
private void Initialise(string strPath)
{
// Create the file from the device path
        m_hHandle = CreateFile(strPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);
       if ( m_hHandle != InvalidHandleValue || m_hHandle == null)// if the open worked...
{
		IntPtr lpData;
		if (HidD_GetPreparsedData(m_hHandle, out lpData))	// get windows to read the device data into an internal buffer
		{
                    try
                    {
                        HidCaps oCaps;
                        HidP_GetCaps(lpData, out oCaps);	// extract the device capabilities from the internal buffer
                        m_nInputReportLength = oCaps.InputReportByteLength;	// get the input...
                        m_nOutputReportLength = oCaps.OutputReportByteLength;	// ... and output report lengths
                        m_oFile = new FileStream(new SafeFileHandle(m_hHandle, true), FileAccess.Read | FileAccess.Write, m_nInputReportLength, true);  // This met with the IOException error.
                        BeginAsyncRead();	// kick off the first asynchronous read 
                    }
                    catch (Exception ex)
                    {
                        throw HIDDeviceException.GenerateWithWinError("Failed to get the detailed data from the hid.");
                    }
	            finally
		    {
			HidD_FreePreparsedData(ref lpData);	// before we quit the funtion, we must free the internal buffer reserved in GetPreparsedData
		    }
		}
		 else	// GetPreparsedData failed? Chuck an exception
		{
			throw HIDDeviceException.GenerateWithWinError("GetPreparsedData failed");
		}
	}
	else	// File open failed? Chuck an exception
	{
		m_hHandle = IntPtr.Zero;
		throw HIDDeviceException.GenerateWithWinError("Failed to create device file");
	}            
}

I found the Handle value is: m_hHandle=2392 , and the value of strPath is:

\?\hid#vid_1915&pid_0027#7&232702e4&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030},

the strPath is just the same as in Visual Studio 2008.

When the dll run to this sentence:

m_oFile = new FileStream(new SafeFileHandle(m_hHandle, true), FileAccess.Read | FileAccess.Write, m_nInputReportLength, true)

the IOExecption error will appear at once.

I have tried several dll ( generic_hid_cs_50, and generic_hid_cs_46, see http://www.lvr.com/hidpage.htm ). I modified them to dll. They all work well in Visual Studio 2008, but not in unity3D. All the problems are at FileStream or Win32.ReadFile sentence.

I have been battling this for 2 weeks now,and would go mad. Any help would be appreciated.

Thanks in advance.

If your DLL has no native dependencies, it should work just fine. Are you DLLs managed ?

Thanks for your answer. My dll is programmed using C#, but called some Win32 dll:

#region P/Invoke
/// <summary>
/// Gets the GUID that Windows uses to represent HID class devices
/// </summary>
/// <param name="gHid">An out parameter to take the Guid</param>
[DllImport("hid.dll",      SetLastError = true)] protected static extern void HidD_GetHidGuid(out Guid gHid);
/// <summary>
/// Allocates an InfoSet memory block within Windows that contains details of devices.
/// </summary>
/// <param name="gClass">Class guid (e.g. HID guid)</param>
/// <param name="strEnumerator">Not used</param>
/// <param name="hParent">Not used</param>
/// <param name="nFlags">Type of device details required (DIGCF_ constants)</param>
/// <returns>A reference to the InfoSet</returns>
[DllImport("setupapi.dll", SetLastError = true)] protected static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, [MarshalAs(UnmanagedType.LPStr)] string strEnumerator, IntPtr hParent, uint nFlags);
/// <summary>
/// Frees InfoSet allocated in call to above.
/// </summary>
/// <param name="lpInfoSet">Reference to InfoSet</param>
/// <returns>true if successful</returns>
[DllImport("setupapi.dll", SetLastError = true)] protected static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
/// <summary>
/// Gets the DeviceInterfaceData for a device from an InfoSet.
/// </summary>
/// <param name="lpDeviceInfoSet">InfoSet to access</param>
/// <param name="nDeviceInfoData">Not used</param>
/// <param name="gClass">Device class guid</param>
/// <param name="nIndex">Index into InfoSet for device</param>
/// <param name="oInterfaceData">DeviceInterfaceData to fill with data</param>
/// <returns>True if successful, false if not (e.g. when index is passed end of InfoSet)</returns>
[DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiEnumDeviceInterfaces(IntPtr lpDeviceInfoSet, uint nDeviceInfoData, ref Guid gClass, uint nIndex, ref DeviceInterfaceData oInterfaceData);
/// <summary>
/// SetupDiGetDeviceInterfaceDetail
/// Gets the interface detail from a DeviceInterfaceData. This is pretty much the device path.
/// You call this twice, once to get the size of the struct you need to send (nDeviceInterfaceDetailDataSize=0)
/// and once again when you've allocated the required space.
/// </summary>
/// <param name="lpDeviceInfoSet">InfoSet to access</param>
/// <param name="oInterfaceData">DeviceInterfaceData to use</param>
/// <param name="lpDeviceInterfaceDetailData">DeviceInterfaceDetailData to fill with data</param>
/// <param name="nDeviceInterfaceDetailDataSize">The size of the above</param>
/// <param name="nRequiredSize">The required size of the above when above is set as zero</param>
/// <param name="lpDeviceInfoData">Not used</param>
/// <returns></returns>
[DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr lpDeviceInfoSet, ref DeviceInterfaceData oInterfaceData, IntPtr lpDeviceInterfaceDetailData, uint nDeviceInterfaceDetailDataSize, ref uint nRequiredSize, IntPtr lpDeviceInfoData);
/// <summary>
/// SetupDiGetDeviceInterfaceDetail
/// Gets the interface detail from a DeviceInterfaceData. This is pretty much the device path.
/// You call this twice, once to get the size of the struct you need to send (nDeviceInterfaceDetailDataSize=0)
/// and once again when you've allocated the required space.
/// </summary>
/// <param name="lpDeviceInfoSet">InfoSet to access</param>
/// <param name="oInterfaceData">DeviceInterfaceData to use</param>
/// <param name="oDetailData">DeviceInterfaceDetailData to fill with data</param>
/// <param name="nDeviceInterfaceDetailDataSize">The size of the above</param>
/// <param name="nRequiredSize">The required size of the above when above is set as zero</param>
/// <param name="lpDeviceInfoData">Not used</param>
/// <returns></returns>
[DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr lpDeviceInfoSet, ref DeviceInterfaceData oInterfaceData, ref DeviceInterfaceDetailData oDetailData, uint nDeviceInterfaceDetailDataSize, ref uint nRequiredSize, IntPtr lpDeviceInfoData);
/// <summary>
/// Registers a window for device insert/remove messages
/// </summary>
/// <param name="hwnd">Handle to the window that will receive the messages</param>
/// <param name="oInterface">DeviceBroadcastInterrface structure</param>
/// <param name="nFlags">set to DEVICE_NOTIFY_WINDOW_HANDLE</param>
/// <returns>A handle used when unregistering</returns>
[DllImport("user32.dll",   SetLastError = true)] protected static extern IntPtr RegisterDeviceNotification(IntPtr hwnd, DeviceBroadcastInterface oInterface, uint nFlags);
/// <summary>
/// Unregister from above.
/// </summary>
/// <param name="hHandle">Handle returned in call to RegisterDeviceNotification</param>
/// <returns>True if success</returns>
[DllImport("user32.dll",   SetLastError = true)] protected static extern bool UnregisterDeviceNotification(IntPtr hHandle);
/// <summary>
/// Gets details from an open device. Reserves a block of memory which must be freed.
/// </summary>
/// <param name="hFile">Device file handle</param>
/// <param name="lpData">Reference to the preparsed data block</param>
/// <returns></returns>
[DllImport("hid.dll",      SetLastError = true)] protected static extern bool HidD_GetPreparsedData(IntPtr hFile, out IntPtr lpData);
/// <summary>
/// Frees the memory block reserved above.
/// </summary>
/// <param name="pData">Reference to preparsed data returned in call to GetPreparsedData</param>
/// <returns></returns>
[DllImport("hid.dll",      SetLastError = true)] protected static extern bool HidD_FreePreparsedData(ref IntPtr pData);
/// <summary>
/// Gets a device's capabilities from the preparsed data.
/// </summary>
/// <param name="lpData">Preparsed data reference</param>
/// <param name="oCaps">HidCaps structure to receive the capabilities</param>
/// <returns>True if successful</returns>
[DllImport("hid.dll",      SetLastError = true)] protected static extern int HidP_GetCaps(IntPtr lpData, out HidCaps oCaps);
/// <summary>
/// Creates/opens a file, serial port, USB device... etc
/// </summary>
/// <param name="strName">Path to object to open</param>
/// <param name="nAccess">Access mode. e.g. Read, write</param>
/// <param name="nShareMode">Sharing mode</param>
/// <param name="lpSecurity">Security details (can be null)</param>
/// <param name="nCreationFlags">Specifies if the file is created or opened</param>
/// <param name="nAttributes">Any extra attributes? e.g. open overlapped</param>
/// <param name="lpTemplate">Not used</param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)] protected static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)] string strName, uint nAccess, uint nShareMode, IntPtr lpSecurity, uint nCreationFlags, uint nAttributes, IntPtr lpTemplate);
/// <summary>
/// Closes a window handle. File handles, event handles, mutex handles... etc
/// </summary>
/// <param name="hFile">Handle to close</param>
/// <returns>True if successful.</returns>
[DllImport("kernel32.dll", SetLastError = true)] protected static extern int CloseHandle(IntPtr hFile);
#endregion

The error sentence:
m_oFile = new FileStream(new SafeFileHandle(m_hHandle, true), FileAccess.Read | FileAccess.Write, m_nInputReportLength, true);
It is a .NET method. It uses asynchronous read. But it’s handle is created by calling Win32 functiong.

In unity3d,I also use these Win32 function:

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]	
public static extern System.IntPtr GetForegroundWindow();		
[DllImport("user32.dll")]	
static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);		
[DllImport("user32.dll")]	
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
void  Start () 		
{		
if (isrunning) return;				
	hMainWindow = GetForegroundWindow();		
	newWndProc = new WndProcDelegate(wndProc);		
	newWndProcPtr = Marshal.GetFunctionPointerForDelegate(newWndProc);		
	oldWndProcPtr = SetWindowLong(hMainWindow, -4, newWndProcPtr);		
	isrunning = true;
	InitializeComponent ();
}

private static IntPtr StructToPtr(object obj)		
{		
	var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));		
	Marshal.StructureToPtr(obj, ptr, false);		
	return ptr;		
}
IntPtr wndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)		
{
	usb.CheckDevicePresent ();
	usb.ParseMessages((int)msg, wParam);
	return DefWindowProc(hWnd, msg, wParam, lParam);		
}