Translating Vista WinAPI C++ header for Delphi - any suggestions? - delphi

I'm needing to call a Windows API function introduced in Vista from my Delphi app, but I don't have any Delphi headers which describe the function.
Related functions are already documented in the JEDI Windows API library, but not this function.
My C++ is almost non-existent, and I'm struggling to work out the Delphi definitions that correspond to the function and it's parameter as documented on MSDN.
From http://msdn.microsoft.com/en-us/library/aa814417.aspx
NETIOAPI_API GetIpInterfaceEntry(__inout PMIB_IPINTERFACE_ROW Row);
typedef struct _MIB_IPINTERFACE_ROW {
ADDRESS_FAMILY Family;
NET_LUID InterfaceLuid;
NET_IFINDEX InterfaceIndex;
ULONG MaxReassemblySize;
ULONG64 InterfaceIdentifier;
ULONG MinRouterAdvertisementInterval;
ULONG MaxRouterAdvertisementInterval;
BOOLEAN AdvertisingEnabled;
BOOLEAN ForwardingEnabled;
BOOLEAN WeakHostSend;
BOOLEAN WeakHostReceive;
BOOLEAN UseAutomaticMetric;
BOOLEAN UseNeighborUnreachabilityDetection;
BOOLEAN ManagedAddressConfigurationSupported;
BOOLEAN OtherStatefulConfigurationSupported;
BOOLEAN AdvertiseDefaultRoute;
NL_ROUTER_DISCOVERY_BEHAVIOR RouterDiscoveryBehavior;
ULONG DadTransmits;
ULONG BaseReachableTime;
ULONG RetransmitTime;
ULONG PathMtuDiscoveryTimeout;
NL_LINK_LOCAL_ADDRESS_BEHAVIOR LinkLocalAddressBehavior;
ULONG LinkLocalAddressTimeout;
ULONG ZoneIndices[ScopeLevelCount];
ULONG SitePrefixLength;
ULONG Metric;
ULONG NlMtu;
BOOLEAN Connected;
BOOLEAN SupportsWakeUpPatterns;
BOOLEAN SupportsNeighborDiscovery;
BOOLEAN SupportsRouterDiscovery;
ULONG ReachableTime;
NL_INTERFACE_OFFLOAD_ROD TransmitOffload;
NL_INTERFACE_OFFLOAD_ROD ReceiveOffload;
BOOLEAN DisableDefaultRoutes;
}MIB_IPINTERFACE_ROW, *PMIB_IPINTERFACE_ROW;
Among other bits, the bit I'm struggling with at the minute is the ZoneIndices[ScopeLevelCount] field; I can't work out what size the array is supposed to be.
This is what I've defined so far, although I haven't worked out the enums in the original C++ definition yet. I'll be explicitly loading the Windows DLL on Vista and getting the address of the new function to call.
type
PMIB_IPINTERFACE_ROW = ^MIB_IPINTERFACE_ROW;
{$EXTERNALSYM PMIB_IPINTERFACE_ROW}
_MIB_IPINTERFACE_ROW = record
Family: ADDRESS_FAMILY;
InterfaceLuid: NET_LUID;
InterfaceIndex: NET_IFINDEX;
MaxReassemblySize,
InterfaceIdentifier,
MinRouterAdvertisementInterval,
MaxRouterAdvertisementInterval: Cardinal;
AdvertisingEnabled,
ForwardingEnabled,
WeakHostSend,
WeakHostReceive,
UseAutomaticMetric,
UseNeighborUnreachabilityDetection,
ManagedAddressConfigurationSupported,
OtherStatefulConfigurationSupported,
AdvertiseDefaultRoute: LongBool;
RouterDiscoveryBehavior: NL_ROUTER_DISCOVERY_BEHAVIOR;
DadTransmits,
BaseReachableTime,
RetransmitTime,
PathMtuDiscoveryTimeout: Cardinal;
LinkLocalAddressBehavior: NL_LINK_LOCAL_ADDRESS_BEHAVIOR;
LinkLocalAddressTimeout,
ZoneIndices[ScopeLevelCount],
SitePrefixLength,
Metric,
NlMtu: Cardinal;
Connected,
SupportsWakeUpPatterns,
SupportsNeighborDiscovery,
SupportsRouterDiscovery: LongBool;
ReachableTime: Cardinal;
TransmitOffload: NL_INTERFACE_OFFLOAD_ROD;
ReceiveOffload: NL_INTERFACE_OFFLOAD_ROD;
DisableDefaultRoutes: LongBool;
end;
{$EXTERNALSYM _MIB_IPINTERFACE_ROW}
MIB_IPINTERFACE_ROW = _MIB_IPINTERFACE_ROW;
{$EXTERNALSYM MIB_IPINTERFACE_ROW}
TMibIpInterfaceRow = MIB_IPINTERFACE_ROW;
PMibIpInterfaceRow = PMIB_IPINTERFACE_ROW;
const
iphlpapilib = 'iphlpapi.dll';
var
HIpHlpApi: THandle = 0;
GetIpInterfaceEntry: function(const pArpEntry: MIB_IPINTERFACE_ROW): LongInt; stdcall;
{$EXTERNALSYM GetIpInterfaceEntry}
Does anybody out there have suggestions or tips/tricks for translating a function definition like this?
Many thanks,
Conor

The Win32 BOOLEAN type is one byte, but Delphi's LongBool type is four. Use Delphi's ByteBool instead.

Maybe you can try "C 2 pas"
http://cc.embarcadero.com/Item/26951
Also found these tools:
http://www.drbob42.com/delphi/headconv.htm
http://rvelthuis.de/programs/convertpack.html
http://rvelthuis.de/articles/articles-convert.html#cconvs

Hmm. A strange structore. But if you look into w2def.h, you can see that ScopeLevelCount=16. So your array should have 16 elements,

Make sure you use the packed command when defining your record because Delphi aligns complex data types on 2, 4 or 8 byte boundaries by default.
TExample = record
f1: Integer; // start at offset 0x00
f2: Char; // start at offset 0x04
f3: Integer; // start at offset 0x06 or 0x08 depending on alignment
end;
TExample = packed record // this is what c++ would do
f1: Integer; // start at offset 0x00
f2: Char; // start at offset 0x04
f3: Integer; // start at offset 0x05
end;

The ZoneIndices array should be defined like this:
ZoneIndices : array [0..ScopeLevelCount - 1] of Cardinal;
ScopeLevelCount is a constant that is 16

I often used Dr. Bob's Header Converter for this task, but mostly I took the long and tedious way of manually performing the conversion, because it helped me to understand the API better. (Because I was going to use it and I have to familiarize myself with it anyway)
Also see this article: http://www.delphi-jedi.org/api-howto.html
In general it's best not to put to much hopes on a header conversion tool, because C++ is a very powerful and complex language. Most conversion tools I tested only supported a subset of C++ and its preprocessor, thus making it necessary to manually make fixes to the generated source.

you can investigate some already translated APIs/structures that related to this subject and convert it yourself. for example this one

I don't see any field of type ULONG64 in the translation, but I do in the original.
Further, FPC also has an header converter which works fine for structs. Typically windows api structures are packed.
In doubt, use some free msvc product to write the sizeof(structure) and do the same under pascal. If that matches, and you still have doubts, calculate field offsets with a bit of pointer magic and compare them.

Related

Delphi: add constant to TFileTime

I am new in Delphi and I need in my project add some constant to TFileTime which is record of lower and upper part of 64bit value. How to do this in Delphi? I have found only code in C++, but i dont know how make in Delphi unsigned int64 (ULONGLONG) and also I dont know how to cast this to longword (DWORD):
ULONGLONG qwResult;
// Copy the time into a quadword.
qwResult = (((ULONGLONG) ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
// Add constant
qwResult += constant;
// Copy the result back into the FILETIME structure.
ft.dwLowDateTime = (DWORD) (qwResult & 0xFFFFFFFF );
ft.dwHighDateTime = (DWORD) (qwResult >> 32 );
Thanks
The FILETIME struct is defined as:
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;
So, because Windows runs on little Endian, the layout of this struct is compatible with a 64 bit integer value.
So, you can cast TFileTime to UInt64, do the arithmetic, and cast back. Like this:
function IncrementedFileTime(const Value: TFileTime; const Incr: UInt64): TFileTime;
begin
Result := TFileTime(UInt64(Value) + Incr);
end;
Now, the documentation for the FILETIME record says:
It is not recommended that you add and subtract values from the
FILETIME structure to obtain relative times. Instead, you should copy
the low- and high-order parts of the file time to a ULARGE_INTEGER
structure, perform 64-bit arithmetic on the QuadPart member, and copy
the LowPart and HighPart members into the FILETIME structure.
Do not cast a pointer to a FILETIME structure to either a
ULARGE_INTEGER* or __int64* value because it can cause alignment
faults on 64-bit Windows.
This is an issue on targets where alignment errors result in hard faults. For instance Itanium. However, on x86 and x64 the code in my answer is fine because those architectures do not issue hard faults for alignment errors. Which is just as well because the Delphi compiler isn't very good at alignment.

Can I force `const` to pass by reference (aka the missing `in` parameter)

Delphi has:
var : pass by reference; parameter is both input and output.
out : pass by reference; parameter is output only.
const: pass by ..... well it depends; parameter is input only.
in : pass by reference; parameter is input only and will not be changed there is no "in".
I don't mind that there is no spoon, but I miss in; considering the following code, is there a cleaner way of doing this?
type TFastDiv = record
strict private
FBuffer: Int64;
other fields
....
//Must be `var` because `const` would pass a Int64 by value
// |||
// VVV
function DivideFixedI32(var Buffer: Int64; x: integer): integer;
asm
mov r9,rcx
imul dword ptr[rcx] // do stuff with the buffer
..
mov ecx, [r9+4] // do other stuff with the rest of the buffer
{Changing the code to imul ecx;...;shr r9,32;mov ecx,r9d would allow pass by value, but let's assume the code must not be changed.}
class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
Result:= DivideFixedI32(Int64((#buffer.FBuffer)^), abs(x)); <<-- Ugly
if (x < 0) then Result:= - Result;
end;
DivideFixed will never change the buffer. The whole point of the routine is that buffer is a precalculated value that does not change.
In the class operator I declare buffer as const, because the record must not change.
The question is:
If I insist on declaring the buffer parameter in IntDivide as const is there a cleaner way of coding or am I stuck in the pointer_to/points_to hack?
Newer compiler versions (from XE3 onwards) support the [Ref] decorator:
procedure Foo(const [Ref] Arg1: Integer; [Ref] const Arg2: Byte);
Example adapted from the documentation, which emphasises the [Ref] can go either before or after the const keyword.
The only option (pre Delphi XE3) if you want to ensure pass-by-reference, is to pass something big.
i.e. bigger than sizeof(pointer)
type TFastDiv = record
strict private
FBuffer: Int64; <<-- make sure this is the first member
other fields
....
function DivideFixedI32(const Buffer: TFastDiv; x: integer): integer;
...
class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
Result:= DivideFixedI32(buffer, abs(x));
This line in the Delphi help file:
Sets, records, and static arrays of 1, 2, or 4 bytes are passed as 8-bit, 16-bit, and 32bit values. Larger sets, records, and static arrays are passed as 32-bit pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest double-word boundary.
is misleading and should be changed to/read as:
Sets, records, and static arrays up to SizeOf(pointer) are passed as 8-bit, 16-bit, and 32bit values (64 bit values on x64). Sets, records, and static arrays larger than SizeOf(Pointer) are passed as pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest SizeOf(pointer) boundary.

LARGE_INTEGER or TLargeInteger?

In Windows.pas, there are:
LARGE_INTEGER = record
case Integer of
0: (
LowPart: DWORD;
HighPart: Longint);
1: (
QuadPart: LONGLONG);
end;
TLargeInteger = Int64;
I see several Windows functions and structure members which is originally declared as LARGE_INTEGER has been translated to TLargeInteger such as:
function QueryPerformanceCounter(var lpPerformanceCount: TLargeInteger): BOOL;
stdcall;
and another example is:
WIN32_STREAM_ID = record
dwStreamId : DWORD;
dwStreamAttributes: DWORD;
Size : TLargeInteger;
dwStreamNameSize : DWORD;
cStreamName : array[0..0] of WCHAR;
end;
Can TLargeInteger acts as a replacement of LARGE_INTEGER for every function parameters and structure members found in Windows header files?
You can always safely use these two types interchangeably in API translations. Although, clearly, once you have selected one type for a particular function, you have to stick to that type whenever you call that function.
Using TLargeInteger makes it easier to assign values because there's no need to refer to a record field.
Using LARGE_INTEGER makes it easier to separate into low and high 32 bit parts.
Now that the compiler has good support for 64 bit integers, it probably makes more sense to use TLargeInteger. Because, usually, there is no need to separate the 64 bit integer into its low and high parts. But way back when the compiler couldn't handle 64 bit integer types, there was no other option to work with 64 bit integers.

How to define an unsigned 64-bit integer in Delphi7?

In Delphi 7, int64s are signed, if I try to declare a hex constant larger than $8000000000000000 (eg, what is really an uint64) I get an error. Can you advise some workarounds, please?
You can make a variant record like so
type muint64 = record
case boolean of
true: (i64 : int64);
false:(lo32, hi32: cardinal);
end;
Now you can just use the cardinals to fill your uint64 with unsigned data.
The other option would be to use code like this:
const almostmaxint64 = $800000045000000;
var muint64: int64;
begin
muint64:= almostmaxint64;
muint64:= muint64 shl 1;
end
Without support from the compiler you don't have many options.
I'm presuming that you wish to pass a value to a function in some external DLL. You'll have to declare the parameter as a signed 64 bit integer, Int64. Then all you can do is pass in the signed value that has the same bit pattern as the desired unsigned value. Build yourself a little converter tool with a compiler that has support for unsigned 64 bit integers.
Traditionally, Broland implementations suffered interoperability issues because lack of largest unsigned supported by target platform. I remember using LongInt values instead of DWORD and waiting for troubles since very early days of Turbo Pascal for Windows. Then was Cardinal happiness, but no, D4 introduced largest integer Int64 in its signed form only. Again.
So your only option is to rely on signed fundamental type Int64 and pray... wait, no, just use Int64Rec typecast to perform arithmetics on least and most significant part separately.
Back to constant declaration:
const
foo = $8000004200000001; // this will work because hexadecimal notation is unsigned by its nature
// however, declared symbol foo becomes signed Int64 value
// attempting to use decimal numeral will result in "Integer constant too large" error
// see "True constants" topic in D7 Help for more details
procedure TForm1.FormCreate(Sender: TObject);
begin
// just to verify
Caption := IntToHex(foo, SizeOf(Int64) * 2);
end;
Unfortunately, the other workaround is to change your compiler. Free Pascal always keeps signed and unsigned types in sync.
This snippet compiles and yields correct result in Borland Delphi Version 15.0 (a.k.a Delphi 7).

How are the basic Delphi types related to each other?

Delphi has long supported a few basic numeric types and I was wondering how they are related to each other.
In Delphi 2007 I found these declarations (some are conflicting, some are mere aliasses) :
Types.pas:
DWORD = LongWord;
Largeint = Int64;
getmem.inc:
DWORD = Integer;
Windows.pas:
DWORD = Types.DWORD;
SHORT = Smallint;
UINT = LongWord;
ULONG = Cardinal;
LONGLONG = Int64;
TLargeInteger = Int64;
ULONGLONG = UInt64;
This leads me into thinking the base signed numeric types are SmallInt, Integer and Int64. Unsigned, there's Byte, WORD and UInt64. But what is the difference between Cardinal and LongWord? (By the way, what's the original and intended casing for these types?)
And is there a type for signed 8 bit integers (Int8)?
// Int8 = ?unknown?;
UInt8 = Byte;
Int16 = SmallInt;
UInt16 = Word;
Int32 = Integer;
UInt32 = LongWord;
// Int64 already exists
// UInt64 already exists
Lastly, how should I define Int and UInt, especially with regard to C/C++ compatibility and a future switch to other platforms (possibly also 64 bit)? (A related question is, of course, how will the various numeric types be defined in 64-bit Delphi?)
The signed one-byte integer type is ShortInt. You can remember its size by the fact that it's not the same size as usual C implementations of the short type.
As for capitalization, capitalize the first letter. The documentation tends to leave the "int" part at the end lowercase, as in Longint, but I think it's more common to capitalize it. Don't write the types in all capitals unless you're using Platform SDK types and you want your code to show its C roots; otherwise I'd just write Word and DWord, Long and ULong, etc.)
Delphi 2009, perhaps earlier, already defines types like Int8 and UInt32. As for how to define Int and UInt, I'd say don't. The language you're using already defines Integer and Cardinal; don't introduce new type names when you don't have to. Keep the names you already have, and then everyone else will know what you're talking about. (Besides, Int is already a function in the System unit.)
Use Cardinal when you want an unsigned type and don't care about its size; use LongWord when the variable must be exactly four bytes. Likewise for Integer and LongInt. Use Cardinal when you want a four-byte unsigned type; use LongWord when you want a generic unsigned type and don't care about the size. Likewise for Integer and LongInt, nowadays. If you're writing 16-bit code, use LongInt when you need four bytes and use Integer when you don't care about the size; Cardinal and LongWord didn't exist in Delphi's and Turbo Pascal's 16-bit days.
The common wisdom for years had been that Integer and Cardinal would become 64-bit types on a 64-bit compiler, but that is apparently not the case. Instead, they will remain 32-bit types, just as their counterparts in Microsoft C++ do. Furthermore, there will be a new type, NativeInt, which will be a 64-bit type in a 64-bit compiler. The LongInt and LongWord types will become 64-bit types because they have always been the same size as the Pointer type, which was 32 bits even in 16-bit times.
UInt8 = Byte
Int8 = ShortInt
UInt16 = Word
Int16 = SmallInt
UInt32 = LongWord
Int32 = LongInt
UInt64 = UInt64
Int64 = Int64
int = Integer
uint = Cardinal
NativeInt (generic, depends on CPU register size)
NativeUInt (generic, depends on CPU register size)
Cardinal and Integer are generic types. For 16 bit they were 16 byte large and for 32 bit they are 32 bit large. For 64 bit the Windows 64bit platform (LLP64) defines them as 32 bit. The new NativeInt and NativeUInt types are now the CPU register sized types.
Cardinal and Integer are aliases.
Cardinal ==> LongWord (unsigned)
Integer ==> LongInt (signed)
To get "the original and intended casing" press Ctrl-Space, Return after you typed a type name (i.e. use code completion).

Resources