Okay one more function that it's not yet working. I am basically calling some C++ functions from C# by using P/Invoke. The problematic function does query a show laser device for some device related information, such as minimal and maximal scan rates and maximal points per second.
The problematic function is:
int GetDeviceInfo(DWORD deviceIndex, DeviceInfo* pDeviceInfo);
Here's the C++ header file that I was given. That's a link to the very brief C++ SDK description. I don't have the sources to rebuild the DLL file and I also don't have the *.pdb file (the manufacturer can not supply it):
#pragma once
#ifdef STCL_DEVICES_DLL
#define STCL_DEVICES_EXPORT extern "C" _declspec(dllexport)
#else
#define STCL_DEVICES_EXPORT extern "C" _declspec(dllimport)
#endif
enum SD_ERR
{
SD_ERR_OK = 0,
SD_ERR_FAIL,
SD_ERR_DLL_NOT_OPEN,
SD_ERR_INVALID_DEVICE, //device with such index doesn't exist
SD_ERR_FRAME_NOT_SENT,
};
#pragma pack (1)
struct LaserPoint
{
WORD x;
WORD y;
byte colors[6];
};
struct DeviceInfo
{
DWORD maxScanrate;
DWORD minScanrate;
DWORD maxNumOfPoints;
char type[32];
};
//////////////////////////////////////////////////////////////////////////
///Must be called when starting to use
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int OpenDll();
//////////////////////////////////////////////////////////////////////////
///All devices will be closed and all resources deleted
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT void CloseDll();
//////////////////////////////////////////////////////////////////////////
///Search for .NET devices (Moncha.NET now)
///Must be called after OpenDll, but before CreateDeviceList!
///In pNumOfFoundDevs can return number of found devices (optional)
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SearchForNETDevices(DWORD* pNumOfFoundDevs);
//////////////////////////////////////////////////////////////////////////
///Creates new list of devices - previous devices will be closed
///pDeviceCount returns device count
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int CreateDeviceList(DWORD* pDeviceCount);
//////////////////////////////////////////////////////////////////////////
///Returns unique device name
///deviceIndex is zero based device index
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int GetDeviceIdentifier(DWORD deviceIndex, WCHAR** ppDeviceName);
//////////////////////////////////////////////////////////////////////////
///Send frame to device, frame is in following format:
///WORD x
///WORD y
///byte colors[6]
///so it's 10B point (=> dataSize must be numOfPoints * 10)
///scanrate is in Points Per Second (pps)
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);
//////////////////////////////////////////////////////////////////////////
///Returns true in pCanSend if device is ready to send next frame
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int CanSendNextFrame(DWORD deviceIndex, bool* pCanSend);
//////////////////////////////////////////////////////////////////////////
///Send DMX if device supports it - pDMX must be (!!!) 512B long
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendDMX(DWORD deviceIndex, byte* pDMX);
//////////////////////////////////////////////////////////////////////////
///Send blank point to position x, y
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int SendBlank(DWORD deviceIndex, WORD x, WORD y);
//////////////////////////////////////////////////////////////////////////
///Get device info
//////////////////////////////////////////////////////////////////////////
STCL_DEVICES_EXPORT int GetDeviceInfo(DWORD deviceIndex, DeviceInfo* pDeviceInfo);
This is the complete C# test code I am currently using. All the functions work fine, except for GetDeviceInfo(...)
:
using System;
using System.Threading;
using System.Runtime.InteropServices;
namespace MonchaTestSDK {
public class Program {
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int OpenDll();
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern void CloseDll();
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SearchForNETDevices(ref UInt32 pNumOfFoundDevs);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int CreateDeviceList(ref UInt32 pDeviceCount);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int GetDeviceIdentifier(UInt32 deviceIndex, out IntPtr ppDeviceName);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int CanSendNextFrame(UInt32 deviceIndex, ref bool pCanSend);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // OK
public static extern int SendBlank(UInt32 deviceIndex, UInt16 x, UInt16 y);
[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)] // FAILS
public static extern int GetDeviceInfo(UInt32 deviceIndex, ref DeviceInfo pDeviceInfo);
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
public UInt16 x;
public UInt16 y;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] colors;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DeviceInfo {
public UInt32 maxScanrate;
public UInt32 minScanrate;
public UInt32 maxNumOfPoints;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string deviceType;
}
public static void Main(string[] args) {
Console.WriteLine("Moncha SDK\n");
OpenDll();
Console.WriteLine("StclDevices.dll is open.");
UInt32 deviceCount1 = 0;
int r1 = SearchForNETDevices(ref deviceCount1);
Console.WriteLine("SearchForNETDevices() [" + r1+"]: "+deviceCount1);
UInt32 deviceCount2 = 0;
int r2 = CreateDeviceList(ref deviceCount2);
Console.WriteLine("CreateDeviceList() ["+r2+"]: "+deviceCount2);
IntPtr pString;
int r3 = GetDeviceIdentifier(0, out pString);
string devname = Marshal.PtrToStringUni(pString);
Console.WriteLine("GetDeviceIdentifier() ["+r3+"]: "+devname);
DeviceInfo pDevInfo = new DeviceInfo();
pDevInfo.type = "";
int r4 = GetDeviceInfo(0, ref pDevInfo);
Console.WriteLine("GetDeviceInfo() ["+r4+"]: ");
Console.WriteLine(" - min: "+pDevInfo.minScanrate);
Console.WriteLine(" - max: " + pDevInfo.maxScanrate);
Console.WriteLine(" - points: " + pDevInfo.maxNumOfPoints);
Console.WriteLine(" - type: " + pDevInfo.deviceType);
Thread.Sleep(5000);
CloseDll();
}
}
}
On line 73 line 64 (cp. screenshot):
int r4 = GetDeviceInfo(0, ref pDevInfo);
I receive the following error:
An unhandled exception of type 'System.NullReferenceException' occured in MonchaTestSDK.exe
Additional information: Object reference not set to an instance of an object
This is the stack trace (can't provide better stack trace without the DLL's *.pdb file I guess):
MonchaTestSDK.exe!MonchaTestSDK.Program.Main(string[] args) Line 73 + 0xa bytes C# mscoreei.dll!73a8d91b()
[Frames below may be incorrect and/or missing, no symbols loaded for mscoreei.dll]
mscoree.dll!73cae879()
mscoree.dll!73cb4df8()
kernel32.dll!74a08654()
ntdll.dll!77354b17()
ntdll.dll!77354ae7()
Some disassembly:
int r4 = GetDeviceInfo(0, ref pDevInfo);
05210749 int 3
0521074A push ebp
0521074B cwde
0521074C xor ecx,ecx
0521074E call 0521011C
05210753 int 3
05210754 test dword ptr [eax-1],edx
05210757 ?? ??
05210758 dec dword ptr [ebx-0AF7Bh]
0521075E dec dword ptr [ecx-6F466BBBh]
Any idea what I am doing wrong here?
Update 1: Suggested debug options:
As suggested in the comments, I tried to enable native/unmanaged code debugging:
Debug > Windows > Exceptions Settings > "Win32 Exceptions" checkbox ticked
Project > Properties > Debug tab > "Enable unmanaged code debugging" checkbox ticked
I still don't get any meaningful exception stack. The manufacturer can't supply me the DLL's *.pdb file.
Here's an image showing the debugger when stopped at the problematic line (debug settings are also shown):
Update 2: Minimal Required Code (cp. comment of mpromonet)
This is the minimal required code to be able to call GetDeviceInfo(...)
:
public static void Main(string[] args) {
OpenDll();
UInt32 deviceCount = 0;
CreateDeviceList(ref deviceCount);
DeviceInfo pDevInfo = new DeviceInfo();
GetDeviceInfo(0, ref pDevInfo); // error occurs on this line
CloseDll();
}
This leads to the exact same error as before:
An unhandled exception of type 'System.NullReferenceException' occured in MonchaTestSDK.exe
Additional information: Object reference not set to an instance of an object
Removing the call GetDeviceInfo(0, ref pDevInfo);
from the code above allows the program to exit without any error.
Update 3: Removing char[] deviceType
from DeviceInfo
struct completely
I removed char[] deviceType
from the struct defintion:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct DeviceInfo {
public UInt32 maxScanrate;
public UInt32 minScanrate;
public UInt32 maxNumOfPoints;
//[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
//public string deviceType;
}
When I run my C# test code now, I successfully receive maxScanrate
, minScanrate
and maxNumOfPoints
back from the C++ DLL. Here's the corresponding console output:
GetDeviceInfo() [0]:
- min: 1000
- max: 40000
- points: 3000
Finally ending in the following error message:
Exception thrown at 0x67623A68 (clr.dll) in MonchaTestSDK.exe: 0xC0000005: Access violation reading location 0x00000000.
Final Update
I finally got an updated DLL from the manufacturer. There was indeed a bug within the SDK that caused the stack to get corrupted. So basically the following solution now works fine without any issues:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct DeviceInfo {
public UInt32 maxScanrate;
public UInt32 minScanrate;
public UInt32 maxNumOfPoints;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string deviceType;
}
private void queryDeviceProperties(UInt32 index) {
HwDeviceInfo pDevInfo = new HwDeviceInfo();
int code = GetDeviceInfo(index, ref pDevInfo);
if(code==0) {
Console.WriteLine(pDevInfo.minScanrate);
Console.WriteLine(pDevInfo.maxScanrate);
Console.WriteLine(pDevInfo.maxNumOfPoints);
Console.WriteLine(pDevInfo.type);
} else {
Console.WriteLine("Error Code: "+code);
}
}
Thank you all for the great support!
pDevInfo.type = "";
, so a string with 32 spaces in it and see if it helps in any ways – SurroundSearchForNETDevices(DWORD* pNumOfFoundDevs)
andCreateDeviceList(DWORD* pDeviceCount)
both functions ask for a DWORD pointer as parameter. Currently I am usingref UInt32
. What about usingUIntPtr
instead? – Separatrix0
; this probably means success? The results I get from the passed pointers are also correct. – Separatrixconst UInt32 numOfPts = 600;
which makes the faulting lineConsole.WriteLine(" - type: " + pDevInfo.type);
which is much more reasonable for throwing a managed NullReferenceException. OP gave the wrong line but it didn't stop me. – Clipfedr4 = GetDeviceInfo(0, ref pDevInfo);
. – Separatrixchar
type lengths between C++ and C#. I will now dive deeper into this topic. – Separatrix