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告诉DLL
returnData
最多可以容纳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是一个很大的禁忌。即使如此,让用户使用