Delphi:为什么我可以静态但不动态地链接这个函数?

时间:2009-12-10 21:01:36

标签: delphi dll dynamic access-violation

我目前正在编写一个模块,该模块与检查扫描器的黑盒第三方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;

8 个答案:

答案 0 :(得分:3)

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

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

原版有'ScanDLL.dll'。

答案 1 :(得分:2)

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

    静态声明中的
  1. cdecl
  2. 动态声明中的
  3. stdcall

答案 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 进行比较。

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

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

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

答案 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有点儿错误,他们推出了一个修复问题的新版本。