Kan dynamiske arrayer bruges som parametre til Windows tilbagekaldsfunktioner?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg gør en kode til 64 bit klar. Dette bruger EnumWindows med tilbagekald for at returnere en liste
af at køre delphi applikationer (bortset fra IDE og sig selv), som derefter dræbes. Oprindeligt
det brugte en TStringlist til at holde håndtagene til disse applikationer. Jeg ønskede at ændre det for at samle håndtagene direkte i numerisk form.
Jeg er kommet til en meget tilfredsstillende løsning vist nedenfor ved hjælp af en generisk TList til at samle håndtagene.


Undervejs forsøgte jeg først at bruge et dynamisk array - det fungerede ikke. Efter at have verificeret TList-løsningen, besøgte jeg det uden akademisk interesse og
prøvede enhver måde at implementere den ved hjælp af et dynamisk array i stedet - alt uden succes. Jeg kunne ikke finde noget forbud i dokumentationen, selvom jeg kom på tværs af det
denne note i Rudy Vs blog: 'Delphi-strings og dynamiske arrayer bør ikke bestås som referencetællede typer til API-funktioner alligevel ...'


Så jeg søger bare en 'rulning', at dynamiske arrayer kan eller ikke kan bruges som parametre til callback funktioner.


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;

Bedste reference


Uden at du faktisk ser din implementering ved hjælp af et dynamisk array, antager jeg, at du tilføjer elementer til dette array ved at udvide det med SetLength. Dette ændrer igen array-variablen, som internt er en pointer. Således bruger opkaldsmetoden stadig den gamle pegervariabel til den ikke mere eksisterende dynamiske array, den passerede som en parameter.


Du kan overvinde dette ved at bruge en peger til et dynamisk array.


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;


Når det er sagt, foretrækker jeg personligt TList-tilgangen for enkelhed og klarhed. Især som du nemt kan returnere et dynamisk array fra det med AList.ToArray.