可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a legacy DLL written in C that contains a function that returns a string, and I need to access this function from Delphi. The only info I have about the DLL is the VB declare for accessing the function:
Public Declare Function DecryptStr Lib "strlib" (Str As String) As String
I've tried the following without success:
Declaration:
function DecryptStr(s: PChar): PChar; cdecl; external 'strlib.dll';
Usage:
var
p1, p2 : pchar;
begin
GetMem( p1, 255 );
StrPCopy( p2, 'some string to decrypt' );
p1 := DecryptStr( p2 );
end;
This consistently crashes the DLL with an Access Violation. I'm at a loss.
Any suggestions ?
回答1:
Consider rewriting your test code as follows:
var
p1, p2 : pchar;
begin
GetMem( p1, 255 ); // initialize
GetMem( p2, 255 );
StrPLCopy( p2, 'some string to decrypt', 255 ); // prevent buffer overrun
StrPLCopy( p1, DecryptStr( p2 ), 255); // make a copy since dll will free its internal buffer
end;
If still fails within a call to DecryptStr, then read http://support.microsoft.com/kb/187912 carefully.
回答2:
p2 isn't initialized. StrPCopy copies the string to a random memory location. And most likely the calling convention is stdcall.
回答3:
I'm guessing here, but are you sure it's cdecl? If the VB declare isn't mentioning it, I'd assume it's in fact a STDCALL function (STDCALL is quite common on Windows, as almost all of its native API uses it). Calling a function of one calling convention as if it were of another calling convention can really mess up the stack, usually leading to a crash.
Also, be sure to check whether the string is ANSI (LPSTR/LPCSTR) or UNICODE (LPWSTR/LPCWSTR). I don't know VB or Delphi, so I don't know what each one uses by default.
回答4:
As Jozz says, p2 (where you copy your string to) is never initialized in your example.
Try this instead.
var
p1, p2 : pchar;
begin
GetMem( p2, 255 ); // allocate memory for 'some string...'
StrPCopy( p2, 'some string to decrypt' );
p1 := DecryptStr( p2 );
end;
Also, the memory you allocated by calling Getmem(p1,...) would have been leaked, because p1 was overwritten by the function return from DecryptStr.
However, I'd be a bit concerned about exactly what DecryptStr is returning, and who owns the memory pointed to by p1. If it's returning a pointer to memory allocated by the DLL you will need to be careful how that memory is freed.
回答5:
I agree with CesarB, try to declare it with stdcall directive as:
function DecryptStr(s: PChar): PChar; stdcall; external 'strlib.dll';
if it doesn't work, post the VB declaration here.
回答6:
The best way in these kind of situation is to debug your program and check the stack before and after executing the callback. How knows, it might even be a bug in the external DLL?
This way you will see pretty easy how to correct this.
回答7:
Was the dll written in Borland C or C++Builder by any chance with the intention of being used with Delphi? In which case it could have been compiled using a pascal directive.
回答8:
The suggestions that the strings must be "initialised" seem to be correct. This is because
C will require that the string being passed in is null-terminated. Check that the character in the bufffer just after the end of the text is a null (#0).
Why do you assume that the string passed in is exactly 255 chars long? You need to allocate Length(p1) + 1 bytes - for the chars in p1, and a #0 char at the end.
Also, your code sample seems confused as to the use of p1 and p2. It looks like p1 is the buffer passed to the C DLL, which you allocate, and p2 is the returned string that the DLL allocates. But then the code would be (note use of p1 and p2)
var
p1, p2 : pchar;
begin
GetMem( p1, 255 );
StrPCopy( p1, 'some string to decrypt' );
p2 := DecryptStr( p1 );
end;
Better variable names would help you make this clearer.
回答9:
I'm dropping in my solution as I've sturggled quite a bit with it and haven't found it in any of the answers.
The C++ function looks like this:
int __stdcall DoSomething(char * _name);
To get it working in Delphi, I declare the following function
function DoSomething(name: PAnsiChar): integer; stdcall; external 'somedll.dll';
And then when I make the call, I have a function that looks like this:
var s: PAnsiChar;
begin
GetMem(s, 255);
DoSomething(s);
// s now contains the value returned from the C DLL
end;
I have tried using PChar instead of PAnsiChar but all I get in return is garbage. Also, if I declare the function in Delphi with the parameter set to var , I get an exception when I try to read it.
Hope this helps anyone..