环境:Windows 7(x64)上的Python 2.7.9(32位)
我在Python中使用ctypes来访问名为“tdbaccess.dll”(found here)的DLL,我假设它是用Delphi编写的,基于作者的文档和this info on the error I'm getting。遗憾的是,我无法访问DLL的源代码。 DLL用于打开旧的Madden NFL PC游戏所使用的“数据库”名单文件(我使用的是2008版本的名单 - 不要判断:P)。
我的代码包含以下两个结构:
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),
]
和
class tdbFieldProperties(Structure):
_fields_ = [
('Name', c_char_p),
('Size', c_int),
('FieldType', c_int),
]
这些结构的实例通过引用传递给DLL中的两个函数,这两个函数又填充了里面的字段。首先,将tdbTableProperties的实例附加到列表中并按原样传递:
# 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))
这很好,并提供以下输出:
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
[...]
我真的只对“PLAY”表感兴趣,所以现在我尝试使用tdbFieldProperties实例进行类似的操作,以获取该表中每个字段的属性......
# 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))
......然而,它最终失败了,总是在第六个表的110个字段中的第70个:
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
根据tdbaccess.dll的文档,在Delphi或Visual Basic .NET实现中,tdbFieldProperties结构将FieldType作为枚举,在Python中没有精确的表示。来自文档:
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
和
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
因此,基于answers to this question,我尝试在Python中使用c_int,c_uint(以及几乎所有其他ctype)作为FieldType的类型。但无论我使用什么,我都会在第6个循环的循环的第70次迭代中得到Windows错误0xEEDFADE。不仅如此,我在tdbFieldProperties结构中为Name字段获取的值不是错误之前的值。如果我扩展我的代码以获取每个表中每个字段的字段属性,那么我查询的每个表都会以相同的顺序在“名称”字段中放置相同的值,所以即使在错误本身之前,显然有些错误:
# 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))
给出了:
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'
[...]
我确定这不是DLL中的错误,因为我知道Visual C#项目在Python中尝试做同样的事情,没有任何问题。我有什么想法可能做错了吗?非常感谢提前!
答案 0 :(得分:2)
第一个明显的错误是c_char_p
是指向8位文本的指针。但Delphi和VB.net结构清楚地显示用户16位Unicode文本。将c_char_p
替换为c_wchar_p
。
下一个问题涉及记录的布局。 Delphi和VB.net代码不同意。 Delphi记录已打包,VB.net结构已对齐。你的Python结构是对齐的。您可以在ctypes结构中尝试_pack_ = 1
来打包记录。任何人都可以猜测记录是如何布局的。
我们不确定枚举类型有多大。它们可以是1,2或4个字节。我们也没有要检查的所有结构声明,所以除了我在这里写的内容之外,可能存在并且可能存在问题。