我目前正在编写一个模块,该模块与检查扫描器的黑盒第三方DLL接口。我需要动态加载DLL函数,这适用于除一个函数之外的所有函数 SetScanParameters函数有一个记录结构作为参数,我认为它在某种程度上干扰了我用来动态加载它的方法(见下文)。动态加载时,该功能会被访问冲突中断 但是,静态加载时,SetScanParameters会加载并正常运行。 还有什么我需要做的动态加载具有记录结构的函数吗?
为清晰起见进行了自我编辑:
记录类型:
TBK_ScanParameter=packed record
Left:short;
Top:short;
Width:short;
Length:short;
//
xResolution:short;
yResolution:short;
BitsPerPixel:short;
LightControl:short;
MotorControl:short;
//
rGain:short;
gGain:short;
bGain:short;
rOffset:short;
gOffset:short;
bOffset:short;
rExposure:short;
gExposure:short;
bExposure:short;
//
FeedDirection:short;
CropImage:short;
ScanWithMICR:short;
//
Reserved:array [0..14] of short;
end;
静态声明:
function BK_SetScanParameter(var ScanParameter:TBK_ScanParameter):integer; cdecl;
静态实施:
function BK_SetScanParameter(var ScanParameter:TBK_ScanParameter):integer; cdecl; external 'BKV2.dll' name '_BK_SetScanParameter@4';
动态逻辑(如果我不必使用静态调用使其工作,那么动态逻辑是什么):
function TdmScannerV2.SetScanParameter(pScanParameter: TBK_ScanParameter): string;
type
TBK_SetScanParameter = function (var ScanParameter:TBK_ScanParameter):integer; stdcall;
var
hV2Dll:HMODULE;
func:TBK_SetScanParameter;
begin
hV2Dll:=0;
result := '';
try
hV2Dll:=LoadLibrary('BKV2.dll');
if hV2Dll>0 then
begin
@func:=GetProcAddress(hV2Dll, '_BK_SetScanParameter@4');
if Assigned(@func) then
begin
try
if BK_SetScanParameter(pScanParameter) > 0 then {This one works, but is static}
//if func(pScanParameter) > 0 then {this one gets an AV}
begin
Result := 'Y:Scan Parameters Set';
end
else
Result := 'ERROR:Failure code returned';
{
if func(pScanParameter) > 0 then
Result := 'Y:Scan Parameters Set'
else
Result := 'ERROR:Failure code returned';
}
except
on e:Exception do
begin
Result := 'ERROR:Exception:' + e.Message;
end;
end;
end
else
Result := 'ERROR:Unable to load BK_SetScanParameter';
end
else
Result := 'ERROR:Unable to load BKV2.dll';
finally
if hV2Dll>0 then FreeLibrary(hV2Dll);
end;
end;
我尝试过使用stdcall,cdecl,safecall,pascal和注册动态,它们都导致了AV。我也尝试在struct [1..15]而不是[0..14]中创建数组。并在 但我没有得到的是,如果我将结构传递给静态版本,它就可以了。
另外,OP中有一些拼写错误,我为此道歉。我正在重写OP中的代码并做了一些拼写错误,这可能会让线程变得混乱。我用当前测试函数的复制/粘贴替换了它。
编辑:下面是DLL文档中描述的typedef:
typedef struct ScanParameter
{
short Left; // left start positsion
short Top; // top start positsion
short Width; // scan image width in 1/100 inch
short Length; // scan image length in 1/100 inch
short xResolution; // horizontal resolution
short yResolution; // vertical resolution
short BitsPerPixel; // 24bit color, 8bit gray
short LightControl; // 0 - All lamp Off, 1 - red, 2 - green, 3 - blue, 4 - All lamp On
short MotorControl; // Motor Control, 0 - off, 1 = on
short rGain; // AFE R-Gain
short gGain; // AFE G-Gain
short bGain; // AFE B-Gain
short rOffset; // AFE R-Offset
short gOffset; // AFE G-Offset
short bOffset; // AFE B-Offset
short rExposure; // AFE R-Exposure
short gExposure; // AFE G-Exposure
short bExposure; // AFE B-Exposure
short FeedDirection; // feedout paper direction, 0 –fordward, 1 - backward
short CropImage; // 0 - no trim edge , 1 - trim edge
short ScanWithMICR; // 0 –off, 1 –scan image until paper leave device
short Reserved[15];
} ScanParameter;
答案 0 :(得分:3)
如上所述,调用约定看起来应该是cdecl,而不是stdcall。其次,尝试将加载库更改为
hV2Dll := LoadLibrary('Scan.dll');
原版有'ScanDLL.dll'。
答案 1 :(得分:2)
您确实定义了两种不同的调用约定:
答案 2 :(得分:1)
我的建议是检查调用约定,在delphi默认调用约定是Pascal但是微软编译的dll最有可能是cdecl。所以尝试将func定义为
TSetScanParameter = function (var ScanParameter:TParams):integer; cdecl;
正如你在静态定义中所做的那样。
答案 3 :(得分:0)
这很可能是Delphi如何存储记录结构的问题,这与DLL中的记录结构不兼容。我在使用C语言编写的DLL中遇到过与Delphi类似的问题。
引用
通常,复杂数据类型(如记录)的元素与2,4或8字节边界对齐,视数据类型而定。例如,Word字段将与4字节边界对齐。记录也被填充以确保它们以4字节边界结束。
因此,当您传递记录时,字段对齐很可能与内部使用的定义不同,您会看到访问冲突。由于静态和动态之间的内存分配不同,因此很可能会出现行为,因为静态分配恰好正确对齐。
首先要尝试的是在记录中使用Packed关键字。
TPackedRecord = Packed Record
再次引用手册
Packed关键字告诉Delphi最小化已定义对象占用的存储空间。 Packed覆盖了这一点,将数据压缩到最小的存储中,尽管这会降低访问性能。
假设您的呼叫约定是正确的,我希望这可以解决问题。
答案 4 :(得分:0)
你说以下是有效的:
function SetScanParameter(var Scans:TParams):integer; cdecl; external 'Scan.dll' name '_SetScanParameter@4';
然而,另一个不起作用....首先,确保你在每个人都做同样的事情。目前你不是,见下文:
TSetScanParameter = function(var ScanParameter:TParams):integer;的 STDCALL; 强>
hV2Dll:=的LoadLibrary(的 'ScanDLL.dll'强>);
更不用说您可能在每次调用中传递不同的传递不同值,您可能使用TParams
的不同声明。如果你想找出为什么一个 apple 与另一个 apple 不同,你就不要把它与 orange 进行比较。
正确掌握这些基础知识;那么,如果你还有问题:
如果您需要帮助,至少应该努力帮助我们。
答案 5 :(得分:0)
如果您有一些演示.exe加载DLL,看看您是否可以反汇编它,并找到它调用此函数的位置。如果你很幸运,并且功能不是太复杂,你可能会发现记录应该有多大。
Afaik对齐是C编译器的典型(一切都是短的或复杂的短,并且都在自然的bounderies上对齐),但是试图将阵列对准一些更大的boundery(4,8,16字节)和/或用一些额外的假人填充记录也可能有帮助。
答案 6 :(得分:0)
您可以从头文件中发布C函数声明吗? 当函数被编译为动态加载DLL的静态vs地址时,还要检查DLL的加载地址。 DLL本身可能有一些特定于位置的代码,当你静态链接它时,Windows加载器会将DLL加载到内存中的正确位置但是当你动态加载它时,该位置可能已经存在某些东西,因此Windows加载器会将DLL重新定位到新的位置,但DLL中的代码可能没有正确编写,它可能仍然试图访问一些静态内存地址。 另一件事是在函数调用之外加载和卸载DLL并在函数外声明动态函数类型,Delphi编译器可以在函数内部声明所有内容时以不同方式优化。 最后确保使用正确的调用约定(应该在C头文件中可用),因为它们非常不同,如果使用不正确的约定调用DLL函数将导致A / V或堆栈问题。
答案 7 :(得分:0)
感谢帮助人员。事实证明它不再是一个问题。扫描仪公司提供的DLL有点儿错误,他们推出了一个修复问题的新版本。