AS。関連する質問を締め切りました。64ビットDelphiでネストされたローカル関数にアドレスを指定できないのはなぜですか?
以下の単純なコード(最上位のIeウィンドウを見つけてその子を列挙する)は、'32ビットWindows 'ターゲットプラットフォームで動作します。私はそれが'64ビットWindowsのターゲットプラットフォームで失敗する場所を示すためにAssert
を挿入した
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
EnumChildWindows(Wnd, @EnumChildren, UINT_PTR(@WndChild));
if WndChild <> 0 then
..
end;
:同様のDelphiの以前のバージョンに問題なしはありません。私がのネストを解除した場合、コードに問題はありませんコールバック。
パラメータで渡された誤った値がちょうどゴミであるのか、メモリのアドレスが誤って配置されているのか(呼び出し規約?)はわかりません。私が最初にやってはならないコールバックの本質的な入れ子ですか?それとも、これは私が生きなければならない欠点ですか?
編集:デビッドの答えを受けて
、型付きコールバックを使って宣言EnumChildWindows
を有する同一のコード。
(編集:私がまだ '@'演算子を使用して以来、Davidが言っていることは実際にはテストされません)それはオペレータとうまく動作しますが、実際にはこの制限は、Windows APIのコールバックに特有のものではなく、procedural type
の変数にその関数のアドレスを取得する場合、同じ問題が起こる
type
TFNEnumChild = function(hwnd: HWND; lParam: LPARAM): Bool; stdcall;
function TypedEnumChildWindows(hWndParent: HWND; lpEnumFunc: TFNEnumChild;
lParam: LPARAM): BOOL; stdcall; external user32 name 'EnumChildWindows';
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
TypedEnumChildWindows(Wnd, @EnumChildren, UINT_PTR(@WndChild));
if WndChild <> 0 then
..
end;
)I非巣ない限り、コールバックをコンパイルそれをたとえばカスタムコンパレータとしてに渡します。
http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedural_Types
procedure TForm2.btn1Click(Sender: TObject);
var s : TStringList;
function compare(s : TStringList; i1, i2 : integer) : integer;
begin
result := CompareText(s[i1], s[i2]);
end;
begin
s := TStringList.Create;
try
s.add('s1');
s.add('s2');
s.add('s3');
s.CustomSort(@compare);
finally
s.free;
end;
end;
それは予想通り、32ビットとしてコンパイル時に動作しますが、Win64の用にコンパイルするときAccess Violation
で失敗します。関数compare
の64ビット版では、s = nil
とi2
=いくつかのランダムな値。
btn1Click
以外の機能がcompare
の場合は、Win64ターゲットでも期待どおりに動作します。
ネストされていないコードがネストされたコードと機能的に同等である場合、コンパイラのバグまたはコールバックパラメータの受け渡しの問題です。私は後者のために投票します.64ビットの呼び出し規約が32ビットの呼び出し規約と異なるため、スタックの破損が発生する可能性があります。したがって、おそらく "stdcall"はここで使用するものではありません。それを取り除き、再度起こるかどうか確認してください。さもなければ入れ子コールバックは(少なくともここに示された方法で)完全にうまくいきます。 – Thomas
Win64には1つの呼び出し規約しかありません。 64ビットモードでは、コードで指定した呼び出し規約は無視されます。しかし、あなたの関数/プロシージャー/ dllインポートのシグニチャーは間違っているかもしれません。しかし、Delphiに関数からのコールバックを実装できないことは、コンパイラのバグのように聞こえます。 –
@Thomas - 'stdcall'を削除すると、アサーションが再び失敗します.64ビットがターゲットになっているときには、Delphiコンパイラがそれを無視すると思います。 –