0

I've searched around Stackoverflow for the issue I was having, but they were all very specific for that particular access violation.

The piece of code I believe I am having trouble with is a function called MpqExtractFile, stored in a DLL.

Function:

function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: AnsiString): Boolean; stdcall; external 'MpqLib.dll' name 'B2';

Call:

if MpqExtractFile(hMPQ, 'war3map.j', AnsiString(tempDir+'\war3map.j')) = True
then ShowMessage('Success.')
else ShowMessage('Failed.');

Upon execution of this code my application throws the access violation error so the ShowMessage is not displayed at all.

I am unsure if the above is adequate to even estimate what the problem could be, but if there is anything else I should please tell me.

Edit

This is an extract of the VB.NET source code that I have been interpreting:

Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (ByVal hMPQ As Integer, ByVal szToExtract As String, ByVal szExtracted As String) As Boolean

I am obviously not familiar with declarations in other languages, but I have found the below function in the same VB file where the VB functions were declared.

BOOL WINAPI MpqExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted);

Thank you in advanced!

ple103
  • 2,080
  • 6
  • 35
  • 50
  • 2
    How is "B2" declared in the DLL? What language is the DLL coded in - if it isn't Delphi then `AnsiString` will never work, normally you would need to use PAnsiChar. – Gerry Coll Oct 21 '12 at 01:50
  • How is it declared? I'm unsure. I believe the DLL is coded in C++. I will try that now and see if it makes a difference. – ple103 Oct 21 '12 at 01:54
  • If in C or C++, it will almost certainly be PAnsiChar, or PChar if using UTF-16. A Delphi AnsiString is a very different beast. – Gerry Coll Oct 21 '12 at 02:01
  • Access violation at address 00240E8E in module 'MpLib.dll'. Read of address 00070D94. – ple103 Oct 21 '12 at 02:55
  • Do you need to cast the second parameter as an AnsiString as well as the third? –  Oct 21 '12 at 03:14
  • @SilentD - I am using PAnsiChar as suggested by Gerry Coll, but yes both the second and third parameters are the same data type. Would you have any idea what `ByVal` denotes in VB.NET? – ple103 Oct 21 '12 at 03:22
  • @petersmileyface just a wild guess: isn't hMPQ declared as "*int" in the dll? –  Oct 21 '12 at 03:22
  • With the asterisks? I'm not sure, but what would the difference be between `int` and `*int`? – ple103 Oct 21 '12 at 03:25
  • 1
    @petersmileyface, there's a huge difference between `int` and `*int`. The first is an integer while the second is a **pointer** to an integer (in C and C++). – Guillem Vicens Oct 21 '12 at 06:36
  • 2
    A pointer to an int would be `int*`, not `*int` in C/C++. – Remy Lebeau Oct 21 '12 at 07:27
  • 2
    This question is poor without the C++ declarations. Please add them. – David Heffernan Oct 21 '12 at 08:07
  • @RemyLebeau, you're right, I miswrote it. Can't edit my comment anymore, though :-( – Guillem Vicens Oct 21 '12 at 08:45
  • @DavidHeffernan - I actually do not have the C++ declarations, but I do have the VB.NET source code that utilises the functions within the DLL. – ple103 Oct 21 '12 at 20:13
  • I have added the VB.NET declaration and the details for the particular function in question. – ple103 Oct 21 '12 at 20:29
  • 1
    @peter - ByVal is 'by value', a value parameter in Delphi. – Sertac Akyuz Oct 21 '12 at 21:49

2 Answers2

7

AnsiString is the completely wrong type to use, unless the DLL was writting in Delphi/C++Builder and actually used AnsiString in its parameters. If that were the case, you would need to know which version of Delphi/C++Builder the DLL was written in, because the memory layout of AnsiString was changed in 2009.

In any case, it is very dangerous to pass non-POD data across DLL boundaries, so most DLLs do not do that. The parameters in question are most likely PAnsiChar instead, eg:

function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; stdcall; external 'MpqLib.dll' name 'B2'; 

.

if MpqExtractFile(hMPQ, 'war3map.j', PAnsiChar(AnsiString(tempDir+'\war3map.j'))) then
  ShowMessage('Success.') 
else
  ShowMessage('Failed.'); 

Other points to consider:

1) not all DLLs use the stdcall calling convention. It is not uncommon for DLLs written in C, like many open-source libraries are, to use the cdecl calling convention instead:

function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; cdecl; external 'MpqLib.dll' name 'B2'; 

2) C does not have a true Boolean data type like Delphi and C++ do. It is not uncommon for C code to use Byte or even Integer to mimic a Boolean.

In order to use a DLL in Delphi, you really need to know the actual proper declaration of its exported functions. This is less of an issue in C/C++ because most DLLs have an accompanied .h file that provides the declarations. Do you have such a .h file? If so, post it here so someone can verify your translation to Delphi.

Update:

Based on new information, the correct Delphi declaration is this:

function MpqExtractFile(hMpq: THandle; const szToExtract, szExtracted: PAnsiChar): BOOL; stdcall; external 'MpqLib.dll' name 'B2';
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I have been able to find the source code for the DLL, but unsuccessful in finding the function mentioned in the original post because there are various .h and .cpp files. The source can be found here: http://files.magosx.com/download.php?file=MpqLibSource.zip – ple103 Nov 08 '12 at 12:04
  • That project is for a .Net Assembly, not a DLL. – Remy Lebeau Nov 08 '12 at 17:53
  • Sorry for the inconveniences Remy. In the same VB file that included the functions I've been trying to interpret, I have found some other declarations. I have added it to the original post also. – ple103 Nov 08 '12 at 21:26
4

Your VB.net declaration is:

Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (
    ByVal hMPQ As Integer, ByVal szToExtract As String, 
    ByVal szExtracted As String) As Boolean

The equivalent Delphi import would be:

function MpqExtractFile(MpqExtractFile: Integer; 
    szToExtract, szExtracted: PAnsiChar): BOOL; 
    stdcall; external 'MpqLib.dll' name 'B2';

Delphi string types should not be used for interop. The p/invoke marshaller maps String to C++ char* which is PAnsiChar in Delphi.

This sort of task really should be carried out with the C++ header file. You say you have not got that. If the DLL is written in C++ then the header file surely exists. It would pay to track it down and work from that as your source.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490