Question

I have dll which accepts Pointer to array of bytes from C++ and try to move this data into AnsiString the following way

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

If I call this method from 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;

it works fine, but from c++ if gives AV on SetString.

Please help me with this.


From RredCat:

Let me add some explanation to Yuriy's question: First of all about languages that we use. We need to call Delphi dll in C# project. I've created C++\CLI layer (proxy) for this purposes. Now about C++\CLI code in header file:

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

In cpp I set Convert in constructor:

hDelphiDLL = LoadLibrary(<path to dll>);

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

And last one method that I call from C#:

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

Solution

Your Delphi code passes a pointer to pointer to array of bytes. Simplify it to use just a pointer to array of bytes:

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

and call it as

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

or

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

which is the same.

OTHER TIPS

You are making this task much more complicated than it needs to be. C++/CLI is great when you have an existing body of C or C++ code, or a native library with a very large interface. So long as you only have a handful of functions, direct p/invoke to the native Delphi DLL is the way to go here.

Serg has already pointed out the spurious extra level of indirection in your Delphi function. So I would write the Delphi function as:

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

On the C# side you can declare the function like this:

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

You can call this function from your C# code like this:

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

And that's it! Since at the C# end you have a string variable you can rely on the p/invoke marshaller to provide a null-terminated string to the native code. Hence there is no need to pass the length. The default calling convention for p/invoke is stdcall so prefer that in the Delphi code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top