sal*_*inx 9 c# c++ dll interop dllimport
还有一个功能,它还没有工作.我基本上是通过使用P/Invoke从C#调用一些C++函数.有问题的功能确实向显示激光设备查询一些设备相关信息,例如最小和最大扫描速率以及每秒最大点数.
有问题的功能是:
int GetDeviceInfo(DWORD deviceIndex, DeviceInfo* pDeviceInfo);
Run Code Online (Sandbox Code Playgroud)
这是我给出的C++头文件.这是非常简短的C++ SDK描述的链接.我没有重建DLL文件的源代码,我也没有*.pdb文件(制造商无法提供):
#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);
Run Code Online (Sandbox Code Playgroud)
这是我目前使用的完整C#测试代码.所有功能都可以正常工作,除了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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在第73行第64行(cp.截图):
int r4 = GetDeviceInfo(0, ref pDevInfo);
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
An unhandled exception of type 'System.NullReferenceException' occured in MonchaTestSDK.exe
Additional information: Object reference not set to an instance of an object
Run Code Online (Sandbox Code Playgroud)
这是堆栈跟踪(我猜不能提供更好的堆栈跟踪而没有DLL的*.pdb文件):
MonchaTestSDK.exe!MonchaTestSDK.Program.Main(string [] args)第73行+ 0xa字节C#mscoreei.dll!73a8d91b()
[下面的框架可能不正确和/或缺失,没有为mscoreei.dll加载符号]
mscoree.dll !73cae879()
mscoree.dll!73cb4df8()
kernel32.dll!74a08654()
ntdll.dll!77354b17()
ntdll.dll!77354ae7()
一些反汇编:
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]
Run Code Online (Sandbox Code Playgroud)
知道我在这里做错了什么吗?
更新1:建议的调试选项:
正如评论中所建议的那样,我尝试启用本机/非托管代码调试:
选中Debug> Windows> Exceptions Settings>"Win32 Exceptions"复选框
选中项目>属性>调试选项卡>"启用非托管代码调试"复选框
我仍然没有得到任何有意义的异常堆栈.制造商无法提供DLL的*.pdb文件.
这是一个显示调试器停止在有问题的行时的图像(还显示了调试设置):
更新2:最低要求代码(cp.评论mpromonet)
这是能够调用的最小代码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();
}
Run Code Online (Sandbox Code Playgroud)
这导致与以前完全相同的错误:
An unhandled exception of type 'System.NullReferenceException' occured in MonchaTestSDK.exe
Additional information: Object reference not set to an instance of an object
Run Code Online (Sandbox Code Playgroud)
GetDeviceInfo(0, ref pDevInfo);从上面的代码中删除调用允许程序退出而没有任何错误.
更新3:完全char[] deviceType从DeviceInfo结构中删除
我char[] deviceType从结构定义中删除了:
[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;
}
Run Code Online (Sandbox Code Playgroud)
当我现在运行我的C#测试代码,我成功地接收maxScanrate,minScanrate并maxNumOfPoints从C++ DLL回来.这是相应的控制台输出:
GetDeviceInfo() [0]:
- min: 1000
- max: 40000
- points: 3000
Run Code Online (Sandbox Code Playgroud)
最后以下面的错误消息结束:
MonchaTestSDK.exe中的0x67623A68(clr.dll)抛出异常:0xC0000005:访问冲突读取位置0x00000000.
最后更新
我终于从制造商那里得到了一个更新的DLL.SDK中确实存在一个导致堆栈损坏的错误.所以基本上以下解决方案现在正常工作没有任何问题:
[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);
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢大家的大力支持!
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]声明该字段存储为一个char[32]数组,如标头中所示,即 31 个字符的空间和一个空终止符。
将其编组为字符串应该不是问题,dll 写入数组的任何内容都不会导致NullReferenceException.
我可以编译一个存根 dll,它可以使用 C# 代码正常加载,并且可以发送回 ANSI 字符串,并添加typedef byte...存根方法主体,例如:
int GetDeviceInfo(DWORD deviceIndex, DeviceInfo* pDeviceInfo)
{
std::string testString = "test string thats quite loooooooong";
pDeviceInfo->maxScanrate = 1234;
pDeviceInfo->minScanrate = 12345;
pDeviceInfo->maxNumOfPoints = 100 + deviceIndex;
sprintf_s(pDeviceInfo->type, "%.31s", testString.c_str());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这对我来说适用于 VS2017 C++ 和 .Net 4.6.1。
如果将 C# 声明更改为以下内容,会发生什么:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DeviceInfo
{
public UInt32 maxScanrate;
public UInt32 minScanrate;
public UInt32 maxNumOfPoints;
public UInt64 deviceTypePart1;
public UInt64 deviceTypePart2;
public UInt64 deviceTypePart3;
public UInt64 deviceTypePart4;
public string GetDeviceType()
{
if (Marshal.SizeOf(this) != 44) throw new InvalidOperationException();
List<byte> bytes = new List<byte>();
bytes.AddRange(BitConverter.GetBytes(deviceTypePart1));
bytes.AddRange(BitConverter.GetBytes(deviceTypePart2));
bytes.AddRange(BitConverter.GetBytes(deviceTypePart3));
bytes.AddRange(BitConverter.GetBytes(deviceTypePart4));
return Encoding.GetEncoding(1252).GetString(bytes.ToArray());
}
}
Run Code Online (Sandbox Code Playgroud)
[编辑]
我不知道为什么手动启动编组可以解决这个问题 - 请务必进行“负载测试”,以防堆/堆栈损坏错误仍然潜伏。
在您的旧代码中,是否Marshal.SizeOf返回 44 以外的其他内容?