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

2017-05-29 19:58:09来源:CSDN作者:rocklee人点击

     经过很多尝试, 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);				


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台