Treating structs like FILETIME as UInt64/Int64 - delphi

The question arise after I read a MSDN Blog article, Why can't you treat a FILETIME as an __int64?. The article said that casting a FILETIME to an __int64 can create a misaligned pointer.
FILETIME, LUID, and LUID_AND_ATTRIBUTES structs declared in Windows header as follows:
typedef struct FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
}
typedef struct LUID {
ULONG LowPart;
LONG HighPart;
}
typedef struct LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
}
Since FILETIME and LUID structs has a similar layout, therefore treating a LUID as an __int64 also can create a misaligned pointer. However, Windows.pas (Delphi XE3 here) practises this--, for example:
{$ALIGN 4}
LUID_AND_ATTRIBUTES = record
Luid : Int64; // Here, LUID is treated as Int64
Attributes: DWORD;
end;
{$ALIGN ON}
another example is
function LookupPrivilegeValue(lpSystemName, lpName: LPCWSTR;
var lpLuid: Int64): BOOL; stdcall; // LUID is treated as Int64
How to safely treat structs like FILETIME or LUID directly as UInt64/Int64? What is the key?

It's a largely non-issue on the architectures that Delphi supports. The x86 and x64 architectures forgive you if you access mis-aligned data. On the other hand, accessing mis-aligned data on Itanium will result in runtime errors. But Delphi never targeted Itanium.
The issue that is significant is record layout. An Int64 has alignment of 8. But FILETIME and LUID have alignment of 4. Which is why LUID_AND_ATTRIBUTES is marked with an explicit $ALIGN 4.
If you are going to declare FILETIME and LUID to be Int64 then you need to take special care with record layout every time you include one in a record.

Related

Why so many pointers?

Searching for references of Win32 LDAP API functions, I found the following JwaWinLDAP.pas unit.
On this unit, to function ldap_search_st is declared:
function ldap_search_st(ld: PLDAP; base: PAnsiChar; scope: ULONG;
filter, attrs: PAnsiChar; attrsonly: ULONG; var timeout: TLDAPTimeVal;
var res: PLDAPMessage): ULONG; cdecl;
The timeout: TLDAPTimeVal parameter is declared as:
PLDAPTimeVal = ^TLDAPTimeVal;
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval;
PLDAP_TIMEVAL = ^LDAP_TIMEVAL;
TLDAPTimeVal = l_timeval;
On the code, if I use something like:
procedure foo;
var
TimeVal: PLDAPTimeVal;
begin
ldap_search_st(foo1, Nil, 0, PAnsiChar('(objectClass=*)'), Nil, 0, TimeVal, foo2);
end;
Compiler gives me error:
[dcc32 Error] Types of actual and formal var parameters must be
identical
because of the timeout parameter. If I change TimeVal type to TLDAPTimeVal it compiles and the application works.
The question is:
when I see declaration of types in Delphi, they are always like:
type
PType1 = ^Type1
Type1 = record...
In the specific example cited, it could be:
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
TLDAPTimeVal = l_timeval;
and it would work the exact same way (I think)... Why so much confusion on this kind of declaration?
type
PType1 = ^Type1
Type1 = record...
Above type declaration declares two types - one is record, and another one is typed pointer for that specific record type. They are usually declared in pairs, because they are related. But if your or any code does not need typed pointer, it does not need to declare pointer type.
Function ldap_search_st uses only declared record type. But some other functions in that unit expect pointer as parameter. That is why declaration has both.
Code in question is LDAP Windows API header translation for Delphi. Windows API uses pointers for passing structures to functions.
API translations are usually complex and sometimes have seemingly superfluous declarations. For completeness, translations usually contain all original declarations (symbols) - those are l_timeval, LDAP_TIMEVAL and PLDAP_TIMEVAL, and while those would be sufficient to use the API, there are two additional declarations whose only purpose is providing Delphi style names for more user friendly experience PLDAPTimeVal and TLDAPTimeVal
If you take a look at original LDAP function declarations they all use pointers for passing structures. For instance:
ULONG ldap_search_st(
_In_ LDAP *ld,
_In_ PCHAR base,
_In_ ULONG scope,
_In_ PCHAR filter,
_In_ PCHAR attrs[],
_In_ ULONG attrsonly,
_In_ struct l_timeval *timeout,
_Out_ LDAPMessage **res
);
and
ULONG ldap_connect(
_In_ LDAP *ld,
_In_ LDAP_TIMEVAL *timeout
);
There is one difference in those two considering timeout parameter.
ldap_search_st expects non-null value in timeout parameter - and Delphi translation of that parameter is var timeout: TLDAPTimeVal to match that intent more clearly - that declaration prevents you from accidentally passing null. While TLDAPTimeVal is not a pointer type, having var keyword makes our timeout parameter to behave like one. Behind the scenes Delphi will pass pointer to the structure and that will perfectly match original function declaration.
On the other hand in ldap_connect timeout can contain null value. In that case default timeout value will be used. The only way to satisfy that requirement is to use pointer type to timeout structure. In another words PLDAPTimeVal and Delphi translation of that function declaration is
function ldap_connect(ld: PLDAP; timeout: PLDAPTimeval): ULONG;
It is the matter of coding standards and conventions and there is so many of them because LDAP declarations were adopted into PSDK and then translated to Delphi. Additionally, since Pascal doesn't allow pointer type declaration (eg ^Integer) within formal parameters unlike plain C (eg int *), corresponding pointer types were added to declaration.
Here, I marked various conventions in the declaration, note the difference in casing and prefixes:
PLDAPTimeVal = ^TLDAPTimeVal; // Delphi pointer (Econos convention)
l_timeval = packed record // canonic structure (LDAP convention)
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval; // Windows structure (PSDK convention)
PLDAP_TIMEVAL = ^LDAP_TIMEVAL; // Windows pointer (PSDK convention)
TLDAPTimeVal = l_timeval; // Delphi structure (Econos convention)
Curious thing: the forward declaration (before structure) of Delphi pointer is also mandated by Econos convention. Original PSDK code declares the pointer after the structure.

Delphi "default" keyword with Record types in older Delphi versions

I have this code in Delphi Detours library which I'm trying to port:
type
TInstruction = record
Archi: Byte; { CPUX32 or CPUX64 ! }
AddrMode: Byte; { Address Mode }
Addr: PByte;
VirtualAddr: PByte;
NextInst: PByte; { Pointer to the Next Instruction }
OpCode: Byte; { OpCode Value }
OpType: Byte;
OpKind: Byte;
OpTable: Byte; { tbOneByte,tbTwoByte,... }
OperandFlags: Byte;
Prefixes: Word; { Sets of Prf_xxx }
...
end;
var
Inst: TInstruction;
begin
...
Inst := default (TInstruction); // <-
Inst.Archi := CPUX;
Pvt := PPointer(AIntf)^; // vTable !
PCode := PPointer(Pvt + Offset)^; // Code Entry !
Inst.NextInst := PCode;
...
end;
What does the "default" keyword do?
I assume something like:
FillChar(Inst, SizeOf(TInstruction), 0);
Is my assumption correct?
Default() is an undocumented intrinsic function introduced to support generics. The design of Delphi generics was heavily inspired by .net generics and you might benefit from reading the analagous documentation for .net: https://msdn.microsoft.com/en-GB/library/xwth0h0d.aspx
The purpose of Default() is to allow you to default initialize a variable. When working with generic types Default() allows you to do so for a variable whose type is generic.
If you wish to replicate the behaviour of Default() do the following:
Finalize(Inst);
FillChar(Inst, SizeOf(Inst), 0);
The call to Finalize is needed in case the type is managed. That is if the type is managed, or contains any members that are managed. Managed types include strings, dynamic arrays, interfaces, variants, anonymous methods etc.
If the type does not contain managed types then the call to Finalize may be omitted. It doesn't hurt to include it though, because the compiler will eliminate it if not needed. If you can be 100% certain that no managed types have been assigned a value then you could also omit that call.
Default initialization means the following:
Zero for numeric types.
The value with ordinal zero for enumerated types.
False for boolean types.
#0 for character types.
The empty string for strings.
The empty variant for Variant.
nil for classes, dynamic arrays, interfaces and anonymous methods.

How to call GetProcessAffinityMask from Delphi XE5

I tried to use the below code snippet to call GetProcessAffinityMask in the windows api.
var
procaffmask,
sysaffmask : DWord;
begin
GetProcessAffinityMask(GetCurrentProcess, procaffmask, sysaffmask);
end;
Upon compilation I got the following error message......
[dcc32 Error] UnitfrmMain.pas(54): E2033 Types of actual and formal var parameters must be identical
The C++ syntax for the API call is below:
BOOL WINAPI GetProcessAffinityMask(
_In_ HANDLE hProcess,
_Out_ PDWORD_PTR lpProcessAffinityMask,
_Out_ PDWORD_PTR lpSystemAffinityMask
);
So then I changed the DWORD to a PDWORD but that did not fix it.
Can anybody tell me how to fix this? I see Delphi code samples around the internet that do not use pointers.
Here is the declaration of GetProcessAffinityMask() in Delphi's Winapi.Windows unit:
function GetProcessAffinityMask(hProcess: THandle;
var lpProcessAffinityMask, lpSystemAffinityMask: DWORD_PTR): BOOL; stdcall;
DWORD and DWORD_PTR are different data types. DWORD_PTR is not a "pointer to a DWORD", it is actually a "pointer-sized DWORD" (4 bytes on a 32bit system, 8 bytes on a 64bit system). Whereas DWORD is always 4 bytes on both 32bit and 64bit systems. Microsoft uses the P prefix to indicate a pointer to a type, and uses the _PTR suffix to indicate the type itself is dependent on the byte size of a pointer.
The Delphi declaration is using var parameters, so you have to match the data type exactly:
var
procaffmask,
sysaffmask : DWORD_PTR;
begin
GetProcessAffinityMask(GetCurrentProcess, procaffmask, sysaffmask);
end;
Even your quote of the C++ declaration shows that the parameters are PDWORD_PTR (pointer to DWORD_PTR).
instead of DWORD use SIZE_T, there will be no error

How to use "Native Wifi API" Windows API functions with Delphi

Im trying to use a function from Windows API on delphi, functions for Windows Wlanapi.dll (Native WIFI API)
WlanOpenHandle
DWORD WINAPI WlanOpenHandle(
__in DWORD dwClientVersion,
__reserved PVOID pReserved,
__out PDWORD pdwNegotiatedVersion,
__out PHANDLE phClientHandle
);
WlanHostedNetworkQueryProperty
DWORD WINAPI WlanHostedNetworkQueryProperty(
__in HANDLE hClientHandle,
__in WLAN_HOSTED_NETWORK_OPCODE OpCode,
__out PDWORD pdwDataSize,
__out PVOID *ppvData,
__out PWLAN_OPCODE_VALUE_TYPE *pWlanOpcodeValueType,
__reserved PVOID pvReserved
);
I trying to use this functions and others for hours, reading the MSDN references and others sites, but I just can't get this working.
My attempt
type
TWlanOpenHandle = function( dwClientVersion:DWORD;
pReserved:Pointer;
pdwNegotiatedVersion:PDWORD;
phClientHandle:PHANDLE
):DWORD; stdcall;
function apiWlanOpenHandle( dwClientVersion:DWORD;
pReserved:Pointer;
pdwNegotiatedVersion:PDWORD;
phClientHandle:PHANDLE
):DWORD;
implementation
function apiWlanOpenHandle ( dwClientVersion:DWORD; pReserved:Pointer; pdwNegotiatedVersion:PDWORD; phClientHandle:PHANDLE ):DWORD;
var
WlanOpenHandle: TWlanOpenHandle;
DLL: Cardinal;
begin
DLL:=LoadLibrary('Wlanapi.dll');
WlanOpenHandle := GetProcAddress(DLL, 'WlanOpenHandle');
if Assigned(WlanOpenHandle) then
begin
WlanOpenHandle(dwClientVersion, pReserved, pdwNegotiatedVersion, phClientHandle);
end
else begin
ShowMessage('Function not found');
end;
end;
I'm trying to translate this API, seems a lot of work, and I'm just a beginner in delphi, I read a lot of stuff on the web, how do I deal with this OpCode parameter, seems a C Struct with constants, and PWLAN_OPCODE_VALUE_TYPE?
http://msdn.microsoft.com/en-us/library/windows/desktop/dd439502(v=vs.85).aspx
You didn't actually show how you called apiWlanOpenHandle which would, I think, explain what the problem is. However, there's one very common mistake that is most likely what is confusing you.
Consider the C declaration of the API:
DWORD WINAPI WlanOpenHandle(
__in DWORD dwClientVersion,
__reserved PVOID pReserved,
__out PDWORD pdwNegotiatedVersion,
__out PHANDLE phClientHandle
);
The parameters that I suspect are causing you problems are the final two. Let us consider pdwNegotiatedVersion. This is a pointer to a DWORD. Because this is an out parameter you must supply a pointer to valid memory. I suspect you are just declaring a variable of type PDWORD and passing that.
var
NegotiatedVersionPtr: PDWORD;
begin
WlanOpenHandle(...., NegotiatedVersionPtr, ...);
The function WlanOpenHandle then de-references that pointer and tries to write to the memory. If you have not given a valid pointer then this will fail.
The naive solution is to change the calling code to look like this:
var
NegotiatedVersion: DWORD;
NegotiatedVersionPtr: PDWORD;
begin
NegotiatedVersionPtr := #NegotiatedVersion;
WlanOpenHandle(...., NegotiatedVersionPtr, ...);
This will work but there is a much cleaner way. Declare the API import like this:
function WlanOpenHandle(
dwClientVersion: DWORD;
pReserved: Pointer;
out NegotiatedVersion: DWORD;
out ClientHandle: THandle
): DWORD; stdcall; external 'Wlanapi.dll';
An out parameter of type DWORD is actually passed as a pointer to the DWORD that you supply as the argument to the function call. You can then change your calling code to look like this:
var
ReturnValue: DWORD;
NegotiatedVersion: DWORD;
ClientHandle: THandle;
begin
ReturnValue := WlanOpenHandle(2, nil, NegotiatedVersion, ClientHandle);
if ReturnValue<>ERROR_SUCCESS then
//respond to error
Note that I have also added some error checking which you really ought to be doing.
The reason that the Windows API function is declared using pointers is that the C language only supports parameter passing by value. It simply does not have pass-by-reference, i.e. out or var in Delphi terms. Languages that do support pass-by-reference should make use of them when they can.
Some Windows API functions have optional parameters declared as pointers. When this is the case passing NULL as the pointer is the way to signal that you do not wish to pass a parameter. Translating those APIs to Delphi is more complex. You need to implement a version using pointers to allow callers to opt-out of supplying the parameter. But it can be helpful to supply an overloaded version that uses out or var for convenience to the caller. The Delphi Windows unit contains many such examples.
As for WlanHostedNetworkQueryProperty, I would declare it like this:
const
// WLAN_HOSTED_NETWORK_OPCODE constants
wlan_hosted_network_opcode_connection_settings = 0;
wlan_hosted_network_opcode_security_settings = 1;
wlan_hosted_network_opcode_station_profile = 2;
wlan_hosted_network_opcode_enable = 3;
// WLAN_OPCODE_VALUE_TYPE constants
wlan_opcode_value_type_query_only = 0;
wlan_opcode_value_type_set_by_group_policy = 1;
wlan_opcode_value_type_set_by_user = 2;
wlan_opcode_value_type_invalid = 3;
function WlanHostedNetworkQueryProperty(
hClientHandle: THandle;
OpCode: Integer;
out DataSize: DWORD;
out Data: Pointer;
out WlanOpcodeValueType: Integer;
Reserved: Pointer
): DWORD; external 'Wlanapi.dll' delayed;
I have used the delayed facility because this is a Windows 7 and up API. You will presumably want your program to run on older versions of Windows and so delay loading is needed. For more information on delay loading in Delphi, see this answer, and particularly the onward links.
Note that the documentation in the MSDN topic to which you link is incorrect. The pWlanOpcodeValueType parameter is declared incorrectly in the MSDN topic. The correct definition, the one to be found in wlanpi.h is this:
__out PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType,

Passing parameters from Delphi 5 to Delphi DLL XE

I have a Delphi 5 application in the application code calls a function in the DLL, passing integer and string parameters, this works well when the DLL is called in a static way, when I try to dynamically change does not work.
which is the correct way to pass parameters to function dynamically?
the code is as follows
main application
function Modulo_Pptos_Operacion(No_Orden : Integer; pathBD : string; PathBDConf : String) : Integer ; stdcall;
external 'LIB_Pptos_Oper.dll';
Modulo_Pptos_Operacion(DmDatos.OrdenesNO_Orden.AsInteger,
DmDatos.CiasPATHA.AsString, 'Alguna String');
DLL
Modulo_Pptos_Operacion function (No_Orden: Integer; PathDB: AnsiString; PathDBConfig: AnsiString): Integer; StdCall;
DYNAMIC CRASH
main application
type
TDLLPpto = function(No_Orden : Integer; PathDB : AnsiString; PathDBConfig : AnsiString) : Integer;
var
DLLHandle: THandle;
: TDLLPpto;
PROCEDURE CALL
DLLHandle := LoadLibrary('LIB_Pptos_Oper.dll');
DLLHandle <> 0 then
begin
#DLLPpto := GetProcAddress(DLLHandle, 'Modulo_Pptos_Operacion');
end;
;
which is the right way?
The problem is probably that you are mixing different runtimes and probably different heaps. Delphi strings are not valid interop types because their implementations vary from version to version.
In this case you can simply switch to using null-terminated strings, PAnsiChar.
In the case of dynamically loaded dll you omitted stdcall; calling convention directive in the declaration of TDLLPpto. Still it is advisable to use PAnsiChar type to pass strings across executable boundaries.
The layout of ansistring has changed with Delphi XE: now there is also a codepage field at negative offset and D5 does not have that. EG: strings from D5 and DXE are utterly incompatible. Thus you should use PAnsiChar or PWideChar in your interface, either zero terminated (Delphi strings are always zero terminated) of introduce an extra parameter with the length if the string might contain #$00 bytes.
Also: the different Delphi versions both have different memory managers. If a string is allocated by the main app and freed by the DLL (strings are reference counted) the pointer get's passed to the wrong memory manager which usually results in corrupted memory and thus nasty Access Violations etc.
Another solution is to use WideString; this is both in D5 en DXE equal to a COM BSTR stringtype and managed by the OS and not the Delphi memory manager. They are compatible. The only problem is: they are slow compared to the Delphi strings and are not ref counted.
In all: when using DLL interfaces, try to avoid string, use PAnsiChar or PWideChar, or WideString

Resources