您的位置:首页 > 编程语言 > Delphi

从Delphi返回动态数组到C#和C++

2017-05-20 15:55 471 查看
     经过很多尝试, Delphi/pascal无法将array of integer这样的数组返回给C#,只能传数组的地址过去, C#以IntPtr类型的参数接收, 然后通过Marshal将指针所指的内容复制出来 

    delphi的dll声明:

function dumpRegisters(out pvOut: PInteger; out pvSize: word): boolean; stdcall;export;
begin
sendDebug('dumpRegisters.');
result:=false;
if (gdm=nil) then begin
sendDebug('dumpRegisters, failed');
exit;
end;
pvOut:=@gdm.FRegisterValues[0];
pvSize:=length(gdm.FRegisterValues);
result:=true;
sendDebugFmt('dumpRegisters, OK, out:%p, size:%d',[pvOut,pvSize]);
end;


C#的import声明:

public static extern void setRoutines(IntPtr pvRoutines);
[DllImport("modbusapi.dll", EntryPoint = "dumpRegisters", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern byte dumpRegisters(out IntPtr pvAddr,
out UInt16 pvSize);
然后通过这个函数转换:

public static int[] IntPtr2RegisterData(IntPtr pvAddr,ushort pvSize)
{
int[] lvRegisters = new int[pvSize];
Marshal.Copy(pvAddr, lvRegisters,0, pvSize);
return lvRegisters;
}
例子:

IntPtr lvPtr;
ushort lvSize;
int[] lvRet = null;
if (ModBus.dumpRegisters(out lvPtr, out lvSize) != 0)
lvRet = ModBus.IntPtr2RegisterData(lvPtr, lvSize);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lvSize; i++)
{
sb.Append(String.Format("Register:{0}, value: {1}\r\n", i, lvRet[i]));
}
textBox1.AppendText(sb.ToString());
}


====================

而用VC调用,则可以做到无缝连接,直接引用指针:

定义function指针:

typedef bool(__stdcall* _dumpRegisters)(PINT&, WORD&);

bool ModBus::readRegisters(PINT& pvOut, WORD& pvSize) {
if (mHnd == 0) return false;
//_dumpRegisters dumpRegisters = (_dumpRegisters)GetProcAddress(mHnd, "dumpRegisters");
_dumpRegisters dumpRegisters = (_dumpRegisters)GetProcAddress(mHnd, "dumpRegisters");
WORD lvSize = 0;
PINT lvAddr = NULL;
if (dumpRegisters(lvAddr, lvSize)) {
pvOut = lvAddr;
pvSize = lvSize;
}
}
调用:

PINT lvRegisters = NULL;
WORD lvSize = 0;
char lvOut[1024*10];
int lvLen = 0;
if (modbus.readRegisters(lvRegisters,  lvSize)) {
for (int i = 0; i < lvSize; i++) {
try {
lvLen += sprintf_s(lvOut + lvLen, 1024 * 10 - lvLen, "Register:%d, Value:%d \r\n", i, lvRegisters[i]);
}
catch (const std::exception& e)
{
}

}
CString lvT(lvOut);
ed_log.SetWindowTextW(lvT);
}


上述方法应该于内存在dll里面管理的情况, 如果内存块在主程序里面先申请的,调用如下 :

dll 代码:

function dumpRegisters(pvOut: PInteger; out pvSize: word): boolean; stdcall;export;
begin
sendDebug('dumpRegisters.');
result:=false;
if (gdm=nil) then begin
sendDebug('dumpRegisters, failed');
exit;
end;
//pvOut:=@gdm.FRegisterValues[0];
pvSize:=length(gdm.FRegisterValues);
sendDebugFmt('dumpRegisters, source addr:%p, target addr:%p,size:%d',[@gdm.FRegisterValues[0],pvOut,pvSize]);
sendDebugFmt('before size:%d',[pvSize]);
move(gdm.FRegisterValues[0],pvOut^,sizeof(Integer)*pvSize); //复制数据到主程序的内存pvOut
sendDebugFmt('after size:%d',[pvSize]);
result:=true;
sendDebugFmt('dumpRegisters, OK, out:%p, size:%d',[pvOut,pvSize]);
end;


VC的调用:
typedef bool(__stdcall* _dumpRegisters)(PINT, WORD&);
_dumpRegisters dumpRegisters = (_dumpRegisters)GetProcAddress(mHnd, "dumpRegisters");
PINT lvAddr = NULL;
mRegisterSize = mRoutines->getRegisterSize();
PINT mRegisters = new int[mRegisterSize];
dumpRegisters(mRegisters, pvSize); .....delete [] mRegisters;


C#的调用:

[DllImport("modbusapi.dll", EntryPoint = "dumpRegisters", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern byte dumpRegisters([In, Out]
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] pvAddr,
out UInt16 pvSize);

UInt16 lvSize = routines.getResiterSize();
int[] registers = new int[lvSize];
dumpRegisters(registers, out lvSize);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ delphi c#