windows - 在 64 位 XE6 中从 Windows 回调返回结果

标签 windows delphi callback delphi-xe6

我有一些代码使用 EnumFontFamiliesEX 来确定是否安装了特定字体(使用其“facename”)。该代码在 32 位中运行良好。当我以 64 位编译和运行它时,它一直在回调例程中抛出异常。

我现在已经让它在这两个下工作,但前提是我没有将函数 FindFontbyFaceName 的结果作为第四个参数传递给 EnumFontFamiliesEX,而是传递了一个本地(或全局)变量 - 在这种情况下为 MYresult。(然后从中设置结果)。我不明白这是怎么回事?谁能解释或指出我更好的方法。 (我对字体的机制不太感兴趣,而是对基本的回调机制感兴趣)。

// single font find callback
function FindFontFace(  {$IFDEF CPUX86}  lpelf: PLogFont;       {$ENDIF}
                        {$IFDEF CPUX64}  lpelf: PEnumLogFontEx; {$ENDIF}
                        lpntm: PNewTextMetricEx;
                        AFontType: DWORD; var Aresult: lparam): integer ; stdcall;
begin
  result := 0;       // 1 shot only please  - not interested in any variations in style etc
  if (lpelf <> nil) then
    Aresult := -1         // TRUE
  else
    Aresult := 0;
end;


function FindFontbyFaceName(ACanvas: TCanvas; const AFacename: string): boolean;
var
  lf: TLogFont;
  Myresult: boolean;
begin
  MYresult := false;

  FillChar(lf, SizeOf(lf), 0);
  StrLCopy(lf.lfFaceName, PChar(AFacename), 32);
  lf.lfCharSet := DEFAULT_CHARSET;

  // this works in both 32 and 64 bit
  EnumFontFamiliesEX(ACanvas.Handle, lf, @FindFontFace, lparam(@MYresult), 0);
  result := MYresult;

  // this works in 32 bit but throws exception in callback in 64 bit
//  EnumFontFamiliesEX(ACanvas.Handle, lf, @FindFontFace, lparam(@result), 0);
end;


function FindFont(const AFacename: string): boolean;
var
  AImage: TImage;
begin
  AImage := Timage.Create(nil);
  try
    result := FindFontbyFaceName(AImage.Canvas, Afacename);
  finally
    Aimage.Free;
  end;
end;

最佳答案

您的回调函数声明不正确。您将最后一个参数声明为 var LPARAM,这是错误的。 lParam 参数按值传递,而不是按引用传递。当调用 EnumFontFamiliesEx() 时,您将传递一个指向 Boolean 的指针作为 lParam 值。

您的回调试图将 sizeof(LPARAM) 字节数写入只有 SizeOf(Boolean) 字节可用的内存地址(以及您为什么要尝试将 -1 写入 Boolean?)。所以你正在覆盖内存。当使用指向局部变量的指针作为 lParam 时,您可能只是覆盖调用函数的调用堆栈上的内存,这并不重要,因此您不会看到崩溃。

您需要:

  1. 删除 var 并将 lParam 参数类型转换为 PBoolean:

    function FindFontFace(  lpelf: PLogFont;
                            lpntm: PTextMetric;
                            FontType: DWORD;
                            lParam: LPARAM): Integer ; stdcall;
    begin
      PBoolean(lParam)^ := True;
      Result := 0;       // 1 shot only please  - not interested in any variations in style etc
    end;
    

    或者:

    function FindFontFace(  lpelf: PLogFont;
                            lpntm: PTextMetric;
                            FontType: DWORD;
                            lParam: PBoolean): Integer ; stdcall;
    begin
      lParam^ := True;
      Result := 0;       // 1 shot only please  - not interested in any variations in style etc
    end;
    
  2. 保留 var 但将参数类型更改为 Boolean 而不是 LPARAM:

    function FindFontFace(  var lpelf: TLogFont;
                            var lpntm: TTextMetric;
                            FontType: DWORD;
                            var lParam: Boolean): Integer ; stdcall;
    begin
      lParam := True;
      Result := 0;       // 1 shot only please  - not interested in any variations in style etc
    end;
    

这两种方法都允许您将 @Result 作为 lParam 传递给 32 位和 64 位的 EnumFontFamiliesEx():

function FindFontbyFaceName(ACanvas: TCanvas; const AFacename: string): Boolean;
var
  lf: TLogFont;
begin
  Result := False;

  FillChar(lf, SizeOf(lf), 0);
  StrLCopy(lf.lfFaceName, PChar(AFacename), 32);
  lf.lfCharSet := DEFAULT_CHARSET;

  EnumFontFamiliesEX(ACanvas.Handle, lf, @FindFontFace, LPARAM(@Result), 0);
end;

附带说明一下,创建一个 TImage 只是为了有一个用于枚举的 Canvas 是一种浪费。你根本不需要它:

function FindFontFace(  lpelf: PLogFont;
                        lpntm: PTextMetric;
                        FontType: DWORD;
                        lParam: LPARAM): integer ; stdcall;
begin
  PBoolean(lParam)^ := True;
  Result := 0;       // 1 shot only please  - not interested in any variations in style etc
end;

function FindFont(const AFacename: string): Boolean;
var
  lf: TLogFont;
  DC: HDC;
begin
  Result := False;

  FillChar(lf, SizeOf(lf), 0);
  StrLCopy(lf.lfFaceName, PChar(AFacename), 32);
  lf.lfCharSet := DEFAULT_CHARSET;

  DC := GetDC(0);
  EnumFontFamiliesEx(DC, lf, @FindFontFace, LPARAM(@Result), 0);
  ReleaseDC(0, DC);
end;

也就是说,如果您使用 TScreen.Fonts 属性而不是直接调用 EnumFontFamiliesEx(),则可以简化代码:

function FindFont(const AFacename: string): Boolean;
begin
  Result := (Screen.Fonts.IndexOf(AFacename) <> -1);
end;

关于windows - 在 64 位 XE6 中从 Windows 回调返回结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42262114/

相关文章:

c++ - 对 `JNI_CreateJavaVM' 窗口的 undefined reference

python - 如何通过 python/pyqt 创建 Windows 7 跳转列表?

delphi - 如何在电子邮件地址中的单词 "com"之前添加点?

delphi更改 Canvas 像素颜色

delphi - 你能帮忙把这个非常小的 C++ 组件翻译成 Delphi 吗?

javascript - Meteor 方法回调何时触发?

c++ - 使用C++导入注册表文件

php - php scandir() 是否排除 Windows 下的隐藏文件?

django - 回调时存储数据的最佳方式 -Python - Django - Bokeh - Webdevelopment

c++ - Gtk+ g_signal_connect() 和 C++ lambda 导致 "invalid cast"错误