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

时间:2017-02-18 20:48:02

标签: arrays windows delphi callback delphi-xe6

我正在重做一些64位准备好的代码。这使用带回调的EnumWindows返回列表 运行delphi应用程序(除了IDE和它自己)然后被杀掉。本来 它使用TStringlist来保存这些应用程序的句柄。我想改变它直接以数字形式收集句柄。 我已经得到了一个非常令人满意的解决方案,使用通用的TList来收集句柄。

一路上,我最初尝试使用动态数组 - 它不起作用。在验证了TList解决方案后,我重新考虑了它,出于学术兴趣,并且 尝试使用动态数组来实现它的每种方式 - 都没有成功。虽然我确实遇到过,但我在文档中找不到任何禁令 在Rudy V博客中的这篇文章:" Delphi字符串和动态数组不应该作为参考计数类型传递给API函数......"

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

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;

1 个答案:

答案 0 :(得分:5)

如果没有使用动态数组实际看到您的实现,我假设您通过使用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轻松地从中返回动态数组。