【问题标题】:Correct converting bytes from C++ to delphi AnsiString正确将字节从 C++ 转换为 delphi AnsiString
【发布时间】:2012-02-01 15:08:17
【问题描述】:

我有一个 dll,它接受来自 C++ 的指向字节数组的指针,并尝试通过以下方式将此数据移动到 AnsiString 中

procedure Convert(const PByteArr: Pointer; ArrSize: Cardinal); export; cdecl;
var
  Buf: AnsiString;
begin
  SetString(Buf, PAnsiChar(PByteArr^), ArrSize);
end;

如果我从 Delphi 调用这个方法

procedure Try;
var
  M: TMemoryStream;
  Arr: TBytes;  
begin
  M := TMemoryStream.Create;
  try
    M.LoadFromFile('myfile.dat');
    SetLength(Arr, M.Size);
    M.Position := 0;
    M.Read(Arr[0], M.Size);
  finally
    M.Free;
  end;
  Convert(@Arr, Length(Arr));
end;

它工作正常,但是从 c++ 中如果在 SetString 上给出 AV。

请帮帮我。


来自 RredCat:

让我对 Yuriy 的问题做一些解释: 首先是关于我们使用的语言。我们需要在 C# 项目中调用 Delphi dll。为此,我创建了 C++\CLI 层(代理)。 现在关于头文件中的 C++\CLI 代码:

HINSTANCE hDelphiDLL;
typedef void (*pPBDFUNC)(byte* aBytes, int size);
pPBDFUNC Convert;

在 cpp 中,我在构造函数中设置了 Convert:

hDelphiDLL = LoadLibrary(<path to dll>);

if(NULL != hDelphiDLL ){
    pPBDFUNC clb= GetProcAddress(HMODULE(hDelphiDLL), "Convert");
        if(NULL != clb){
            Convert= pPBDFUNC (clb);
        }...

最后一个我从 C# 调用的方法:

void Class1::Test(byte* aBytes, int size){
    Convert(aBytes,size);
}

【问题讨论】:

  • 1.不需要TBytes。只需使用M.MemoryM.Size。 2. 至于问题本身,给我们看C++。或者 C#,不管它是什么。
  • 他两次提到 C++,并把它标记为 C++,所以我要更改标题。

标签: c# c++ delphi interop data-conversion


【解决方案1】:

您的 Delphi 代码将指针传递给指向字节数组的指针。将其简化为仅使用指向字节数组的指针:

procedure Convert(const PByteArr: Pointer; ArrSize: Cardinal); export; cdecl;
var
  Buf: AnsiString;
begin
  SetString(Buf, PAnsiChar(PByteArr), ArrSize);
end;

并将其称为

Convert(@Arr[0], Length(Arr));

Convert(Pointer(Arr), Length(Arr));

这是一样的。

【讨论】:

  • 如果Convert 的第一个参数具有更好的类型,则更容易发现错误。例如,如果它是指向字节的指针,则将其设为PByte。那么很明显,传递^TBytes 是错误的;如果你做的明智的事情并启用“typed @ operator”选项,编译器甚至会抱怨它。
  • 这个答案正是我需要的。
【解决方案2】:

您正在使这项任务变得比它需要的复杂得多。当您拥有现有的 C 或 C++ 代码主体或具有非常大接口的本机库时,C++/CLI 非常有用。只要你只有少数几个函数,直接 p/invoke 到本机 Delphi DLL 就可以了。

Serg 已经指出了 Delphi 函数中虚假的额外间接级别。所以我会把Delphi函数写成:

procedure TestFunction(Text: PAnsiChar); stdcall;
var
  Buf: AnsiString;
begin
  Buf := Text;
end;

在 C# 端,您可以像这样声明函数:

[DllImport(@"mydll.dll")]
static extern void TestFunction(string Text);

您可以像这样从 C# 代码中调用此函数:

TestFunction("Hello");
string str = GetStringFromSomewhere();
TestFunction(str);

就是这样!由于在 C# 端您有一个字符串变量,您可以依靠 p/invoke 编组器向本机代码提供一个以 null 结尾的字符串。因此没有必要传递长度。 p/invoke 的默认调用约定是stdcall,所以更喜欢在 Delphi 代码中使用。

【讨论】:

  • 在开始实施之前,我们已经对这两种方式进行了调查。 PInvoke 不适合我们。因为在实际代码中,我们通过保留内存将结果作为 PChar 返回,将其转换为 C++\CLI 中的字符串并调用释放此内存的方法(并将此 PCHar 作为参数传递)。
  • 您可以使用 p/invoke 轻松完成所有这些工作。一种简单的方法是返回 Delphi WideString,然后使用 MarshalAs 在托管端将其编组为 BSTR。这真的很简单,而且比您的 C++/CLI 方法要容易得多。
  • 我听说过传球是一种不好的方法。最好使用 PChar 或字节。对吗?
  • 将字符串数据从 C# 传递到本机是微不足道的。在托管端将参数声明为string,在本机端将参数声明为PAnsiCharPWideChar。在另一个方向,WideString/BSTR 是最简单的。做起来很简单,我敢肯定你把它复杂化了。
  • 将文件作为字节数组传递呢?我需要做两件事:1)PByte 记录; 2) PByte 到 AnsiString。至于2,我在我的问题中写了和例子。写Move(TBytes(PArr)[0], FMyRecord, SizeOf(FMyRecord))时我还有AV; `
猜你喜欢
  • 2014-01-19
  • 2010-09-25
  • 2011-07-09
  • 2013-09-30
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多