Environment: Python 2.7.9 (32 bit) on Windows 7 (x64)
I'm using ctypes in Python to access a DLL named "tdbaccess.dll" (found here), which I assume was written in Delphi, based on the author's documentation and this info on the error I'm getting. I don't have access to the source of the DLL, unfortunately. The DLL is used to open a 'database' roster file used by the old Madden NFL PC games (a roster from the 2008 version is what I am using - don't judge :P).
My code contains these two structs:
class tdbTableProperties(Structure):
_fields_ = [
('Name', c_char_p),
('FieldCount', c_int),
('Capacity', c_int),
('RecordCount', c_int),
('DeletedCount', c_int),
('NextDeletedRecord', c_int),
('Flag0', c_bool),
('Flag1', c_bool),
('Flag2', c_bool),
('Flag3', c_bool),
('NonAllocated', c_bool),
('HasVarchar', c_bool),
('HasCompressedVarchar', c_bool),
]
and
class tdbFieldProperties(Structure):
_fields_ = [
('Name', c_char_p),
('Size', c_int),
('FieldType', c_int),
]
Instances of these structs get passed by ref to two functions in the DLL which in turn fill out the fields inside. First, the instances of tdbTableProperties are appended to a list and passed as so:
# Create a list to hold all our table properties structs.
listTdbTableProperties = []
# Loop over the tables and get their properties.
for i in range(intNumberOfTables):
listTdbTableProperties.append(tdbTableProperties(Name="ASDF")) #Initialize Name field per docs
boolGotTableProperties = tdbaccessDLL.TDBTableGetProperties(intDBIndex, i, byref(listTdbTableProperties[i]))
if boolGotTableProperties:
print("listTdbTableProperties[%d].Name = %r" % (i, listTdbTableProperties[i].Name))
print("listTdbTableProperties[%d].FieldCount = %d\n" % (i, listTdbTableProperties[i].FieldCount))
This works fine and gives the following output:
listTdbTableProperties[0].Name = 'CITY'
listTdbTableProperties[0].FieldCount = 21
listTdbTableProperties[1].Name = 'COCH'
listTdbTableProperties[1].FieldCount = 68
[...]
listTdbTableProperties[5].Name = 'INJY'
listTdbTableProperties[5].FieldCount = 5
listTdbTableProperties[6].Name = 'PLAY'
listTdbTableProperties[6].FieldCount = 110
[...]
I'm really only interested in the "PLAY" table, so now I try a similar thing with instances of tdbFieldProperties, to get the properties for each field in that table...
# A list to hold all our field properties structs for the player table.
listPlayerTableTdbFieldProperties = []
# Get the properties of each of the fields for the PLAY table (index 6 in listTdbTableProperties).
for i in range(listTdbTableProperties[6].FieldCount):
listPlayerTableTdbFieldProperties.append(tdbFieldProperties(Name="Blah")) #Initialize Name field per docs
print("listPlayerTableTdbFieldProperties[%d].Name BEFORE = %r" % (i, listPlayerTableTdbFieldProperties[i].Name))
boolGotTableFieldProperties = tdbaccessDLL.TDBFieldGetProperties(intDBIndex, listTdbTableProperties[6].Name, i, byref(listPlayerTableTdbFieldProperties[i]))
if boolGotTableFieldProperties:
print("listPlayerTableTdbFieldProperties[%d].Name AFTER = %r" % (i, listPlayerTableTdbFieldProperties[i].Name))
... However, it eventually fails as so, always at the 70th of the 110 fields in the sixth table:
listPlayerTableTdbFieldProperties[0].Name BEFORE = 'Blah'
listPlayerTableTdbFieldProperties[0].Name AFTER = 'TRV1'
listPlayerTableTdbFieldProperties[1].Name BEFORE = 'TRV1'
listPlayerTableTdbFieldProperties[1].Name AFTER = 'TEZ1'
listPlayerTableTdbFieldProperties[2].Name BEFORE = 'TEZ1'
listPlayerTableTdbFieldProperties[2].Name AFTER = 'TRV2'
[...]
listPlayerTableTdbFieldProperties[68].Name BEFORE = 'TAth'
listPlayerTableTdbFieldProperties[68].Name AFTER = 'TAss'
listPlayerTableTdbFieldProperties[69].Name BEFORE = 'TAss'
Traceback (most recent call last):
File "DLLtest2.py", line 70, in <module>
boolGotTableFieldProperties = tdbaccessDLL.TDBFieldGetProperties(intDBIndex, listTdbTableProperties[6].Name, i, byref(listPlayerTableTdbFieldProperties[i]))
WindowsError: [Error 250477278] Windows Error 0xEEDFADE
Accoring to the documentation for tdbaccess.dll, in a Delphi or Visual Basic .NET implementation the tdbFieldProperties struct would have FieldType as an enum, which has no exact representation in Python. From the documentation:
Delphi Syntax
type
TtdbFieldProperties = packed record
Name: PWideChar;
Size: Integer;
FieldType: TtdbFieldType;
end;
Visual Basic .NET Syntax
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Structure TdbFieldProperties
Public Name As String
Public Size As Integer
Public FieldType As TdbFieldType
End Structure
and
Delphi Syntax
type
TtdbFieldType = (tdbString = 0, tdbBinary = 1, tdbSInt = 2, tdbUInt = 3, tdbFloat = 4, tdbVarchar = $D, tdbLongVarchar = $E, tdbInt = $2CE);
Visual Basic .NET Syntax
Enum TdbFieldType
tdbString = 0
tdbBinary = 1
tdbSInt = 2
tdbUInt = 3
tdbFloat = 4
tdbVarchar = &HD
tdbLongVarchar = &HE
tdbInt = &H2CE
End Enum
So, based on answers to this question, I've tried using c_int, c_uint (and virtually every other ctype) in Python as the type for FieldType. But no matter what I use, I get Windows Error 0xEEDFADE at the 70th iteration of the loop for the 6th table. And not only that, but the values I'm getting for the Name field in the tdbFieldProperties struct are not what they should be even before the error. If I expand my code to get the field properties for each field in each table, then every table I query puts the same values in the Name field in the same order, so clearly something is wrong even before the error itself:
# Loop over the fields in each tdbtpTableProperties and get the properties of each field.
tdbfpTableFieldProperties = [[] for x in range(intNumberOfTables)]
for i in range(intNumberOfTables):
print("\n")
for j in range(tdbtpTableProperties[i].FieldCount):
tdbfpTableFieldProperties[i].append(tdbFieldProperties(Name="Blah"))
print("tdbfpTableFieldProperties[%d][%d].Name BEFORE = %r" % (i, j, tdbfpTableFieldProperties[i][j].Name))
boolGotTableFieldProperties = tdbaccessDLL.TDBFieldGetProperties(intDBIndex, tdbtpTableProperties[i].Name, j, byref(tdbfpTableFieldProperties[i][j]))
print("tdbfpTableFieldProperties[%d][%d].Name AFTER = %r" % (i, j, tdbfpTableFieldProperties[i][j].Name))
Which gives:
tdbfpTableFieldProperties[0][0].Name BEFORE = 'Blah'
tdbfpTableFieldProperties[0][0].Name AFTER = 'TRV1'
tdbfpTableFieldProperties[0][1].Name BEFORE = 'TRV1'
tdbfpTableFieldProperties[0][1].Name AFTER = 'TEZ1'
tdbfpTableFieldProperties[0][2].Name BEFORE = 'TEZ1'
tdbfpTableFieldProperties[0][2].Name AFTER = 'TRV2'
tdbfpTableFieldProperties[0][3].Name BEFORE = 'TRV2'
tdbfpTableFieldProperties[0][3].Name AFTER = 'TEZ2'
[...]
tdbfpTableFieldProperties[0][20].Name BEFORE = 'CGID'
tdbfpTableFieldProperties[0][20].Name AFTER = 'DGID'
tdbfpTableFieldProperties[1][0].Name BEFORE = 'DGID'
tdbfpTableFieldProperties[1][0].Name AFTER = 'TRV1'
tdbfpTableFieldProperties[1][1].Name BEFORE = 'TRV1'
tdbfpTableFieldProperties[1][1].Name AFTER = 'TEZ1'
tdbfpTableFieldProperties[1][2].Name BEFORE = 'TEZ1'
tdbfpTableFieldProperties[1][2].Name AFTER = 'TRV2'
tdbfpTableFieldProperties[1][3].Name BEFORE = 'TRV2'
tdbfpTableFieldProperties[1][3].Name AFTER = 'TEZ2'
[...]
tdbfpTableFieldProperties[1][67].Name BEFORE = 'TCTX'
tdbfpTableFieldProperties[1][67].Name AFTER = 'TAth'
tdbfpTableFieldProperties[2][0].Name BEFORE = 'TAth'
tdbfpTableFieldProperties[2][0].Name AFTER = 'TRV1'
tdbfpTableFieldProperties[2][1].Name BEFORE = 'TRV1'
tdbfpTableFieldProperties[2][1].Name AFTER = 'TEZ1'
tdbfpTableFieldProperties[2][2].Name BEFORE = 'TEZ1'
tdbfpTableFieldProperties[2][2].Name AFTER = 'TRV2'
tdbfpTableFieldProperties[2][3].Name BEFORE = 'TRV2'
tdbfpTableFieldProperties[2][3].Name AFTER = 'TEZ2'
[...]
I'm sure it's not a bug in the DLL, as I know of a Visual C# project that does the same thing I am trying to do in Python, with no problems. Any ideas what I might be doing wrong? Many thanks in advance!