Delphi p> 说了这么多,试着做些类似的事情: library TestDLL; uses SysUtils, Classes, Windows, Messages, Vcl.Dialogs, IdIOHandlerStack, IdSSLOpenSSL, IdHTTP, IdCompressorZLib; {$R *.res} function PostAdminDataViaDll(body, method, url: PChar; var OutData : PChar): integer; stdcall; var HTTPReq : TIdHTTP; SendStream : TStringStream; IdSSLIOHandler : TIdSSLIOHandlerSocketOpenSSL; s : string; begin OutData := nil; try HTTPReq := TIdHTTP.Create(nil); try IdSSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(HTTPReq); IdSSLIOHandler.SSLOptions.Mode := sslmClient; IdSSLIOHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; HTTPReq.IOHandler := IdSSLIOHandler; HTTPReq.Compressor := TIdCompressorZLib.Create(HTTPReq); HTTPReq.ReadTimeout := 180000;//set read timeout to 3 minutes HTTPReq.HTTPOptions := []; HTTPReq.Request.ContentType := 'text/xml'; HTTPReq.Request.Charset := 'UTF-8'; HTTPReq.Request.Accept := 'text/xml'; HTTPReq.Request.CustomHeaders.AddValue('SOAPAction', 'http://tempuri.org/Administration/' + method); SendStream := TStringStream.Create(Body, TEncoding.UTF8); try s := HTTPReq.Post(string(url) + 'admin.asmx', SendStream); finally SendStream.Free; end; Result := Length(s); if Result > 0 then begin GetMem(OutData, (Result + 1) * Sizeof(Char)); Move(PChar(s)^, OutData^, (Result + 1) * Sizeof(Char)); end; finally HTTPReq.Free; end; except on E: Exception do begin ShowMessage(E.Message); Result := -1; end; end; end; function FreeDataViaDll(Data : Pointer): integer; stdcall; begin try FreeMem(Data); Result := 0; except on E: Exception do begin ShowMessage(E.Message); Result := -1; end; end; end; exports PostAdminDataToCenPosViaDll, FreeDataViaDll; begin end.
Delphi p> 说了这么多,试着做些类似的事情: library TestDLL; uses SysUtils, Classes, Windows, Messages, Vcl.Dialogs, IdIOHandlerStack, IdSSLOpenSSL, IdHTTP, IdCompressorZLib; {$R *.res} function PostAdminDataViaDll(body, method, url: PChar; var OutData : PChar): integer; stdcall; var HTTPReq : TIdHTTP; SendStream : TStringStream; IdSSLIOHandler : TIdSSLIOHandlerSocketOpenSSL; s : string; begin OutData := nil; try HTTPReq := TIdHTTP.Create(nil); try IdSSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(HTTPReq); IdSSLIOHandler.SSLOptions.Mode := sslmClient; IdSSLIOHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; HTTPReq.IOHandler := IdSSLIOHandler; HTTPReq.Compressor := TIdCompressorZLib.Create(HTTPReq); HTTPReq.ReadTimeout := 180000;//set read timeout to 3 minutes HTTPReq.HTTPOptions := []; HTTPReq.Request.ContentType := 'text/xml'; HTTPReq.Request.Charset := 'UTF-8'; HTTPReq.Request.Accept := 'text/xml'; HTTPReq.Request.CustomHeaders.AddValue('SOAPAction', 'http://tempuri.org/Administration/' + method); SendStream := TStringStream.Create(Body, TEncoding.UTF8); try s := HTTPReq.Post(string(url) + 'admin.asmx', SendStream); finally SendStream.Free; end; Result := Length(s); if Result > 0 then begin GetMem(OutData, (Result + 1) * Sizeof(Char)); Move(PChar(s)^, OutData^, (Result + 1) * Sizeof(Char)); end; finally HTTPReq.Free; end; except on E: Exception do begin ShowMessage(E.Message); Result := -1; end; end; end; function FreeDataViaDll(Data : Pointer): integer; stdcall; begin try FreeMem(Data); Result := 0; except on E: Exception do begin ShowMessage(E.Message); Result := -1; end; end; end; exports PostAdminDataToCenPosViaDll, FreeDataViaDll; begin end.,delphi,delphi-xe2,delphi-10.2-tokyo,Delphi,Delphi Xe2,Delphi 10.2 Tokyo,函数PostAdminData(body,method,url:string):IXMLDOMDocument; 类型 TMyPost=函数(主体、方法、url:PChar;var-OutData:PChar):整数;stdcall; TMyFree=函数(数据指针):整数;stdcall; 变量 hDll:THandle; MyPost:TMyPost; MyFree:TMyFree; 数据字符串:字符串; 返回数据:PChar; returnLen:整数; 开始 hDll:=LoadLibr
函数PostAdminData(body,method,url:string):IXMLDOMDocument;
类型
TMyPost=函数(主体、方法、url:PChar;var-OutData:PChar):整数;stdcall;
TMyFree=函数(数据指针):整数;stdcall;
变量
hDll:THandle;
MyPost:TMyPost;
MyFree:TMyFree;
数据字符串:字符串;
返回数据:PChar;
returnLen:整数;
开始
hDll:=LoadLibrary(PChar(ExtractFilePath(Application.ExeName)+'TestDLL.DLL');
如果hDll=0,则开始
Application.MessageBox('无法加载TestDLL.DLL','错误发布',MB\u ICONROR或MB\u OK);
出口
结束;
尝试
尝试
MyPost:=GetProcAddress(hDll,“PostAdminDataDavill”);
MyFree:=GetProcAddress(hDll,'FreeDataViaDll');
如果已分配(MyPost)和已分配(MyFree),则开始
returnLen:=MyPost(PChar(body)、PChar(method)、PChar(url)、returnData);
如果returnLen>0,则开始
尝试
设置字符串(数据字符串、返回数据、返回长度);
最后
MyFree(返回数据);
结束;
结束;
结束;
最后
免费图书馆(hDll);
结束;
除了
结束;
如果是数据字符串“”,则开始
尝试
结果:=CreateOleObject('Microsoft.XMLDOM')作为IXMLDOMDocument;
Result.async:=False;
loadXML(数据字符串);
除了
结束;
结束;
结束;
我有一个将XML数据发布到站点的DLL。DLL是用Delphi 10构建的,目的是使用TLS 1.2,这在Delphi XE2中不可用
为什么不简单地将XE2中的Indy更新为支持TLS 1.2的更新版本?那么您根本不需要DLL
我最初的DLL约定基本上是不正确的,因为我以PChar
的形式返回了返回的文本数据的内容。在我这里和其他地方的阅读资料中,这是一个很大的禁忌
这不是一个“大禁忌”,特别是如果响应数据本质上是动态的。返回指向动态分配的数据的指针是非常好的。当调用者使用完数据时,您只需导出一个额外的函数来释放数据,仅此而已。“大禁忌”是,如果调用者忘记调用第二个函数,这确实会导致潜在的内存泄漏。但这正是尝试的目的。最后,
是有好处的
在我开始返回大量数据之前,这种“糟糕”的方法一直运作良好。我对它进行了测试,但超过132365个字符的测试都失败了
那不是很多记忆。你在这方面的任何失败都很可能是因为你只是误用了记忆
我重新构造了我的DLL,并调用代码将缓冲区作为PChar
进行填充,但尝试填充输出值时出错
这是因为您没有正确填充内存
其次,由于我不知道返回的数据有多大,如何从调用方法中指定要填充的缓冲区有多大
当使用POST
时,您不能这样做。您必须将响应数据缓存在一旁的某个位置,然后公开让调用方查询该缓存的大小和数据的方法
获取错误的DLL代码:
library TestDLL;
uses
SysUtils,
Classes,
Windows,
Messages,
vcl.Dialogs,
IdSSLOpenSSL, IdHTTP, IdIOHandlerStack, IdURI,
IdCompressorZLib;
{$R *.res}
function PostAdminDataViaDll(body, method, url: PChar; OutData : PChar; OutLen : integer): integer; stdcall
var HTTPReq : TIdHTTP;
var Response: TStringStream;
var SendStream : TStringStream;
var IdSSLIOHandler : TIdSSLIOHandlerSocketOpenSSL;
var Uri : TIdURI;
var s : string;
begin
Result := -1;
try
HTTPReq := TIdHTTP.Create(nil);
IdSSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
IdSSLIOHandler.SSLOptions.Mode := sslmClient;
IdSSLIOHandler.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1];
if Assigned(HTTPReq) then begin
HTTPReq.Compressor := TIdCompressorZLib.Create(HTTPReq);
HTTPReq.IOHandler := IdSSLIOHandler;
HTTPReq.ReadTimeout := 180000;//set read timeout to 3 minutes
HTTPReq.Request.ContentType := 'text/xml;charset=UTF-8';
HTTPReq.Request.Accept := 'text/xml';
HTTPReq.Request.CustomHeaders.AddValue('SOAPAction', 'http://tempuri.org/Administration/' + method);
HTTPReq.HTTPOptions := [];
end;
SendStream := TStringStream.Create(Body);
Response := TStringStream.Create(EmptyStr);
try
HTTPReq.Request.ContentLength := Length(Body);
Uri := TiDUri.Create(url);
try
HTTPReq.Request.Host := Uri.Host;
finally
Uri.Free;
end;
HTTPReq.Post(url + 'admin.asmx', SendStream,Response);
if Response.Size > 0 then begin
if assigned(OutData) then begin
s := Response.DataString;// Redundant? Probably can just use Response.DataString?
StrPLCopy(OutData, s, OutLen);// <- ACCESS VIOLATION HERE
//StrPLCopy(OutData, s, Response.Size);// <- ACCESS VIOLATION HERE
Result := 0;
end;
end
else begin
Result := -2;
end;
finally
Response.Free;
SendStream.Free;
IdSSLIOHandler.Free;
HTTPReq.Free;
end;
except
on E:Exception do begin
ShowMessage(E.Message);
Result := 1;
end;
end;
end;
exports
PostAdminDataViaDll;
begin
end.
我的调用方法代码:
function PostAdminData(body, method, url : string): IXMLDOMDocument;
type
TMyPost = function (body, method, url: PChar; OutData : PChar; OutLen : integer): integer; stdcall;
var Handle : THandle;
var MyPost : TMyPost;
var dataString : string;
var returnData : string;
begin
if not (FileExists(ExtractFilePath(Application.ExeName) + 'TestDLL.DLL')) then begin
Application.MessageBox(pchar('Unable to find TestDLL.DLL.'), pchar('Error posting'),MB_ICONERROR + MB_OK);
Exit;
end;
dataString := EmptyStr;
returnData := '';
Handle := LoadLibrary(PChar(ExtractFilePath(Application.ExeName) + 'TestDLL.DLL'));
if Handle <> 0 then begin
try
try
MyPost := GetProcAddress(Handle, 'PostAdminDataViaDll');
if @MyPost <> nil then begin
// NOTE 32767 is not big enough for the returned data! Help!
if MyPost(PChar(body), PChar(method), PChar(url), PChar(returnData), 32767) = 0 then begin
dataString := returnData;
end;
end;
except
end;
finally
FreeLibrary(Handle);
end;
end
else begin
Application.MessageBox(pchar('Unable to find TestDLL.DLL.'), pchar('Error posting'),MB_ICONERROR + MB_OK);
end;
if not sametext(dataString, EmptyStr) then begin
try
Result := CreateOleObject('Microsoft.XMLDOM') as IXMLDOMDocument;
Result.async := False;
Result.loadXML(dataString);
except
end;
end;
end;
我在代码中看到了一些逻辑错误
但是,访问冲突错误的最重要原因是EXE没有为其returnData
变量分配任何内存
将字符串
强制转换为PChar
不会生成nil
指针。如果输入的字符串
不是空的,则返回指向该字符串的第一个字符
的指针。否则,将返回指向静态字符的指针。这可确保强制转换为PChar
的字符串始终生成非nil、以null结尾的C样式字符串
您的EXE告诉DLLreturnData
最多可以容纳32767个字符,但实际上它根本不能容纳任何字符!在DLL中,OutData
不是nil
,而OutLen
是错误的
另外,StrPLCopy()
总是null终止输出,但是MaxLen
参数不包括null终止符,因此调用者必须为MaxLen+1
字符分配空间。这一点在报告中有所说明
说了这么多,试着做些类似的事情:
library TestDLL;
uses
SysUtils,
Classes,
Windows,
Messages,
Vcl.Dialogs,
IdIOHandlerStack, IdSSLOpenSSL, IdHTTP, IdCompressorZLib;
{$R *.res}
function PostAdminDataViaDll(body, method, url: PChar;
var OutData : PChar): integer; stdcall;
var
HTTPReq : TIdHTTP;
SendStream : TStringStream;
IdSSLIOHandler : TIdSSLIOHandlerSocketOpenSSL;
s : string;
begin
OutData := nil;
try
HTTPReq := TIdHTTP.Create(nil);
try
IdSSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(HTTPReq);
IdSSLIOHandler.SSLOptions.Mode := sslmClient;
IdSSLIOHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
HTTPReq.IOHandler := IdSSLIOHandler;
HTTPReq.Compressor := TIdCompressorZLib.Create(HTTPReq);
HTTPReq.ReadTimeout := 180000;//set read timeout to 3 minutes
HTTPReq.HTTPOptions := [];
HTTPReq.Request.ContentType := 'text/xml';
HTTPReq.Request.Charset := 'UTF-8';
HTTPReq.Request.Accept := 'text/xml';
HTTPReq.Request.CustomHeaders.AddValue('SOAPAction', 'http://tempuri.org/Administration/' + method);
SendStream := TStringStream.Create(Body, TEncoding.UTF8);
try
s := HTTPReq.Post(string(url) + 'admin.asmx', SendStream);
finally
SendStream.Free;
end;
Result := Length(s);
if Result > 0 then begin
GetMem(OutData, (Result + 1) * Sizeof(Char));
Move(PChar(s)^, OutData^, (Result + 1) * Sizeof(Char));
end;
finally
HTTPReq.Free;
end;
except
on E: Exception do begin
ShowMessage(E.Message);
Result := -1;
end;
end;
end;
function FreeDataViaDll(Data : Pointer): integer; stdcall;
begin
try
FreeMem(Data);
Result := 0;
except
on E: Exception do begin
ShowMessage(E.Message);
Result := -1;
end;
end;
end;
exports
PostAdminDataToCenPosViaDll,
FreeDataViaDll;
begin
end.
函数PostAdminData(body,method,url:string):IXMLDOMDocument;
类型
TMyPost=函数(主体、方法、url:PChar;var-OutData:PChar):整数;stdcall;
TMyFree=函数(数据指针):整数;stdcall;
变量
hDll:THandle;
MyPost:TMyPost;
MyFree:TMyFree;
数据字符串:字符串;
返回数据:PChar;
returnLen:整数;
开始
hDll:=LoadLibrary(PChar(ExtractFilePath(Application.ExeName)+'TestDLL.DLL');
如果hDll=0,则开始
Application.MessageBox('无法加载TestDLL.DLL','错误发布',MB\u ICONROR或MB\u OK);
出口
结束;
尝试
尝试
MyPost:=GetProcAddress(hDll,“PostAdminDataDavill”);
MyFree:=GetProcAddress(hDll,'FreeDataViaDll');
如果已分配(MyPost)和已分配(MyFree),则开始
returnLen:=MyPost(PChar(body)、PChar(method)、PChar(url)、returnData);
如果returnLen>0,则开始
尝试
设置字符串(数据字符串、返回数据、返回长度);
最后
MyFree(返回数据);
结束;
结束;
结束;
最后
免费图书馆(hDll);
结束;
除了
结束;
如果是数据字符串“”,则开始
尝试
结果:=CreateOleObject('Microsoft.XMLDOM')作为IXMLDOMDocument;
Result.async:=False;
loadXML(数据字符串);
除了
结束;
结束;
结束;
是的,除非DLL还提供了再次删除缓冲区的方法,否则返回DLL分配的数据作为PChar是一个很大的禁忌。即使如此,让用户使用