2Pascal-新时代的Pascal

 找回密码
 立即注册
搜索
热搜: fastreport
查看: 1128|回复: 0
打印 上一主题 下一主题

关于 DLL 和 DLL 字符串的传递 V2015-08-29

[复制链接]

90

主题

293

帖子

8万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
81976
跳转到指定楼层
楼主
发表于 2015-8-28 17:23:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
京东购书支持本站
(* ************************************************ *)
(*                         *)
(*                         *)
(*  编写:爱吃猪头肉 & Flying Wang 2015-08-28   *)
(*      上面的版权声明请不要移除。      *)
(*                         *)
(* ************************************************ *)

本人所在的群(① FireMonkey[移动开发]  165232328)


1. 使用 PAnsiChar PWideChar。跨平台使用 PByte。
2. 不要用 Char PChar,否则死了活该。
3. 函数返回值不要是 P任何Char。否则死了活该。
4. 任何内存,任何对象。都是谁创建,谁销毁。你弄错了。死了活该。
5. 需要返回的 string 用 var (不是必须的),而且需要带一个 字符串长度的整数参数。
6. 假设 DLL 的某接口如下
dlltestfun(InString: PAnsiChar; var OutString: PAnsiChar; var OutStrLength: Integer): Integer; stdcall;
6.1 内部代码建议如下
var
  sInString: string;
  sOutString: string;
  CharCount, ByteCount,
  sOutStrLen: Integer;
begin
  SetString(sInString, InString, StrLen(InString)); //输入的字符串,必须这样获取。
  SetString(sOutString, OutString, StrLen(OutString));
  //你的一些代码之后。
  sOutString := '';
  sOutString := '你要输出的字符串';
  if (OutString = nil) then
  begin
    //没有办法输出字符串,因为调用者没有初始化。
    exit;
  end;
  CharCount := Length(sOutString);
  if CharCount > OutStrLength then
  begin
    OutStrLength := CharCount;
    //靠,你的保存区空间不够。
    exit;
  end;
  ByteCount := Min(OutStrLength, CharCount) * Sizeof(AnsiChar);
  if(OutString <> nil) then
  begin
    CopyMemory(OutString, PAnsiChar(AnsiChar(sOutString)), ByteCount);
    //总算是返回了。
  end;
end;
6.2 调用者代码如下
var
  sInString: string;
  sOutString: string;
  InString: PAnsiChar;
  OutString: PAnsiChar;
  OutStrLength: Integer;
begin
  sInString := '你需要输入的字符串';
  sOutString := '';

  InString := PAnsiChar(AnsiString(sInString));
  OutStrLength := 200; 不够你自己加。
  OutString := GetMem(OutStrLength);
  FillMemory(OutString, OutStrLength, 0);
  dlltestfun(InString, OutString, OutStrLength);
  SetString(sOutString, OutString, Min(StrLen(OutString),OutStrLength));
  //总算是得到返回字符串了。
end;
6.3 以上是 ansi 版本的代码。
6.4 如果你希望支持多国语言,请使用 WideChar


7 以下是使用 TEncoding 和 TBytes 的新版本写法。以及 WideChar 版本。
7.1 dll 代码
function dlltestfunA(InString: PAnsiChar; var OutString: PAnsiChar; var OutStrLength: Integer): Integer; stdcall;
var
  sInString: string;
  sOutString: string;
  CharCount, ByteCount,
  sOutStrLen: Integer;
  OutBytes: TBytes;
begin
  Result := 0;
  SetString(sInString, InString, StrLen(InString)); //输入的字符串,必须这样获取。
  SetString(sOutString, OutString, StrLen(OutString));
  //你的一些代码之后。
  sOutString := '';
  sOutString := '你要输出的字符串';
  if (OutString = nil) then
  begin
    Result := -1;
    //没有办法输出字符串,因为调用者没有初始化。
    exit;
  end;
  OutBytes := TEncoding.ANSI.GetBytes(sOutString + #0);  // + #0 这样可以比较安全的传输字符串。
  ByteCount := Length(OutBytes);
  // #0 不算。
  CharCount := ByteCount div Sizeof(AnsiChar) - 1;
  if CharCount > OutStrLength then
  begin
    Result := -2;
    OutStrLength := CharCount;
    //靠,你的保存区空间不够。
    exit;
  end;
  if (OutString <> nil) then
  begin
    Result := ByteCount;
    Move(OutBytes[0], OutString[0], ByteCount);
    //总算是返回了。
  end;
end;

function dlltestfunW(InString: PWideChar; var OutString: PWideChar; var OutStrLength: Integer): Integer; stdcall;
var
  sInString: string;
  sOutString: string;
  CharCount, ByteCount,
  sOutStrLen: Integer;
  OutBytes: TBytes;
begin
  Result := 0;
  SetString(sInString, InString, StrLen(InString)); //输入的字符串,必须这样获取。
  SetString(sOutString, OutString, StrLen(OutString));
  //你的一些代码之后。
  sOutString := '';
  sOutString := '你要输出的字符串';
  if (OutString = nil) then
  begin
    Result := -1;
    //没有办法输出字符串,因为调用者没有初始化。
    exit;
  end;
  OutBytes := TEncoding.Unicode.GetBytes(sOutString + #0);  // + #0 这样可以比较安全的传输字符串。
  ByteCount := Length(OutBytes);
  // #0 不算。
  CharCount := ByteCount div Sizeof(WideChar) - 1;
  if CharCount > OutStrLength then
  begin
    Result := -2;
    OutStrLength := CharCount;
    //靠,你的保存区空间不够。
    exit;
  end;
  if (OutString <> nil) then
  begin
    Result := ByteCount;
    Move(OutBytes[0], OutString[0], ByteCount);
    //总算是返回了。
  end;
end;


7.2 调用代码
procedure TForm1.Button1Click(Sender: TObject);
var
  sInString: string;
  sOutString: string;
  InBytes: TBytes;
  OutBytes: TBytes;
  OutStrLength: Integer;
  InString: PAnsiChar;
  OutString: PAnsiChar;
begin
  sInString := '你需要输入的字符串';
  sOutString := '';

  InBytes := TEncoding.ANSI.GetBytes(sInString + #0);  // + #0  这样安全,也是各种语言要求的。
  OutStrLength := 200; //不够你自己加。
  SetLength(OutBytes, OutStrLength * Sizeof(AnsiChar));
  FillChar(OutBytes[0], OutStrLength * Sizeof(AnsiChar), 0);
  InString := Addr(InBytes[0]);
  OutString := Addr(OutBytes[0]);
  dlltestfunA(InString, OutString, OutStrLength);
  SetString(sOutString, OutString, Min(StrLen(OutString),OutStrLength)); //输出的字符串,必须这样获取。
  //总算是得到返回字符串了。
  ShowMessage(sOutString);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  sInString: string;
  sOutString: string;
  InBytes: TBytes;
  OutBytes: TBytes;
  OutStrLength: Integer;
  InString: PWideChar;
  OutString: PWideChar;
begin
  sInString := '你需要输入的字符串';
  sOutString := '';

  InBytes := TEncoding.Unicode.GetBytes(sInString + #0); // + #0  这样安全,也是各种语言要求的。
  OutStrLength := 200; //不够你自己加。
  SetLength(OutBytes, OutStrLength * Sizeof(WideChar));
  FillChar(OutBytes[0], OutStrLength * Sizeof(WideChar), 0);
  InString := Addr(InBytes[0]);
  OutString := Addr(OutBytes[0]);
  dlltestfunW(InString, OutString, OutStrLength);
  SetString(sOutString, OutString, Min(StrLen(OutString),OutStrLength)); //输出的字符串,必须这样获取。
  //总算是得到返回字符串了。
  ShowMessage(sOutString);
end;



最后,再提醒一下 var xxx: PXXXXChar  中 var不是必须的。
特别是 跨语言互相调用的时候。建议去掉。
(C)(P)Flying Wang
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|新时代Pascal论坛

GMT+8, 2024-5-10 06:16 , Processed in 0.058678 second(s), 28 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表