Need convert VC code to Delphi - delphi

I need call a DLL file in my delphi code, here is the code snippet of the DLL Head file:
#define BookInfoDLL __declspec(dllexport)
struct _BookTime
{
unsigned char day;
unsigned char month;
unsigned short year;
};
struct _stBookData
{
unsigned char encrypt;
_BookTime bkTime;
unsigned int PageCount;
};
int BookInfoDLL UpdateBooks(const char * const pBookID,
const char cBookTypeWord,
const _stBookData * const pBookData,
const int nBookDataCounter);
I need invoke the dll function "UpdateBooks" in my delphi code.
How can I convert those code into delphi? Thank you!

Use h2pas! Although it is a freepascal tool, it should produce Delphi compatible code.

Snippet for non-managed Delphi code (not tested, but compiles and changed according suggestions in comments):
interface
type
TBookTime = packed record
day : byte; // unsigned 8-bit
month : byte;
year : word; // unsigned 16-bit
end;
TBookData = packed record
encrypt : byte;
bkTime : TBookTime;
PageCount : LongWord; // unsigned 32-bit
end;
TBookDataPtr = ^TBookData;
function UpdateBooks(
pBookID : PChar;
cBookTypeWord : byte;
pBookData : TBookDataPtr;
nBookDataCounter : integer
) : integer; stdcall; external 'dll_file_name.dll' name 'UpdateBooks';
implementation
// ...
end;
Simple call UpdateBooks(...) from delphi code.
Update: code changed, thanks for commenting!
Below is snippets for sample calls ...
Common functions and constants for all snippets:
// --- Test data fill utility and constants -----------------------------------
const
BOOK_ID = 'Test Book ID';
BOOK_TYPE_WORD = 'T';
BOOK_DATA_COUNT = 5;
procedure FillTestBookData(pBookData : TBookDataPtr; iTestNum : integer);
begin
if(pBookData = nil) then exit;
pBookData^.encrypt := iTestNum;
pBookData^.bkTime.day := iTestNum;
pBookData^.bkTime.month := iTestNum;
pBookData^.bkTime.year := 2000 + iTestNum;
pBookData^.PageCount := iTestNum;
end;
Calling function in common Delphi style:
// --- Test procedure in Delphi style -----------------------------------------
procedure TestBookUpdate_DelphiStyle;
var
bookArray : array of TBookData;
iBookNumber : integer;
begin
SetLength(bookArray, BOOK_DATA_COUNT);
try
for iBookNumber := Low(bookArray) to High(bookArray) do begin
FillTestBookData( #(bookArray[iBookNumber]), iBookNumber );
end;
UpdateBooks(
PChar(BOOK_ID), ord(BOOK_TYPE_WORD),
#(bookArray[Low(bookArray)]), BOOK_DATA_COUNT
);
finally
SetLength(bookArray, 0); // no explicit requirement to include in code
end;
end;
Bonus: same test calls in C-style and Pascal-style :-)
// --- Test procedure in Old Delphi (plain Pascal) style ----------------------
type
TBookDataOldArray = array[0..0] of TBookData;
TBookDataOldArrayPtr = ^TBookDataOldArray;
// Store range checking compiler option state
{$IFOPT R+}
{$DEFINE RANGE_CHECK_ON}
{$ENDIF}
procedure TestBookUpdate_OldDelphiStyle;
var
bookArrayPtr : TBookDataOldArrayPtr;
iBookNumber : integer;
begin
GetMem(bookArrayPtr, BOOK_DATA_COUNT*sizeof(TBookData));
try
// Disable range checking compiler option
{$R-}
for iBookNumber := 0 to BOOK_DATA_COUNT - 1 do begin
FillTestBookData(#(bookArrayPtr^[iBookNumber]), iBookNumber);
end;
// Restore range checking compiler option if turned on before disabling
{$IFDEF RANGE_CHECK_ON}{$R+}{$ENDIF}
UpdateBooks(
PChar(BOOK_ID), ord(BOOK_TYPE_WORD), TBookDataPtr(bookArrayPtr), BOOK_DATA_COUNT
);
finally
FreeMem(bookArrayPtr);
end;
end;
// --- Test procedure in C style ---------------------------------------------
procedure TestBookUpdate_CStyle;
var
bookArrayPtr : TBookDataPtr;
curBookPtr : TBookDataPtr;
curBookNumber : integer;
begin
bookArrayPtr := AllocMem( BOOK_DATA_COUNT * sizeof(TBookData) );
try
curBookNumber := 0;
curBookPtr := bookArrayPtr;
while(curBookNumber < BOOK_DATA_COUNT) do begin
FillTestBookData( curBookPtr, curBookNumber );
inc(curBookNumber);
inc(curBookPtr, 1);
// Another pointer increment solution is :
// curBookPtr := PChar(curBookPtr) + sizeof(TBookData);
end;
UpdateBooks( PChar(BOOK_ID), ord(BOOK_TYPE_WORD), bookArrayPtr, BOOK_DATA_COUNT );
finally
FreeMem(bookArrayPtr);
end;
end;

I have ended my first C header conversion yesterday. The articles and tools from TeamB member Rudy Velthuis were very, very helpful for me, specially
Pitfalls of converting
Conversion Helper Package

Related

Delphi Convert Set Of Bits as TBits to Integer or unsigned int

Ihave value from nor or xor gate with represented as TBits and i want to convert it to generic variable like integer or unsigned integer my current working ide Tokyo 10.2
var
ABits: TBits;
AComulative: UInt32;
const
PosBitFromSensor1 = 0;
PosBitFromSensor2 = 1;
begin
ABits := TBits.Create;
try
ABits.Size := 32;
{GetValFromSensor return Boolean type}
ABits.Bits[PostBitFromSensor1] := GetValFromSensor(PosBitFromSensor1);
ABits.Bits[PostBitFromSensor2] := GetValFromSensor(PosBitFromSensor2);
AComulative := SomeBitsConvertToInteger(ABits); {some function like this}
finally
ABits.Free;
end;
end;
or any simple solution.
It won't be very fast but you can do regular bit manipulation, set each bit that corresponds to a "true" in the boolean array . For example:
function SomeBitsConvertToInteger(ABits: TBits): UInt32;
var
i: Integer;
begin
if ABits.Size <> SizeOf(Result) * 8 then
raise EBitsError.Create('Invalid bits size');
Result := 0;
for i := 0 to Pred(SizeOf(Result) * 8) do
Result := Result or UInt32((Ord(ABits[i]) shl i));
end;
this solution provided by #Victoria and #LURD it maybe usefull for the other that have same solving problem. sorry about my English.
type
TBitsHelper = class helper for TBits
public
function ToUInt32: UInt32;
end;
{ TBitsHelper }
function TBitsHelper.ToUInt32: UInt32;
type
PUInt32 = ^UInt32;
begin
if Size > SizeOf(Result) * 8 then
raise EOverflow.Create('Size overflow!');
with Self do
Result := PUInt32(FBits)^;
end;
maybe something like this :
type
{$IF CompilerVersion > 32} // tokyo
{$MESSAGE Fatal 'Check if TBits still has the exact same fields and adjust the IFDEF'}
{$ENDIF}
TPrivateAccessBits = class
public
FSize: Integer;
FBits: Pointer;
end;
Move(#TPrivateAccessBits(ABits).FBits, AComulative, sizeOf(AComulative));

Accessing OleVariant containing VT_ARRAY of VT_RECORD from Delphi

Using Delphi, I need to access a OleVariant containing one or more records in an array.
The method I call returns a VT_ARRAY of VT_RECORD, and the records themselves are defined as:
struct StreamTimeInfo {
unsigned int PID;
LONGLONG PTS;
LONGLONG TimeStamp;
};
My code is like this:
procedure Test;
type
TStreamInfo = record
PID: Cardinal;
PTS: Int64;
TimeStamp: Int64;
end;
var
Value: OleVariant
StreamTime: TStreamInfo;
begin
GetValue(Value); // Value holds a VT_ARRAY of VT_RECORD
// How should I access the array of records in Delphi?
// I've tried this to get to the first element:
StreamTime := TStreamInfo(TVarData(Value).VPointer^);
end;
I do not understand how to access the records from Delphi.
Any input is greatly appreciated.
I've never done this before, but I think this should work.
type
TStreamInfoArray = array [0..MaxArrayCount-1] of TStreamInfo;
PStreamInfoArray = ^TStreamInfoArray;
var
Value: Variant;
p: PStreamInfoArray;
StreamInfo: TStreamInfo;
begin
GetValue(Value);
p := PStreamInfoArray(VarArrayLock(Value));
try
StreamInfo := p^[Index];
finally
VarArrayUnlock(Value);
end;
end;
For future reference and for others, here is the final working code:
// Original C-Source definition of StreamTimeInfo
// import "oaidl.idl";
// import "ocidl.idl";
// [uuid(A5AA2ACD-BEA0-4570-9232-D8301A6DAE0F)]
// struct StreamTimeInfo {
// unsigned int PID;
// LONGLONG PTS;
// LONGLONG TimeStamp;
// };
// cpp_quote("typedef struct StreamTimeInfo StreamTimeInfo;")
procedure GetStreamTimes;
type
TStreamTimeInfo = record
PID: Cardinal;
PTS: Int64;
TimeStamp: Int64;
end;
TStreamTimeInfoArray = array[0..31] of TStreamTimeInfo;
PStreamTimeInfoArray = ^TStreamTimeInfoArray;
var
Value: OleVariant;
SizeOfArray: Integer;
PtrToArray: PStreamTimeInfoArray;
begin
GetValue(EMPGPDMX_STREAMTIMES, Value);
if VarArrayDimCount(Value) = 1 then
begin
SizeOfArray := 1 + VarArrayHighBound(Value, 1) - VarArrayLowBound(Value, 1);
PtrToArray := PStreamTimeInfoArray(VarArrayLock(Value));
try
for I := 0 to SizeOfArray - 1 do
begin
StreamTimeInfo := PtrToArray^[I];
// Usage Sample:
// FStatus.StreamTimePID[I] := StreamTimeInfo.PID;
// FStatus.StreamTimePTS[I] := StreamTimeInfo.PTS;
// FStatus.StreamTimeTS[I] := StreamTimeInfo.TimeStamp;
end;
finally
VarArrayUnlock(Value);
end;
end;
end;

How to fix this EInvalidPointer error while translating Free Pascal into Delphi?

We are tring to use in delphi a pas file generated by Free Pascal. The link is at:
http://www.markwatson.com/opensource/FastTag_Pascal.zip
While testing, it prompts InValidPointer. Please look at the following error line in debugger.
interface
procedure ReadLexicon;
type sarray = array[1..12] of string;
type big_sarray = array[1..1000] of string; { used for word lists and tags: limit on size of input text }
type psarray = ^sarray;
{function GetTagList(word: string): psarray;}
procedure TagWordList(wordList : big_sarray; var tags : big_sarray);
implementation
uses SysUtils, Classes;
{ Hash Table Support - copied from FreePascal source: benchmork shootout examples }
type
THashEntryPtr = ^THashEntryRec;
THashEntryRec = record
name : string;
number : psarray;
next : THashEntryPtr;
end;
const
TABLE_SIZE = 100000;
...
...
...
function GetTagList(word: string): psarray;
var
ret : psarray;
ok : boolean;
begin
ok := localHash.fetch(word, ret);
if ok then GetTagList := ret else GetTagList := nil;
end;
procedure TagWordList(wordList : big_sarray; var tags : big_sarray);
var i : integer;
x : real;
psa : psarray;
lastThreeChars : string;
lastTwoChars : string;
lastChar : string;
firstTwoChars : string;
tagFirstTwoChars : string;
begin
for i := 0 to length(wordList) do
begin
**psa := GetTagList(wordList[i]);///// EInvalidPointer ERROR**
if psa <> nil then tags[i] := psa^[1] else tags[i] := '???';
end;
...
...
...
How can we fix it.
Thank you very much in advance.
The original source doesn't set any compiler mode, and so the default TP like mode is active, meaning string=shortstring.
Replace, in the entire source string with shortstring and it will probably work.
There are at least two errors I can find in the TagWordList procedure.
for i := 0 to length(wordList) do, the array is 1-based so the loop has to start with 1.
A bit later there is a check if i > 0 that fails for the same reason.
It could also be a wrong definition of the type big_sarray = array[1..1000] of string;. If you change that to a 0-based array it might work.

Call dll function

I have to acces a c written dll function, prototyped as:
#include "extcode.h"
#pragma pack(push)
#pragma pack(1)
#ifdef __cplusplus
extern "C" {
#endif
void __stdcall PSA_Send_BO(char hostname[], char BO_NumberIn[],
char BO_NumberOut[], int16_t *Status, char Error_text[], int32_t *Error_code,
int32_t *length_BO_NumberOut, int32_t *length_error_text);
long __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma pack(pop)
My Delphi code:
procedure Aaa(HostNaam: PChar; BO_To: PChar; BO_From: PChar; ErrorText: PChar;
var OutputLength: LongInt; var ErrorLength: LongInt;
var ErrorNumber: Longint
) ; far; stdcall; external 'aaa.dll'
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
BO_From, ErrorText: Array[0..999] of Char;
ErrorLength, BoLength, ErrorNumber: LongInt;
begin
Aaa(PChar(edtHostName.Text), PChar(edtBoNumber.Text), BO_From, ErrorText, BoLength, ErrorLength, ErrorNumber);
Label1.Caption := 'BO_From = ' + BO_From ;
Label2.Caption := 'BoLength = ' + IntToStr(BoLength);
Label3.Caption := 'ErrorText = ' + ErrorText;
Label4.Caption := 'ErrorLength = ' + IntToStr(ErrorLength);
Label5.Caption := 'ErrorNumber = ' + IntToStr(ErrorNumber);
end;
When I run this example, the returned strings BO_From and ErrorText are empty, all other returned parameters are OK.
When I comment one of the lines out where I do the display of the returned parameters, the strings are displayed well!
Stepping into the code with the debugger has similar effect.
Copying all returned parameters before displaying them has no effect.
The length of the returned strings is far below the declared size.
Does someone has any clue?
Thanks in advance for any help,
Cock
You've got a missing var Status: Smallint in the declaration of procedure Aaa.
As Sertac Akyuz mentioned, you have a missing Status parameter, and since stdcall parameters are passed right-to-left (http://docwiki.embarcadero.com/RADStudio/en/Procedures_and_Functions#Calling_Conventions), any parameters declared before this missing parameter will be corrupted.
If you want the code to function on Delphi 2009+ you should also convert PChar => PAnsiChar, and Char => AnsiChar, since SizeOf(Char)=2 on Delphi 2009+.
The "far" directive is also obsolete.
procedure Aaa(HostNaam: PAnsiChar; BO_To: PAnsiChar; BO_From: PAnsiChar;
var Status: SmallInt; ErrorText: PAnsiChar;
var OutputLength: LongInt; var ErrorLength: LongInt;
var ErrorNumber: Longint
) ; stdcall; external 'aaa.dll';
Without seeing the details of the dll, it is hard to say exactly what is going on. One thing, though ...
Do you need to set ErrorLength and BOLength? Usually on calls like this these are filled in with the size of the buffer on the call. That allows the dll to avoid any kind of buffer overrun. So try, setting them to 999 before making the call.

How to simulate bit-fields in Delphi records?

I would like to declare a record in Delphi that contains the same layout as it has in C.
For those interested : This record is part of a union in the Windows OS's LDT_ENTRY record. (I need to use this record in Delphi because I'm working on an Xbox emulator in Delphi - see project Dxbx on sourceforge).
Anyway, the record in question is defined as:
struct
{
DWORD BaseMid : 8;
DWORD Type : 5;
DWORD Dpl : 2;
DWORD Pres : 1;
DWORD LimitHi : 4;
DWORD Sys : 1;
DWORD Reserved_0 : 1;
DWORD Default_Big : 1;
DWORD Granularity : 1;
DWORD BaseHi : 8;
}
Bits;
As far as I know, there are no bit-fields possible in Delphi. I did try this:
Bits = record
BaseMid: Byte; // 8 bits
_Type: 0..31; // 5 bits
Dpl: 0..3; // 2 bits
Pres: Boolean; // 1 bit
LimitHi: 0..15; // 4 bits
Sys: Boolean; // 1 bit
Reserved_0: Boolean; // 1 bit
Default_Big: Boolean; // 1 bit
Granularity: Boolean; // 1 bit
BaseHi: Byte; // 8 bits
end;
But alas: it's size becomes 10 bytes, instead of the expected 4.
I would like to know how I should declare the record, so that I get a record with the same layout, the same size, and the same members. Preferrably without loads of getter/setters.
TIA.
Thanks everyone!
Based on this information, I reduced this to :
RBits = record
public
BaseMid: BYTE;
private
Flags: WORD;
function GetBits(const aIndex: Integer): Integer;
procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
BaseHi: BYTE;
property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;
The index is encoded as follows : (BitOffset shl 8) + NrBits. Where 1<=NrBits<=32 and 0<=BitOffset<=31
Now, I can get and set these bits as follows :
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
Offset: Integer;
NrBits: Integer;
Mask: Integer;
begin
NrBits := aIndex and $FF;
Offset := aIndex shr 8;
Mask := ((1 shl NrBits) - 1);
Result := (Flags shr Offset) and Mask;
end;
procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
Offset: Integer;
NrBits: Integer;
Mask: Integer;
begin
NrBits := aIndex and $FF;
Offset := aIndex shr 8;
Mask := ((1 shl NrBits) - 1);
Assert(aValue <= Mask);
Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;
Pretty nifty, don't you think?!?!
PS: Rudy Velthuis now included a revised version of this in his excellent "Pitfalls of converting"-article.
Rudy's Delphi Corner is the best resource I know of regarding Delphi and C/C++ interoperability. His Pitfalls of conversion is pretty much a must read when using C/C++ APIs in Delphi. The chapter you'll be most interested in is Records and alignment -> Bitfields, but I urge you to read the entire thing top to bottom, twice. The other articles are definitely worth the time investment, too.
Ok, my bit manipulation is a bit rusty, so I could have reversed the bytes. But the code below gives the general idea:
type
TBits = record
private
FBaseMid : Byte;
FTypeDplPres : Byte;
FLimitHiSysEa: Byte;
FBaseHi : Byte;
function GetType: Byte;
procedure SetType(const AType: Byte);
function GetDpl: Byte;
procedure SetDbl(const ADpl: Byte);
function GetBit1(const AIndex: Integer): Boolean;
procedure SetBit1(const AIndex: Integer; const AValue: Boolean);
function GetLimitHi: Byte;
procedure SetLimitHi(const AValue: Byte);
function GetBit2(const AIndex: Integer): Boolean;
procedure SetBit2(const AIndex: Integer; const AValue: Boolean);
public
property BaseMid: Byte read FBaseMid write FBaseMid;
property &Type: Byte read GetType write SetType; // 0..31
property Dpl: Byte read GetDpl write SetDbl; // 0..3
property Pres: Boolean index 128 read GetBit1 write SetBit1;
property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15
property Sys: Boolean index 16 read GetBit2 write SetBit2;
property Reserved0: Boolean index 32 read GetBit2 write SetBit2;
property DefaultBig: Boolean index 64 read GetBit2 write SetBit2;
property Granularity: Boolean index 128 read GetBit2 write SetBit2;
property BaseHi: Byte read FBaseHi write FBaseHi;
end;
function TBits.GetType: Byte;
begin
Result := (FTypeDplPres shr 3) and $1F;
end;
procedure TBits.SetType(const AType: Byte);
begin
FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3);
end;
function TBits.GetDpl: Byte;
begin
Result := (FTypeDplPres and $06) shr 1;
end;
procedure TBits.SetDbl(const ADpl: Byte);
begin
FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1);
end;
function TBits.GetBit1(const AIndex: Integer): Boolean;
begin
Result := FTypeDplPres and AIndex = AIndex;
end;
procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean);
begin
if AValue then
FTypeDblPres := FTypeDblPres or AIndex
else
FTypeDblPres := FTypeDblPres and not AIndex;
end;
function TBits.GetLimitHi: Byte;
begin
Result := (FLimitHiSysEa shr 4) and $0F;
end;
procedure TBits.SetLimitHi(const AValue: Byte);
begin
FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4);
end;
function TBits.GetBit2(const AIndex: Integer): Boolean;
begin
Result := FLimitHiSysEa and AIndex = AIndex;
end;
procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean);
begin
if AValue then
FLimitHiSysEa := FLimitHiSysEa or AIndex
else
FLimitHiSysEa := FLimitHiSysEa and not AIndex;
end;
Well, you basically need to get down to the dirty with bit-manipulation.
Why, specifically, do you need to retain that structure?
If you only need to talk to a legacy program that either talks in this dialect (TCP/IP or similar), or stores data in this manner (files, etc.), then I would map a normal Delphi structure to a bit-version compatible. In other words, I would use a normally structured Delphi structure in memory, and write code to write and read that structure in a compatible manner.
If you need to save memory, I would make getters and setters that manipulate bits of internal integers or similar. This will have a performance impact, but not much more than what the original C program would have, the only difference is that the bit-manipulation would be added by compiler magic in the C version, whereas you will have to write it yourself.
If you don't have many records in memory, and don't need to talk to another program, I'd use a natural Delphi structure. Trade-off for higher performance will be more memory used.
But it all depends on your criteria.
In any case, you won't be able to talk the Delphi compiler into doing the same job for you as the C compiler.
PACKED RECORD, suggested by another here, doesn't do that, and was never meant to. It will only remove alignment padding to put integers on 32-bit boundaries and similar, but won't pack multiple fields into one byte.
Note that a common way to do this is through Delphi SETS, which are implementing internally using bit-fields. Again, you will have different code than the C variant.

Resources