Delphi 调用 DLL 里面的 C 函数传递字符串

Delphi 专家李维有篇文章,针对本文标题的情况,介绍了一种代码的写法。

前提:

用 C 写的 DLL,如果传递的参数是字符串,通常是传递指针 Char*,对应到 Delphi 的数据类型是 PAnsiChar;这个 PAnsiChar 其实也就是个指针 Pointer;就好比说,这个白马它其实就是个马。

研究:

我没有 C 编译器用来做一个 DLL。所以,我在 Delphi 里面写了一个模拟的函数,关键是把函数的参数的类型都用 PAnsiChar 这个类型,就能很好地模拟了。

 我的模拟 DLL 的 C 函数:

procedure TForm1.AddStr(Str1, Str2: PAnsiChar;  OutStr: PAnsiChar);
var
  S: AnsiString;
begin
  //模拟 C 函数的 Char*
  S := AnsiString(Str1) + AnsiString(Str2);
  Move(S[1], OutStr^, Length(S));
  //OutStr := PAnsiChar(S); //这里的 S 离开这个函数就不存在了,因此,把 S 的指针传出去,是有问题的。
  //因为 OutStr 这个指针是外部传入的,在外部已经分配好内存了,这里只需要往里面填数据,
  //因此这个 OutStr 不能定义为 var 的。
end;

有了上述函数,我先用普通的 Delphi 的代码去调用它,测试一下。

普通的代码如下:

procedure TForm1.Button1Click(Sender: TObject);
var
  OutStr: PAnsiChar;
  Str1, Str2: PAnsiChar;
  S, S1, S2: AnsiString;
begin
  S1 := Edit1.Text;
  S2 := Edit2.Text;
  Str1 := PAnsiChar(S1);
  Str2 := PAnsiChar(S2);

  GetMem(OutStr, 1024);
  try
    Self.AddStr(Str1, Str2, OutStr); //采用指针调用 C 函数

    S := AnsiString(OutStr);
    Memo1.Lines.Add(S);
  finally
    FreeMem(OutStr);
  end;
end;

上述代码,测试成功。处理英文和汉字混合的字符串也没有问题。

李维推荐的新的写法:

procedure TForm1.Button2Click(Sender: TObject);
var
  M: TMarshaller;
  pOutData : TPtrWrapper;
  sOutData : String;
begin
  //李维介绍的写法 https://gordonliwei.wordpress.com/2021/12/
  
  try
    pOutData := TMarshal.AllocMem(2048);

    Self.AddStr(M.AsAnsi(Edit1.Text).ToPointer,
      M.AsAnsi(Edit2.Text).ToPointer,
      pOutData.ToPointer);
  finally
    sOutData := TMarshal.ReadStringAsAnsi( pOutData, -1);
    TMarshal.FreeMem(pOutData);
  end;
  Memo1.Lines.Add(sOutData);
end;

上述代码同样测试通过。这里的原理是采用 TMarshaller 这个东西的一些方法来做字符串和指针之间的转换。

要注意的问题:

这里,AddStr 函数的参数都是 PAnsiStr。一开始写这个代码时,OutStr 因为是输出,我习惯性地给它加了一个 var 在前面。然后,第二种方式,编译时提示 pOutData.ToPointer 这里类型不对。

我以为这里是 Pointer 和 PAnsiChar 的类型不匹配导致,因此把 AddStr 的参数全部改为 Pointer 类型。结果仍然提示类型不对。最后把 AddStr 的参数里面的 var 去掉,才编译通过。

然后就有问题了。当 AddStr 的三个参数的类型都是 Pointer 的时候,第一种传统写法的代码,运行结果正常。但第二种代码,运行结果出错。设置断点跟踪,发现传进去 AddStr 函数里面的指针是空指针。

最后,把 AddStr 的参数的类型从 Pointer 改为 PAnsiChar 后,一切都正常了。

M.AsAnsi(Edit1.Text).ToPointer 给出的东西就是一个 Pointer 类型的指针,为啥传递给 AddStr 的时候,如果 AddStr 的参数是指针 Pointer,它反倒没内容,是 PAnsiChar 却又正确了?

结论:

对于 DLL 里面的接口函数 C 函数的参数类型为 Char*  的调用,李维推荐的代码写法测试通过。

你可能感兴趣的:(windows)