求助:C# 调用C++的dll的回调函数的结构体数组的问题,为什么只得到数组的一条记录。
DLL接口:structCardEvent{DWORDCardNo;//卡号INT32EvtYear;//年INT32EvtMonth;//月INT32EvtDay;//...
DLL接口:
struct CardEvent
{
DWORD CardNo; //卡号
INT32 EvtYear; //年
INT32 EvtMonth; //月
INT32 EvtDay; //日
INT32 EvtHour; //小时
INT32 EvtMin; //分钟
INT32 EvtSec; //秒
INT32 EvtDoor; 门序号
INT32 EvtType; 事件类别
};
回调函数:hDev为收到事件的设备,pEvt:事件信息数组, nEvtCnt 事件个数
typedef VOID (CALLBACK* lpOnRecvCardEvent)(HANDLE hDev, CardEvent* pEvt, INT32 nEvtCnt);
VOID ACSSetupCardEventCallback (lpOnRecvCardEvent lpCall); :设定系统接收到卡事件后的回调函数.lpCall:回调函数地址。返回:无。
C# 定义及调用:
public delegate void CardEvent(IntPtr hDev, strcNACSCardEvent[] phld, Int32 nEvtCnt);
[DllImport("Comm.dll")] //设定系统接收到卡事件后的回调函数
private extern static void ACSSetupCardEventCallback(CardEvent lpCall);
public struct CardEvent
{
public uint CardNo; //卡号
public Int32 EvtYear; //年
public Int32 EvtMonth; //月
public Int32 EvtDay; //日
public Int32 EvtHour; //小时
public Int32 EvtMin; //分钟
public Int32 EvtSec; //秒
public Int32 EvtDoor; //门序号
public Int32 EvtType;
}
public static void themain(IntPtr hDev, CardEvent[] phld1, Int32 nEvtCnt)
{
for (int i = 0; i < nEvtCnt; i++)
{
System.Console.Write("事件数:" + nEvtCnt);
System.Console.WriteLine();
}
for (int i = 0; i < phld1.Length; i++)
{
string time = phld1[i].EvtYear + "-" + phld1[i].EvtMonth + "-" + phld1[i].EvtDay + " " + phld1[i].EvtHour + ":" + phld1[i].EvtMin + ":" + phld1[i].EvtSec;
System.Console.Write("时间:" + time);
System.Console.WriteLine();
}
}
static void Main(string[] args)
{
...........//前面与设备的连接
lCardEvent myhuidiao = new CardEvent(themain);
ACSSetupCardEventCallback(myhuidiao);
}
运行后,返回的事件个数nEvtCnt 是正确的(有多少显示为多少),但返回结构体数组则只有第一条记录。不知道为什么?求高手解答,高分感谢啊。 展开
struct CardEvent
{
DWORD CardNo; //卡号
INT32 EvtYear; //年
INT32 EvtMonth; //月
INT32 EvtDay; //日
INT32 EvtHour; //小时
INT32 EvtMin; //分钟
INT32 EvtSec; //秒
INT32 EvtDoor; 门序号
INT32 EvtType; 事件类别
};
回调函数:hDev为收到事件的设备,pEvt:事件信息数组, nEvtCnt 事件个数
typedef VOID (CALLBACK* lpOnRecvCardEvent)(HANDLE hDev, CardEvent* pEvt, INT32 nEvtCnt);
VOID ACSSetupCardEventCallback (lpOnRecvCardEvent lpCall); :设定系统接收到卡事件后的回调函数.lpCall:回调函数地址。返回:无。
C# 定义及调用:
public delegate void CardEvent(IntPtr hDev, strcNACSCardEvent[] phld, Int32 nEvtCnt);
[DllImport("Comm.dll")] //设定系统接收到卡事件后的回调函数
private extern static void ACSSetupCardEventCallback(CardEvent lpCall);
public struct CardEvent
{
public uint CardNo; //卡号
public Int32 EvtYear; //年
public Int32 EvtMonth; //月
public Int32 EvtDay; //日
public Int32 EvtHour; //小时
public Int32 EvtMin; //分钟
public Int32 EvtSec; //秒
public Int32 EvtDoor; //门序号
public Int32 EvtType;
}
public static void themain(IntPtr hDev, CardEvent[] phld1, Int32 nEvtCnt)
{
for (int i = 0; i < nEvtCnt; i++)
{
System.Console.Write("事件数:" + nEvtCnt);
System.Console.WriteLine();
}
for (int i = 0; i < phld1.Length; i++)
{
string time = phld1[i].EvtYear + "-" + phld1[i].EvtMonth + "-" + phld1[i].EvtDay + " " + phld1[i].EvtHour + ":" + phld1[i].EvtMin + ":" + phld1[i].EvtSec;
System.Console.Write("时间:" + time);
System.Console.WriteLine();
}
}
static void Main(string[] args)
{
...........//前面与设备的连接
lCardEvent myhuidiao = new CardEvent(themain);
ACSSetupCardEventCallback(myhuidiao);
}
运行后,返回的事件个数nEvtCnt 是正确的(有多少显示为多少),但返回结构体数组则只有第一条记录。不知道为什么?求高手解答,高分感谢啊。 展开
2个回答
展开全部
关键是 lpOnRecvCardEvent 这个函数的第二个参数是个 C++ 指针。
楼主在 C# 中声明这个导出函数时,使用了并不对应的 CardEvent[] C# 数组类型。
正确的做法是: 在 C# 声明 lpOnRecvCardEvent 时,第二个参数应该是 IntPtr ,不是数组。
由于楼主的这个 VC DLL 可能是自己写的,我没办法给出正确代码。
我提供一份模拟代码做参考:
----------------------------------------------------------------------------------------------------
VC DLL:
StructMod.h
#pragma once
#include <Windows.h>
#ifndef STRUCTMOD_EXPORT
#define STRUCTMOD_EXPORT extern "C" __declspec(dllimport)
#endif
typedef struct _tagCardEvent {
DWORD CardNo;
INT32 EvtYear;
} CardEvent, *PCardEvent;
STRUCTMOD_EXPORT CardEvent;
STRUCTMOD_EXPORT PCardEvent;
typedef void CALLBACK PFNTEST(PCardEvent pce, INT32 nEvt);
STRUCTMOD_EXPORT void Caller(PFNTEST pfnTest);
StructMod.cpp
#define STRUCTMOD_EXPORT extern "C" __declspec(dllexport)
#include "StructMod.h"
#include <tchar.h>
void Caller(PFNTEST pfnTest) {
CardEvent ce[2] = { { 2, 2011 }, { 3, 2005 } };
if (pfnTest) (*pfnTest)(ce, _countof(ce));
}
--------------------------------------------------------------------------------------------------------------------
C# 调用:
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace IntPtrTest
{
class Program
{
static void Main(string[] args)
{
TestD t = new TestD(Test);
Caller(t);
}
[StructLayout(LayoutKind.Sequential)]
public struct CardEvent
{
public uint CardNo;
public Int32 EvtYear;
}
delegate void TestD(IntPtr p, Int32 nEvt);
[DllImport("StructMod", EntryPoint = "Caller", CallingConvention = CallingConvention.Cdecl)]
private static extern void Caller(TestD t);
public static unsafe void Test(IntPtr p, Int32 nEvt)
{
Console.WriteLine("nEvt = " + nEvt.ToString() + Environment.NewLine);
for (int n = 0; n < nEvt; n++)
{
p = p + n * sizeof(CardEvent);
CardEvent ce = (CardEvent)Marshal.PtrToStructure(p, typeof(CardEvent));
Console.WriteLine("[{0}]", n);
Console.WriteLine("CardNo - " + ce.CardNo.ToString());
Console.WriteLine("EvtYear - " + ce.EvtYear.ToString());
}
}
}
}
楼主在 C# 中声明这个导出函数时,使用了并不对应的 CardEvent[] C# 数组类型。
正确的做法是: 在 C# 声明 lpOnRecvCardEvent 时,第二个参数应该是 IntPtr ,不是数组。
由于楼主的这个 VC DLL 可能是自己写的,我没办法给出正确代码。
我提供一份模拟代码做参考:
----------------------------------------------------------------------------------------------------
VC DLL:
StructMod.h
#pragma once
#include <Windows.h>
#ifndef STRUCTMOD_EXPORT
#define STRUCTMOD_EXPORT extern "C" __declspec(dllimport)
#endif
typedef struct _tagCardEvent {
DWORD CardNo;
INT32 EvtYear;
} CardEvent, *PCardEvent;
STRUCTMOD_EXPORT CardEvent;
STRUCTMOD_EXPORT PCardEvent;
typedef void CALLBACK PFNTEST(PCardEvent pce, INT32 nEvt);
STRUCTMOD_EXPORT void Caller(PFNTEST pfnTest);
StructMod.cpp
#define STRUCTMOD_EXPORT extern "C" __declspec(dllexport)
#include "StructMod.h"
#include <tchar.h>
void Caller(PFNTEST pfnTest) {
CardEvent ce[2] = { { 2, 2011 }, { 3, 2005 } };
if (pfnTest) (*pfnTest)(ce, _countof(ce));
}
--------------------------------------------------------------------------------------------------------------------
C# 调用:
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace IntPtrTest
{
class Program
{
static void Main(string[] args)
{
TestD t = new TestD(Test);
Caller(t);
}
[StructLayout(LayoutKind.Sequential)]
public struct CardEvent
{
public uint CardNo;
public Int32 EvtYear;
}
delegate void TestD(IntPtr p, Int32 nEvt);
[DllImport("StructMod", EntryPoint = "Caller", CallingConvention = CallingConvention.Cdecl)]
private static extern void Caller(TestD t);
public static unsafe void Test(IntPtr p, Int32 nEvt)
{
Console.WriteLine("nEvt = " + nEvt.ToString() + Environment.NewLine);
for (int n = 0; n < nEvt; n++)
{
p = p + n * sizeof(CardEvent);
CardEvent ce = (CardEvent)Marshal.PtrToStructure(p, typeof(CardEvent));
Console.WriteLine("[{0}]", n);
Console.WriteLine("CardNo - " + ce.CardNo.ToString());
Console.WriteLine("EvtYear - " + ce.EvtYear.ToString());
}
}
}
}
追问
感谢silvergingko!分给您了,谢谢。
我取3条记录 前面2条没问题,后面一条就乱了。就这个地方改了下
p =(IntPtr)( (int)p + n * sizeof(CardEvent));
不是到有问题么?
还有
我想再问下,如何释放掉那些内存呢? 我发现回调函数调用几次后内存就会加大?
追答
楼主说的这个 DLL 我不熟悉。里面的函数我也不了解。
一般情况下,一个 DLL 在一个函数的内部动态分配了内存的话,这个 DLL 会记录该内存的使用情况的。该 DLL 可以通过两种方式来实现内存的释放。
一、当该 DLL 在卸载时,DLL 自己释放。
二、另外再提供一个对应的函数,供客户程序调用来释放前面一个函数分配的内存。如 API : HeapAlloc,总会另外再提供 HeapFree 来让我们能自主释放内存。
楼主看下这个 DLL 的导出函数列表,里面是否有对应的内存释放函数,自行调用就可以了。还有就是内存的增长跟 DLL 没有关系,完全是代码有 BUG 所致。就如同楼主对于 DLL 提供的指针类型,楼主强行将它转成了 Array ,VC 的 struct 中没有 Length 这一数据成员,在 C# 中,强行使用了 Length 属性,结果是将排列整齐 CardEvent 数组(VC,不是C#数组)中任意的一个数据当做了 Length 属性,被 C# 读取出来。结果可想而知。
-----------------------------------------------------------------------------------------------
如果还不正确。楼主就要再研究下 DLL 这两个被调用的函数究竟是如何工作的。譬如传进来的参数究竟代表了什么,回调函数究竟该如何使用这些参数。C# 通不过的话,建议楼主还是用 VC 实现程序想要实现的功能。如果 VC 还是不行,那么就可以肯定要么是楼主对 DLL 导出函数理解不正确,产生了错误的编程逻辑;要么就是 DLL 自身有 BUG。这也是为什么我建议楼主先用 VC 的原因。先排除对 C# 语言不熟造成的编程干扰。只有确信 DLL 没有 BUG ,楼主的编程逻辑是正确的,然后再通过 C# 来实现对 DLL 的调用。
根据经验,一般内存不断增长,肯定是存在了 BUG 。内存的释放,我估计不是由于 DLL 函数引起的,而是楼主其他地方有问题。楼主阅读一下主调函数的使用文档,是否记载了该函数会动态分配内存,是的话,正规文档肯定会记载释放内存的方法说明。
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询