Data block:
PMyDataBlock = ^MyDataBlock;
MyDataBlock = record
// .............
end;
Is a following definition:
function MyFunction(const pstSettings: MyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
a full equivalent of this?:
function MyFunction(pstSettings: PMyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
The short answer is "No, it is not"
In your case, your record may or may not be passed by reference. The size of the record is 1 of the factor I know of that affect that behavior. If your record is 4 bytes or less, I believe it will be passed by value, otherwise, it will be passed by reference. I don't believe this behavior is contractual (In other word, Embarcadero is free to change it at any time in the future). In other words, it's a bad idea to use const parameter to call an external function.
If you want to pass your record by reference, the proper way to do would be to declare it var
function MyFunction(var pstSettings: MyDataBlock): HRESULT; stdcall; external 'MyLib.dll' name 'MyFunction';
or pass it as a pointer.
Related
I'm trying to implement a record pointer in Inno Setup (Unicode) to match a Delphi DLL's specifications...
type
PUnzipFile = ^TUnzipFile;
TUnzipFile = record
Caption: WideString;
Src: WideString;
Dest: WideString;
Status: Integer;
Size: Integer;
ErrCode: Integer;
ErrMsg: WideString;
end;
TUnzipFiles = array of PUnzipFile;
function UnzipFiles(var Files: TUnzipFiles; const Silent: Bool): Bool;
external 'UnzipFiles#files:Unzipper.dll stdcall';
The problem is that the compiler fails on the line PUnzipFile = ^TUnzipFile; because apparently Inno Setup doesn't support pointers as Delphi does. This record pointer works perfect when implemented in Delphi...
function UnzipFiles(var Files: TUnzipFiles; const Silent: Bool): Bool; stdcall;
external 'Unzipper.dll';
How can I work with this DLL if Inno Setup doesn't support record pointers?
There is no need for pointers.
Inno Setup Pascal Script does not support pointers.
The statement:
function UnzipFiles(var Files: TUnzipFiles; const Silent: BOOL): BOOL;
external 'UnzipFiles#files:Unzipper.dll stdcall';
Passes Files as a var parameter, which means that what's really passed is a pointer to TUnzipFiles. There is no need to make TUnzipFiles array of pointers.
Just make it a normal array and everything will work.
The solution is to just use an array of the record in question:
TUnzipFiles = array of TUnzipFile;
Now it will work.
Because a var parameter passes a pointer internally your call will not be any slower (or faster).
That's the beauty of Delphi. It hides the complexity of pointers in almost all cases where you'd need it in C.
All objects references and var parameters are really pointers, but you needn't worry about that.
I'm attempting to call GetStringTypeW from a non-unicode delphi application and, no matter what I do, I get ERROR_INVALID_FLAGS back. I couldn't find any code sample of that function in use either.
I also had to redifine the function header because the ones provided in windows.pas incorrectly identifies the 3rd parameter as a boolean (it's an INT)
here is my definition for the function:
function GetStringTypeW(dwInfoType: DWORD; lpSrcStr: PWideChar; cchSrc: Integer; lpCharType: Pointer): BOOL;
(For some reason, that function isn't defined as stdcall. Trying it to define it as stdcall will result in an access violation.)
And my call:
var
aCharType: Array of WORD;
APassword: WideString
begin
{..}
SetLength(aCharType, Length(APassword));
if not GetStringTypeW(CT_CTYPE1, PWideChar(APassword[1]), Length(APassword), #aCharType[0]) then
RaiseLastOSError;
{..}
The error I get is
System Error. Code: 1004.
Invalid flags.
I've verified that CT_CTYPE1 is equal to 1.
Does anyone know what could be wrong or have a code sample for using this function ?
The declaration in Windows.pas is indeed wrong, but your correction is wrong, too. You've fixed the parameter types, but you need to fix the calling convention. It should be stdcall:
function GetStringTypeW(
dwInfoType: DWORD;
const lpSrcStr: PWideChar;
cchSrc: Integer;
lpCharType: PWordArray
): BOOL; stdacll;
Without the calling-convention fix, Delphi puts most of the parameters in registers, but the OS expects to find them on the stack. The stack doesn't contain the right values at the right places, to it fails. A bad calling convention can also lead to access violations because the stack is left in an inconsistent state, but since you immediately throw your own exception anyway, that might conceal the stack problems.
When you use stdcall, you get an access violation because you're passing a character and claiming it's a pointer. The OS attempts to dereference the "pointer," but since the character value doesn't represent a valid address, it fails. Type-cast the whole string, not just one character, when you call the function:
GetStringTypeW(CT_CTYPE1, PWideChar(APassword), Length(APassword), PWordArray(aCharType))
Your second parameter is wrong. You are type-casting a single WideChar value to a PWideChar, instead of obtaining the memory address of that WideChar. In other words, change this:
PWideChar(APassword[1])
To this:
PWideChar(APassword)
Or this (only if the length of APassword is never 0):
#APassword[1]
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?
I am writing a DLL which make some operations on a particular window, but sometimes the handle passed is not valid. Does there exist any function to validate that the handle passed is valid (belongs to a window)?
Try using the IsWindow function, which is declared in the Windows unit.
function IsWindow(hWnd: HWND): BOOL; stdcall;
I have a dll library. I have excluded memory unit for delphi types.
In that way, what would be the appropriate Boolean type for function declaration?
Is it BOOL or something else?
The problem is that in the method signature:
function Test(Param1: BOOL; Param2: BOOL; docContent: PCharArray): Integer;
I get AV when program leaves that function.
I assume that it is the problem with the data type of these two first parameters.
BOOL is fine for Boolean types. It's a Windows type, so it's what you'll see in all the functions in Windows.pas.
Access violations upon return from a DLL function often indicate that you have the calling convention wrong — the default calling convention is register, but you probably need stdcall or cdecl. Add it at the end of the declaration:
function Test(Param1: BOOL; Param2: BOOL; docContent: PCharArray): Integer; stdcall;