动态数组可以用作 Windows 回调函数的参数吗?

Can dynamic arrays be used as parameters to Windows callback functions?

我正在重做一些代码以准备好 64 位。这将 EnumWindows 与回调 return 列表一起使用 运行 delphi 个应用程序(除了 IDE 和它本身)然后被杀死。起初 它使用 TStringlist 来保存这些应用程序的句柄。我想更改它以直接以数字形式收集句柄。 我已经找到了一个非常令人满意的解决方案,如下所示,使用通用 TList 来收集句柄。

在此过程中,我最初尝试使用动态数组 - 它没有用。验证了TList的解法后,出于学术兴趣,重温了一下, 尝试了各种方法来使用动态数组来实现它——但都没有成功。我在文档中找不到任何禁令,尽管我确实遇到过 Rudy V 博客中的这条注释:"Delphi strings and dynamic arrays should not be passed as reference counted types to API functions anyway..."

所以,我只是在寻求一个"ruling"动态数组可以或不能用作回调函数的参数。

type
  THandleList=Tlist<THandle>;
const
  ReqdClass: string = 'TApplication' ;


procedure KillWindowViaHandle(Ahwnd:THandle; Amsg: Cardinal=WM_CLOSE);
begin
  PostMessage(Ahwnd, Amsg, 0, 0);
end;

// Get Active "User" Applications (except for bds.exe & caller). Relies on top
// level window having classname of TApplication. Returns list of handles.

function FindActiveUSERApps(AHandle: HWND; AList: lparam): BOOL ; stdcall;
var
  classname: string;
  pid: DWORD;
  imagename: string;
begin
  Result := true;         // keep it going .. want them all
  GetWindowThreadProcessID(AHandle, @pid);  // not interested in ThreadID returned
  imagename := GetProcessFileName(pid) ;
  SetLength(ClassName, 255);
  SetLength(ClassName, GetClassName(AHandle, PChar(className), Length(className)));
  if ( ansicontainstext(classname, ReqdClass) ) and
     ( not ansisametext(ImageName, 'bds.exe')) and
     ( not ansisametext(ImageName, ExtractFileName(Application.ExeName))) then
    THandleList(Alist).Add(AHandle) ;
end;


function GetActiveUSERApps(AList: THandleList): boolean;
begin
  AList.Clear;
  EnumWindows(@FindActiveUSERApps, lparam(AList) );
  result  := Alist.Count > 0;
end;


function KillActiveUSERApps: boolean;
var
  i : integer;
  ActiveList: THandleList;
begin
  result := false;
  ActiveList := THandleList.Create;
  try
    GetActiveUSERApps(ActiveList);
    for i:= 0 to activelist.Count - 1   do
      KillWindowviaHandle( ActiveList[i] );

    // noticed that some processes were resistant to being killed via WM_CLOSE.
    // So try gentle approach first, and then if necessary, use the big stick.
    GetActiveUSERApps(activeList);
    for i:= 0 to activelist.Count - 1   do
      KillWindowviaHandle( ActiveList[i], WM_QUIT );

    result  := true;
  finally
    ActiveList.Free;
  end;
end;

在没有实际看到您使用动态数组的实现的情况下,我假设您通过使用 SetLength 扩展它来向该数组添加元素。这反过来又改变了内部是指针的数组变量。因此,调用方法仍然使用旧指针变量指向它作为参数传递的不存在的动态数组。

您可以通过使用指向动态数组的指针来解决这个问题。

type
  THandleList = TArray<THandle>;
  PHandleList = ^THandleList;

function FindActiveUSERApps(AHandle: HWND; AList: lparam): BOOL ; stdcall;
var
  ...
  PList: PHandleList;
begin
  ...
  PList := PHandleList(AList);
  SetLength(PList^, Length(PList^) + 1);
  PList^[High(PList^)] := AHandle;
end;


function GetActiveUSERApps(var AList: THandleList): boolean;
begin
  AList.Clear;
  EnumWindows(@FindActiveUSERApps, lparam(@AList) );
  result  := Alist.Count > 0;
end;

也就是说,我个人更喜欢 TList 方法,因为它简单明了。特别是因为您可以使用 AList.ToArray.

从中轻松地 return 一个动态数组