天天看點

從Delphi傳回動态數組到C#和C++

     经过很多尝试, 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:[email protected][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:[email protected][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);