使用字节参数数组从Delphi调用C#DLL
我将dll称为“plcommpro.dll”,用于访问控制的特定操作 下面的C#代码工作正常,可以在缓冲区中正确检索数据使用字节参数数组从Delphi调用C#DLL,c#,delphi,C#,Delphi,我将dll称为“plcommpro.dll”,用于访问控制的特定操作 下面的C#代码工作正常,可以在缓冲区中正确检索数据 [DllImport("plcommpro.dll", EntryPoint = "GetDeviceData")] public static extern int GetDeviceData(IntPtr h, ref byte buffer, int buffersize, string tablename, string filename, string filter
[DllImport("plcommpro.dll", EntryPoint = "GetDeviceData")]
public static extern int GetDeviceData(IntPtr h, ref byte buffer,
int buffersize, string tablename, string filename, string filter, string
options);
现在,我需要从Delphi编写相同的操作,因此我尝试了以下操作:
TGetDeviceData = Function(iDevID : NativeInt; buffer : Pbyte ; iSize :
Integer;
tablename, filename, strFilter, strOptions : PAnsiChar) : Int64;stdcall;
我调用函数如下:
var
myBuffer : TBytes;
iRetLog : Integer;
bufferSize : Integer;
sConnect : TConnect;
GetDeviceData : TGetDeviceData;
dllHandle : THandle;
iDevID : Integer;
begin
dllHandle := LoadLibrary('plcommpro.dll') ;
if dllHandle <> 0 then
begin
@sConnect := GetProcAddress(dllHandle, 'Connect');
if @sConnect <> Nil then
begin
strParams := PChar('protocol=TCP,ipaddress=' + grd_Machines.Cells[cl_Machine_IP, iLoop] + ',port=4370,timeout=2000,passwd=');
iDevID := sConnect(strParams);
strTableName := PAnsiChar(AnsiString(('user')));
strDatas := PAnsiChar(AnsiString(''));
strFileName := PAnsiChar(AnsiString(''));
strFilter := PAnsiChar(AnsiString(''));
strOptions := PAnsiChar(AnsiString(''));
@GetDeviceData := GetProcAddress(dllHandle, 'GetDeviceData');
if @GetDeviceData <> Nil then
begin
try
buffersize := 1024*1024;
//bufferSize := MaxInt - 1;
SetLength(myBuffer, 1024*1024);
mem_AttLogs.Lines.Add('buffer Size : ' + IntToStr(buffersize) );
iRetLogs := GetDeviceData(iDevID, PByte(myBuffer[0]), buffersize, strTableName, strFileName, strFilter, strOptions);
if iRetLogs > 0 then
begin
....
//Here: I need to read the returned values from the function; but it always fails
end
var
myBuffer:TBytes;
iRetLog:整数;
缓冲区大小:整数;
sConnect:t连接;
GetDeviceData:TGetDeviceData;
德尔汉德尔:坦德尔;
iDevID:整数;
开始
dllHandle:=LoadLibrary('plcommpro.dll');
如果dllHandle为0,则
开始
@sConnect:=GetProcAddress(dllHandle,'Connect');
如果@sConnect Nil,则
开始
strParams:=PChar('protocol=TCP,ipaddress='+grd_Machines.Cells[cl_Machine_IP,iLoop]+',port=4370,timeout=2000,passwd=');
iDevID:=连接(strParams);
strTableName:=PAnsiChar(AnsiString(('user'));
标准数据:=PAnsiChar(AnsiString(“”));
strFileName:=PAnsiChar(AnsiString(“”));
strFilter:=PAnsiChar(AnsiString(“”));
选择:=PAnsiChar(AnsiString(“”));
@GetDeviceData:=GetProcAddress(dllHandle,'GetDeviceData');
如果@GetDeviceData为零,则
开始
尝试
缓冲区大小:=1024*1024;
//bufferSize:=MaxInt-1;
设置长度(myBuffer,1024*1024);
mem_AttLogs.Lines.Add('buffer Size:'+IntToStr(buffersize));
iRetLogs:=GetDeviceData(iDevID,PByte(myBuffer[0]),buffersize,strTableName,strFileName,strFilter,strOptions);
如果iRetLogs>0,则
开始
....
//这里:我需要读取函数返回的值;但它总是失败
结束
代码被修改以更清楚地解释我的情况。你能帮忙吗?你给出的C#声明显然有缺陷:ref byte buffer
毫无意义。缓冲区不是一个字节。它可能类似于[out]byte[]buffer
(感谢David Heffernan)。两者都转换一个底层指针,但在C#端完成的转换是不同的
由于这似乎是一个与普通Windows DLL接口的互操作代码,我可以看到原始代码是什么:一个指向字节的指针,在Delphi中最好翻译为PByte
(但没有var
,这将引入一个太多的间接级别)
所以现在应该是这样的:
var
GetDeviceData: function(h: THandle; buffer: PByte; buffersize: Integer;
tablename, filename, filter, options: PAnsiChar): Integer stdcall;
现在您终于更新了代码,得到的错误非常明显:
iRetLog := GetDeviceData(iDevID, PByte(myBuffer[0]), buffersize,
strTableName, strFileName, strFilter, strOptions);
这是错误的。您正在将一个AnsiChar
(即myBuffer[0]
)强制转换为指针。您需要指向myBuffer
的第一个元素的指针,因此:
iRetLog := GetDeviceData(iDevID, @myBuffer[0], buffersize,
strTableName, strFileName, strFilter, strOptions); // Note the @
FWIW,因为您似乎在使用常量字符串,所以您可以执行以下操作:
iRetLog := GetDeviceData(iDevID, @myBuffer[0], buffersize, 'user', '', '', '');
为了便于维护,如果已经有一个变量具有此数字(而且必须大小完全相同),请不要使用文字数字,因此请执行以下操作:
buffersize := 1024*1024;
SetLength(myBuffer, buffersize);
var byte:PByte
错误(一级间接寻址太多)。首先,Byte
是一种现有类型,虽然这可能是合法的,但我认为您应该将其命名为buffer
之类的C#assembly。但更重要的是,您应该去掉var
。所以,让它成为:缓冲区:PByte
。FWIW,ansisting
也错了。让它成为PAnsiChar
。您能告诉我们如何访问您的字节吗?这是一个指向字节的指针,它通常引用字节数组,但您似乎引用的是单个字节。向我们展示用于尝试获取“byte”的代码将有所帮助。还要注意,在Delphi中,byte是一种数据类型,所以不同的名称可能会有所帮助。FWIW,对吗?这看起来好像是从非.NET DLL导入C#,因此使用了DllImport
属性。如果是这种情况,您应该直接使用plcommpro.dll
。也许它甚至有一个C头。是的,我正在直接使用“plcommpro.dll”。问题是,我无法从我的Delphi应用程序中获取数据,但我可以从C#App中获取数据。你有用于DLL的C头吗?如果是,则显示同一函数的C声明。而PByte
实际上不应该是var
参数。也不应将字符串转换为AnsiString
(一种特定于Delphi的类型,在C或C#中不存在)。它应该是PAnsiChar
,这是C语言中char*
的翻译。NET编组代码将char*
转换为C字符串,但在Delphi中不起作用。看一看关于使用DLLs.FWIW的内容,从我在网上找到的信息来看,GetDeviceData
返回的是一个整数,而不是Int64
。最好将第一个参数声明为THandle
。我假设这不是您真正的代码。您可以声明iRetLog
,但稍后将使用iRetLogs
。不要那样做。这会使您的代码难以理解,并引入不必要的错误。下次发布问题时,请提供更多、格式更好的代码。如果可能的话,还有真正的代码,或者。如果您从一开始就这样做了,您会更早得到答案。ref byte buffer
只能映射到var buffer:byte
,这显然毫无意义。但提问者接受了与c不匹配的答案。整个事情对于这个网站来说是一团糟,完全不合适。@大卫:是的,ref byte buffer
只映射到var buffer:byte
。在C#端,这只是一个糟糕的翻译(在现实生活中,指针被传递也是有效的,但无法使用)。我看过其他的翻译,一个字节的缓冲区是毫无意义的,我打赌buffer:PByte
可以工作。