DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: tkzcol
今日帖子: 4
在线用户: 2
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 11:14:15
标题:
TNetHTTPClient使用的一些代码写成DLL,在D2007里调用,遇到问题 浏览:1978
加入我的收藏
楼主: library NetHTTP;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  System.SysUtils,
  System.Net.URLClient,
  System.Net.HttpClient,
  System.Net.HttpClientComponent, System.StrUtils, System.NetEncoding,
  System.Classes;

{$R *.res}

function UrlDecode(const AStr: AnsiString): AnsiString;
var
  Sp, Rp, Cp: PAnsiChar;
  s: AnsiString;
begin
  SetLength(Result, Length(AStr));
  Sp := PAnsiChar(AStr);
  Rp := PAnsiChar(Result);
  Cp := Sp;
  while Sp^ <> #0 do
  begin
    case Sp^ of
      '+':
        Rp^ := ' ';
      '%':
        begin
          Inc(Sp);
          if Sp^ = '%' then
          Rp^ := '%'
          else
          begin
          Cp := Sp;
          Inc(Sp);
          if (Cp^ <> #0) and (Sp^ <> #0) then
          begin
          s := AnsiChar('$') + Cp^ + Sp^;
          Rp^ := AnsiChar(StrToInt(string(s)));
          end;
          end;
          Cp := Cp;
        end;
    else
      Rp^ := Sp^;
    end;
    Inc(Rp);
    Inc(Sp);
  end;
  SetLength(Result, Rp - PAnsiChar(Result));
end;

function NetHTTPClient(const aURL: PAnsiChar; UTF8: Boolean): PAnsiChar;
var
  vHttp: TNetHTTPClient;
  vUTF8, vGBK: TStringStream;
begin
  Result := '';
  vHttp := TNetHTTPClient.Create(nil);
  vUTF8 := TStringStream.Create('', TEncoding.GetEncoding(CP_UTF8));
  vGBK := TStringStream.Create('', TEncoding.GetEncoding(936));
  try
    with vHttp do
    begin
      if UTF8 then vUTF8.Clear else vGBK.Clear;
      ConnectionTimeout := 2000; // 2秒
      ResponseTimeout := 10000; // 10秒
      AcceptCharSet := IfThen(UTF8, 'utf-8', 'gbk');
      AcceptEncoding := IfThen(UTF8, '65001', '936');
      AcceptLanguage := 'zh-CN';
      ContentType := 'text/html';
      UserAgent := 'Embarcadero URI Client/1.0';
      try
        if UTF8 then
        begin
          //D2007传参数的话,这里aURL为nil,好像没有接受到值
          //改成固定的网址,D2007里调用能显示正确的响应字符
          Get('http://www.taobao.com', vUTF8);
          Result := PAnsiChar(AnsiString(TNetEncoding.URL.UrlDecode(vUTF8.DataString)));
        end
        else
        begin
          Get(string(AnsiString(aURL)), vGBK);
          Result := PAnsiChar(UrlDecode(AnsiString(vGBK.DataString)));
        end;

      except
        on E: Exception do
          Result := PAnsiChar(AnsiString(E.Message))
      end;
    end;
  finally
    vUTF8.Free;
    vGBK.Free;
    vHttp.Free;
  end;

end;


exports NetHTTPClient;
begin

end.


2007

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

function NetHTTPClient(const aURL: PAnsiChar; UTF8: Boolean): PAnsiChar; stdcall; external 'NetHTTP.dll';

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage( NetHTTPClient(PAnsiChar('http://www.taobao.com/'), True));
end;

end.


d2007里执行后,显示响应字符后,程序就失去响应。
调试环境下报:
----------
Debugger Fault Notification
----------
Project C:\Users\lenovo\Documents\Embarcadero\Studio\Projects\NetHTTPDLL\Win32\Debug\Project1.exe raised too many consecutive exceptions: 'access violation at 0x00000000: read of address 0x00000000'. Process Stopped. Use Step or Run to continue.
----------
OK   Help   
----------
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 11:34:37
1楼: 哪位朋友帮忙试试,看看是什么原因?谢谢。
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 internetzs (internetzs) ★☆☆☆☆ -
盒子活跃会员
2019/11/13 12:03:13
2楼: function NetHTTPClient(const aURL: PAnsiChar; UTF8: Boolean): PAnsiChar


Result := PAnsiChar(AnsiString(TNetEncoding.URL.UrlDecode(vUTF8.DataString)));

返回的字符串所用的内存是临时内存,返回后内存是不能访问的。

建议函数改为:
procedure NetHTTPClient(const aURL: PAnsiChar; UTF8: Boolean; outstr: PAnsiChar);
结果要内存复制StrCopy给outstr


调用前,分配内存给outstr
GetMem(outstr, 512);
----------------------------------------------
-
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 13:08:32
3楼: 楼上,暂时未测试你的建议。

但是,还有一个问题,为何 DLL里 不能正确的接受到 传的aURL参数呢?
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 13:58:02
4楼: 我用 显式调用的方法 可以了。
procedure TForm1.Button1Click(Sender: TObject);
var
  libH: THandle;
  F: function(const aURL: PAnsiChar; UTF8: Boolean): PAnsiChar;
begin
  libH := LoadLibrary('NETHTTP.DLL');
  try
    @F :=  GetProcAddress(libH, 'NetHTTPClient');
    Memo1.Text := F(PChar(Edit1.Text), True) ;
  finally
    FreeLibrary(libH)
  end;
end;
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 gmxyb (gmxyb) ★☆☆☆☆ -
普通会员
2019/11/13 14:59:45
5楼: 2楼说的是对的。。可是楼主听不进去。
----------------------------------------------
-
作者:
男 internetzs (internetzs) ★☆☆☆☆ -
盒子活跃会员
2019/11/13 15:32:48
6楼: function NetHTTPClient(const aURL: PAnsiChar; UTF8: Boolean): PAnsiChar; stdcall; external 'NetHTTP.dll';

DLL里 不能正确的接受到 传的aURL参数,是因为函数声明都要一致。上面多加了stdcall。

4楼的代码少了stdcall,可以用,但内存问题还是存在,只是有时正常,也有时会报非法指针使用,看运气了。
----------------------------------------------
-
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 15:59:03
7楼: 知道了,我再看看
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 chencong5025 (Nicosoft) ▲▲▲△△ -
普通会员
2019/11/13 20:13:48
8楼: @gmxyb  @internetzs
那么请问

使用function的正确方法是什么。既然返回值不能访问了。
----------------------------------------------
-
作者:
男 lsuper (lsuper) ★☆☆☆☆ -
盒子活跃会员
2019/11/13 20:48:52
9楼: 1、注意 unicode 版本 delphi 的 ansistring <> ansi 版本的 ansistring,不是简单的 sharemem 就 ok 的,底层内存块数据结构的不同
2、建议参考 com 的做法,公布 com 兼容的数据类型,如 widestring 这种操作系统管理、统一的数据类型:

function NetHTTPClient(const aURL: WideString; UTF8: Boolean): WideString;

注意二进制编码,或者 byte 的 olevariant
----------------------------------------------
-
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/13 22:08:46
8楼: 是 函数声明和定义 不一致的问题。调用的里面,去了 或者DLL里 加上 stdcall 都行。我当时声明时,有点随便加了一个stdcall,浪费大家时间了。

我4楼的代码是能运行,但不是调用DLL方式变化的原因,而是 定义局部过程变量F时正好和DLL的定义一样,没有多(stdcall)此一举。

PChar 可以用来传递和返回字符内容:
我以前也有一个类似的情况DLL,使用很久了,都是正常的。可能的原因是 双方都是delphi程序的原因,参考 delphi 2005从入门到精通 p368页。

DLL 里的英文,应该也是可以用PChar的意思吧。
To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters.
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 xlonger (xlonger) ★☆☆☆☆ -
普通会员
2019/11/14 7:42:11
10楼: 我楼上的回复要改一下。

DLL导出函数一般是要加个 调用约定的,普通的是stdcall。如果 EXE 和DLL 都是用delphi 编译的,那么双方都不加,也能运行。这个应该是 p368页的意思。

对于PChar传递字符串的问题,那个书本p371-372页,正好有例子。
学习如下:
1、虽然是 为了取得字符串结果的function,但是 返回值的类型是 LongBool,就是为调用程序提供一个判断值。

2、确实是通过 增加一个参数,专门传出 字符串的。

3、分配内存的问题。还在学习中,似乎有几种方法,特别是双方都是delphi的话。
----------------------------------------------
我打的是酱油,而不是别的什么油。
我灌的是口水,而不是别的什么水。
我聊的折腾不是那个不折腾的折腾。
我说的阿娇不是那个邓玉娇的阿娇。
3个代表,6个为什么,9个肠胃炎。
D性强的领导干部都不喜欢热比娅。
我特别要讲的是,屁民网黄色论坛是我经常上网必选的 网站之一
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2019/11/14 10:35:52
11楼: stdcall这些是函数参数调用约定,不是兰州所谓的多此一举。
delphi在默认情况下的约定是register
另外常见的还有pascal,cdecl和safecall。
其中c语言默认是cdecl。

主要区别就是参数传递的位置和顺序不一样
register和pascal:参数从左向右传递,也就是说最左边的参数最先求值并传入,最右边的参数最后求值和传入。cdecl,stdcall和safecall则按从右向左方向。
对于除cdecl之外的所有调用惯例,函数/过程在返回的时候要把堆栈中的参数退栈。对cdecl惯例,调用者在被调用的过程返回后执行参数退栈操作。
register调用惯例能用CPU寄存器来传递参数,而其它调用惯例只能通过堆栈来传递参数。
safecall调用惯例实现了异常的防火墙。在Windows上实现了跨进程的COM错误通知机制。
register调用效率最高,因为它避免了堆栈的创建。Delphi中published属性必须是register。
cdecl常用于调用C/C++编写的共享库中的函数;但是,如果要调用外部代码,那么一般要用stdcall和safecall。
在Windows上,系统的API都是stdcall和safecall;在其它操作系统上通常用cdecl,而stdcall效率一般都比cdecl高。
一般双接口中的方法都必须用safecall。
pascal只是为了兼容,另外export也只是在16位中使用,32、64位并不起作用,只作为兼容而保留。
----------------------------------------------
--
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/3/4 11:15:19
13楼: DLL 的接口里面,最好不要用 delphi 的 string 之类的东西。按照 DLL 的标准来。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行70.3125毫秒 RSS