2

我目前正在编写一个模块,该模块与用于检查扫描仪的黑盒第 3 方 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 和 register,它们都导致了 AV。我还尝试在结构 [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;
4

8 回答 8

3

如上所述,调用约定看起来应该是 cdecl,而不是 stdcall。其次,尝试将加载库更改为,

hV2Dll := LoadLibrary('Scan.dll');

原来有“ScanDLL.dll”。

于 2009-12-10T22:25:53.807 回答
2

您确实定义了两种不同的调用约定

  1. 静态声明中的cdecl
  2. 动态声明中的stdcall
于 2009-12-10T21:21:09.390 回答
1

我的建议是检查调用约定,在 delphi 中,默认调用约定是 Pascal,但微软编译的 dll 很可能是 cdecl。所以尝试将 func 定义为

TSetScanParameter = function (var ScanParameter:TParams):integer; cdecl;

就像你在静态定义中所做的那样。

于 2009-12-10T21:10:19.907 回答
0

如果你有一些加载DLL的演示.exe,看看你是否可以反汇编它,并找到它调用这个函数的地方。如果你运气好,并且功能不是太复杂,你可能会发现记录应该有多大。

Afaik 对齐对于 C 编译器来说是典型的(一切都是短的或复杂的短,并且都在自然边界上对齐),但试图在更大的边界(4、8、16 字节)上对齐数组和/或填充用一些额外的假人记录可能也有帮助。

于 2009-12-11T23:15:24.413 回答
0

谢谢你们的帮助。事实证明这不再是问题。扫描仪公司提供的 DLL 有点错误,他们推出了一个新的来解决问题。

于 2009-12-15T16:50:33.810 回答
0

这很可能是 Delphi 如何存储记录结构的问题,这与 DLL 中的记录结构不兼容。我用 C 语言编写的与 Delphi 接口的 DLL 也有类似的问题。

去引用

通常,复杂数据类型(如记录)的元素与数据类型相适应,对齐到 2、4 或 8 字节边界。例如,Word 字段将与 4 字节边界对齐。记录也被填充以确保它们以 4 字节边界结束。

因此,当您传递记录时,字段对齐方式很可能与内部使用的定义不同,并且您会看到访问冲突。由于静态和动态之间的内存分配不同,因此完全有可能出现这种行为,因为静态分配恰好正确对齐。

首先要尝试的是在记录上使用 Packed 关键字。

TPackedRecord = Packed Record

再次引用手册

Packed 关键字告诉 Delphi 最小化定义对象占用的存储空间。Packed 覆盖了这一点,将数据压缩到最小的存储中,尽管会降低访问性能。

假设您的调用约定是正确的,我希望这可以解决问题。

于 2009-12-10T21:18:34.513 回答
0

你说以下是有效的:

function SetScanParameter(var Scans:TParams):integer; cdecl; external 'Scan.dll' name '_SetScanParameter@4';

然而,另一个不工作....首先,确保你在每一个中做同样的事情。目前你不是,见下文:

TSetScanParameter = function (var ScanParameter:TParams):integer; 标准调用;
hV2Dll:=LoadLibrary( 'ScanDLL.dll' );

更不用说您可能在每次调用中传递不同的传递不同的值,您可能使用不同的TParams. 如果你想找出一个苹果与另一个苹果不同的原因不要去拿它和橘子比较。

正确掌握这些基础知识;那么,如果您仍然有问题:

  • 向我们提供TParams 的声明。
  • 向我们提供此第 3 方 DLL 文档的相关摘录。
  • 如果您能找到的话,用于创建 DLL 的语言将会很有用。
  • 上述语言中方法和结构的实际声明会更加有用。

如果您需要帮助,至少要努力帮助我们帮助您。

于 2009-12-11T13:17:32.707 回答
0

您可以从头文件中发布 C 函数声明吗?当函数被编译为静态时,还要检查 DLL 的加载地址与动态加载 DLL 的地址。DLL 本身可能有一些特定于位置的代码,当您静态链接它时,Windows 加载程序会将 DLL 加载到内存中的正确位置,但是当您动态加载它时,该位置可能已经存在某些内容,因此 Windows 加载程序会将 DLL 重新定位到新的位置,但 DLL 中的代码可能没有正确编写,它可能仍在尝试访问一些静态内存地址。要尝试的另一件事是在函数调用之外加载和卸载 DLL,并在函数之外声明动态函数类型,当您在函数内声明所有动态函数类型时,Delphi 编译器可能会进行不同的优化。

于 2009-12-12T00:06:14.017 回答