"Handle" Data Type Equivalent - delphi

I am reading the documentation for MoPaQ and trying to convert the functions over to Delphi because some of the data types mentioned in the documentation do not directly match the ones in Delphi.
This is the function I am having troubles with:
HANDLE WINAPI MpqOpenArchiveForUpdate(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwHashTableSize);
In Delphi, I have converted it as:
function MpqOpenArchiveForUpdate(lpFileName: Char, CreationDisposition, dwHashTableSize: LongWord); external 'lmpqapi.dll';
I am missing the return type for my function which I believe is HANDLE (according to the documentation). If that is the case, what is the Delphi equivalent for the HANDLE data type?

The equivalent would be something along these lines:
function MpqOpenArchiveForUpdate(lpFileName: LPCSTR;
dwCreattionDisposition,
dwHasTableSize: DWord): THandle; stdcall; external 'lmpqapi.dll';
Note that you'll have to find out if MoPacQ is Unicode-aware or not; if it's not, change the definition of lpFileName to PAnsiChar instead.
Remy points out in his comment below that Delphi maps LPCSTR to the proper PAnsiChar type on all versions, so you should use it that way.

Related

Pointer typecasting in Delphi

How can I correctly typecast to a structure in Delphi? This does not work exactly like in C++ where one would just pass a &Data according to the MSDN documentation.
program Project1;
uses
System.SysUtils,
Winapi.Windows,
Winapi.Winsock2;
function WSAStartup(wVersionRequired: WORD; out lpWSAData: LPWSADATA): Integer; WINAPI; external 'ws2_32.dll';
var
Data: WSADATA;
begin
WSAStartup(WINSOCK_VERSION, LPWSADATA(#Data)); // E2197 Constant object cannot be passed as var parameter
ReadLn;
end.
I guess that you've translated the function from the MSDN documentation which reads:
int WSAStartup(
_In_ WORD wVersionRequested,
_Out_ LPWSADATA lpWSAData
);
The confusion stems from the use of the _Out_ annotation. That is a macro that expands to nothing. It is used to convey intent to tools that, for instance, convert the header file declaration to different languages. More information can be found here:
What is the meaning of _In_ and _Out_ in documentation of any function?
What is the purpose of __in __out __in_opt __allowed(), how do they work? Should I use similar constructs in my own code?
You've erroneously translated _Out_ to the Delphi out keyword. You could simply remove that keyword and your declaration would be correct:
function WSAStartup(wVersionRequired: WORD; lpWSAData: LPWSADATA): Integer;
WINAPI; external 'ws2_32.dll';
Then your call would be:
WSAStartup(WINSOCK_VERSION, #Data);
Alternatively, since this parameter is not optional, you could translate it like this:
function WSAStartup(wVersionRequired: WORD; out lpWSAData: WSADATA): Integer;
WINAPI; external 'ws2_32.dll';
You would then call like this:
WSAStartup(WINSOCK_VERSION, Data);
You should however, use the declaration of the function that can be found in Winapi.Winsock2 and so avoid risking making such mistakes. That is, assuming that Embarcadero have not made mistakes in translation, which does sometimes happen.
Finally, it would be remiss of me were I not to chide you, at least mildly, for ignoring the return value of the function call.

What is exact signature of System.Assign (for use in procedural expression)?

Unfortunately, exact Assign signature is not available in the RTL source, and my attempts to guess, like:
const
Assign: procedure (var F; const FileName: string) = System.Assign;
{ or }
Assign: function (var F; const FileName: string): Integer = System.Assign;
{ also I tried "internal" one from _AssignFile }
didn't yield any positive results, and compiler refuses to treat this contant expression as procedural and complains about right-value (E2029 '(' expected but ';' found)).
So, which type should I use to match Delphi RTL exactly?
Assign is a language remnant of Delphi tracing its origins to the original Turbo Pascal syntax. Long before function overloading was added to the language syntax, long before the RTL's text file and "file of type" internal data structures were documented, there was Assign.
Assign is a simple enough procedure to use, but when you look at what it has to do you'll see that it's pretty much impossible to implement without some sort of compiler magic. File of TFoo creates a file type that is distinct and incompatible with File of Integer. And yet, both can be passed as the first parameter to Assign.
Today, you could probably implement Assign using a generic type param for the file parameter. The generic type would conform to the type of the variable passed in. That's great and all, but we needed a way to associate a typed file variable with a string filename 25 years before generics were added to the mix.
Assign is a compiler intrinsic function. That means it lives outside of the Delphi/Pascal syntax space. Assign is essentially a type conforming procedure, which can't be represented in the strongly typed Delphi/Pascal language (without modern and complex language extensions such as generic types). It's not typeless, and it's not dynamically typed because the actual parameter type is fully determined at compile time.
You can't take the address of the Assign function. Even if you could, because it is amorphous, you won't be able to define a procedure type that accurately represents all of its possible call signatures.
Compiler magic functions exist outside the language to take care of tasks that could not be easily represented in the syntax available at the time. Assign is one example. Writeln() is another.
System.Assign is an intrinsic function. The official documentation of it is completely hopeless. It says:
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
I can only guess as to why the documentation generator cannot cope with this function. But the three identical overloads are clearly bogus. And it's not a function, rather it is a procedure.
No matter. Because it is an intrinsic, you cannot assign it to a function pointer. The solution is to wrap it up in a function of your own.
procedure MyAssign(var F: File; const FileName: string);
begin
Result := System.Assign(F, FileName);
end;
You can then assign that function to a variable or constant of procedural type.
const
AssignProc: procedure(var F: File; const FileName: string) = MyAssign;
The other variant of Assign takes a third parameter that specifies the code page, passed as a Word. You can only call that function if the first argument to Assign is a TextFile.
So, intrinsics are really a law unto themselves.
Note that the documentation does state that Assign should no longer be used. Instead you should use AssignFile. The documentation is no better there mind you!
If to open System.pas file and read it, in XE2 you have those internal procedures:
function _AssignFile(var t: TFileRec; const s: PChar): Integer;
function _AssignText(var t: TTextRec; const s: PChar; const CP: word): Integer;
And i don't think you have any global stable procedure w/o underscore in names - those listed are Delphi compiler internals, like invisible procedures, that are covertly called when you declare string or dynarray variable.
http://en.wikipedia.org/wiki/Intrinsic_function
Like a lot of procedures like _Write0LString which are implementing usual WriteLn calls.
So you would have to make a wrapper function that would call AssignFile and put reference to your wrapper into your variable instead;
However you did not put tag with your Delphi version, and different vesions may have different declarations for intrinsics. So look into sources of your particular Delphi.
Also, why would you need it ? I remember i did such a thing in TurboPascal 5.5 to make string-based file - make WriteLn act like C sprintf. But why would you need it in modern Delphi, when you can use TStream instead?

Access violation at address 003B0E8E in module 'MpLib.dll'. Read of address 00120BF4

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!
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';
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.

Delphi 7 DLL to be called from Delphi XE

I need to wrap some legacy code in Delphi 7 for use within Delphi XE2. My question seems simple, but I tried ALL the examples I could find and they all fail. Basically, I need to be able to pass strings between D7 and DXE2, and as far as I can figure out, the safest approach is to use pchar (since I do not want to ship the borlandmm dll).
So DLL written in D7, to be called by Delphi XE2
My interface needs to be
IN My DLL:
function d7zipFile(pFatFile,pThinFile : PChar) : integer; stdCall;
function d7unzipfile(pThinFile,pFatFile : PChar) : integer; stdCall;
I need to pass BACK the pFatFile name in the unzipfile function.
In My calling code:
function d7zipFile(pFatFile,pThinFile : PChar) : integer; external 'd7b64zip.dll';
function d7unzipfile(pThinFile,pFatFile : PChar) : integer; external 'd7b64zip.dll';
Could someone please assist with the best way to implement these?
Obviously I am not looking for the actual zip/unzip code - I have that working fine within D7. I want to know how to declare and work with the string / pchar params, since the various types I tried (PWideChar, WideString, ShortString etc) all give errors.
So I would be happy to simply be able to do a showMessage in the d7zipFile function for both filenames.
And then be able to do a showMessage in delphiXE2 on the pFatFile variable, which means the strings went both ways OK?
By far the easiest way to do this is to use WideString. This is the Delphi wrapper around the COM BSTR type. Dynamic allocation of the string payload is done using the shared COM allocator. Since the Delphi RTL manages that, it is transparent to you.
In the Delphi 7 code you declare your functions like this:
function d7zipFile(const FatFile, ThinFile: WideString): integer; stdcall;
function d7unzipfile(const ThinFile: WideString; var FatFile: WideString):
integer; stdcall;
In your calling code you declare the functions like this:
function d7zipFile(const FatFile, ThinFile: WideString): integer; stdcall;
external 'd7b64zip.dll';
function d7unzipfile(const ThinFile: WideString; var FatFile: WideString):
integer; stdcall; external 'd7b64zip.dll';
The alternative to this approach is to use PAnsiChar or PWideChar. Note that you cannot use PChar because that alias refers to different types depending on which version of Delphi you use. In Delphi 7 PChar is an alias for PAnsiChar, and in XE2 it is an alias for PWideChar.
The big downside of using PAnsiChar, say, is that the caller needs to allocate the string which is returned from the DLL. But typically the caller does not know how large that string needs to be. There are a variety of solutions to the problem but the neatest approach is always to use a shared allocator. You state that you do not want to rely on borlandmm.dll and so the next most obvious common allocator is the COM allocator. And that's why WideString is attractive.

Exist any Delphi RTL function equivalent to swscanf

I'm looking for RTL delphi function equivalent to the sscanf/swscanf C++ functions. Exist something like that in the RTL delphi functions or I must look for a third-party implementation?
I personally would simply call the real functions which can be imported from msvcrt.dll.
function sscanf(buffer, format: PAnsiChar): Integer;
cdecl; varargs; external 'msvcrt.dll';
function swscanf(buffer, format: PWideChar): Integer;
cdecl; varargs; external 'msvcrt.dll';
These are tried and tested robust implementations of the C standard library functions.
You can download Scan function for Delphi, which is ported MSVC RTL function.
There's a port of sscanf at http://cc.embarcadero.com/item/24258 but it looks to pre-date Unicode so may need some tweaks for XE.
These are deprecated functions even in C. Use something more modern and type-safe to parse your data. This is going to be a rich source of errors in your application. For example, how do you intend to make a (wchar_t *) C style scanf string function interoperate with Delphi Strings?

Resources