delphiwinapidrag-and-dropdelphi-10.4-sydneygetfiles

Why do I get a stack overflow when I call the Windows API from my Delphi program?


My form supports drag'n'drop of files from the Windows Explorer:

uses 
  ShellApi, System.IOUtils;

procedure TFormMain.FormCreate(Sender: TObject);
begin
  DragAcceptFiles(Self.Handle, True);
end;

procedure TFormMain.WMDropFiles(var Msg: TMessage);
var
  hDrop: THandle;
  FileCount, NameLen, i: Integer;
  CurrFile: String;
  FileSysEntries: TArray<String>;
begin
  inherited;

  hDrop := Msg.wParam;
  try
    FileCount := DragQueryFile(hDrop, $FFFFFFFF, nil, 0);

    for i := 0 to FileCount - 1 do
    begin
      NameLen := DragQueryFile(hDrop, i, nil, 0) + 1; //+1 for NULL
      SetLength(CurrFile, NameLen);
      DragQueryFile(hDrop, i, PWideChar(CurrFile), NameLen);

      //If I don't do this...
      SetLength(CurrFile, StrLen(PWideChar(CurrFile)));

      if DirectoryExists(CurrFile) then
      begin
        //...I get a stack overflow here!
        FileSysEntries := TDirectory.GetFiles(CurrFile, '*.*', TSearchOption.soAllDirectories);
        //Rest removed for clarity...
      end;
    end;
  finally
    DragFinish(hDrop);
  end;
end;

Now if I don't strip the NULL (#0) character off the CurrFile string (see 2nd SetLength) I get a stack overflow when I call TDirectory.GetFiles and I'm now sure why.

Is the second SetLength (that strips #0) really necessary or should I do NameLen - 1 for the first SetLength? Or maybe something else?


Solution

  • I see a few issues:

    Try this instead:

    uses 
      ShellApi, System.IOUtils;
    
    procedure TFormMain.CreateWnd;
    begin
      inherited;
      DragAcceptFiles(Self.Handle, True);
    end;
    
    procedure TFormMain.WMDropFiles(var Msg: TMessage);
    var
      hDrop: THandle;
      FileCount, NameLen, i: Integer;
      CurrFile: String;
      FileSysEntries: TArray<String>;
    begin
      hDrop := Msg.wParam;
      try
        FileCount := DragQueryFile(hDrop, $FFFFFFFF, nil, 0);
    
        for i := 0 to FileCount - 1 do
        begin
          NameLen := DragQueryFile(hDrop, i, nil, 0);
          SetLength(CurrFile, NameLen);
          DragQueryFile(hDrop, i, PChar(CurrFile), NameLen + 1);
    
          if TDirectory.Exists(CurrFile) then
          begin
            FileSysEntries := TDirectory.GetFiles(CurrFile, '*.*', TSearchOption.soAllDirectories);
            //...
          end;
        end;
      finally
        DragFinish(hDrop);
      end;
    end;