Показать сообщение отдельно
  #6  
Старый 17.04.2009, 22:15
Nyctos Kasignete Nyctos Kasignete вне форума
Активный
 
Регистрация: 29.03.2009
Сообщения: 300
Репутация: 94
По умолчанию

Есть на самом деле еще одно обстоятельство, о котором я не сказала. Тот код, который выше, я писала в предположении, что у запускаемого приложения будет единственное окно. А именно, функция EnumThrWndProc возвратит False сразу как только найдет первое окно, принадлежащее нити.
Цитата:
Сообщение от Nyctos Kasignete
Код:
Result := not IsWindow(hndl);
Когда callback-функция возвращает False, перебор прекращается, независимо от того, есть ли еще окна, принадлежащие потоку, или нет. Так вот, если не останавливать перебор, то можно получить дескрипторы всех окон в потоке... Для этого функция EnumThrWndProc всегда должна возвращать True. Но как из этой полученной кучи дескрипторов получить именно handle главной формы? Вот над этим пока и думаю сейчас...

__________________________________

Сегодня между делом ломала голову над вышеобозначенным вопросом. Уж не знаю, продолжает ли работать над проблемой сам автор темы... В общем, пришла к выводу, что решение задачи в общем виде (т.е. для вообще произвольного многооконного приложения, копии которого мы запускаем) найти у меня не получается. =(

Вся проблема состоит в том, чтобы по заданному набору дескрипторов, соответствующих окнам многооконного приложения, выбрать тот единственный, который принадлежит главному окну. Не понятно, по какому критерию можно опознать, что данное окно — главное. Хотела найти лазейку через стили (и расширенные стили) окна, считывая их функцией GetWindowLong. Понятное дело, можно отсеять те окна, которые, например, обладают расшренным стилем WS_EX_TOOLWINDOW. Но на самом деле не исключены такие окна в многооконном приложении, которые имеют в точности тот же стиль, что и главное... Так что, наверное, не выход.

Была идея воспользоваться тем обстоятельством, что обычно приложение создает лишь одну кнопку на панели задач, соответствующую главному окну, а дополнительные окна не светятся на панели задач. Здесь написано, как определить, какие окна видятся на панели задач. Но проблема в том, что и для главного окна многооконного приложения не выполняется условие GetWindowLong(Wnd, GWL_HWNDPARENT) = 0, т.е. главное, как и остальные окна, имеет родительское окно программы, общее для всех окон программы. Это не то окно, которое нужно нам.

Короче, уже надоело писать. X( Скажу только, что на данный момент вижу один способ дальнейших действий. Все-таки надо забрать дескрипторы всех окон нити, а потом на основании каких-то отличительных признаков главного окна той самой, конкретной программы выбрать из имеющихся окон нужное. Ну ведь чем-то отличается все-таки главное окно от остальных??
Код немного переделала. Теперь не нужно каждый раз перед запуском очередной копии делать задержку. Делается одна-единственная общая задержка после того как все нужные копии программы отправлены на запуск с помощью CreateProcess. После этого проходимся по всем запущенным нитям и собираем дескрипторы окон.
Код:
type
  THandles = array of HWND;

  { новый тип — структура, в которой хранится идентификатор процесса
    и набор дескрипторов ассоциированных с ним окон}
  TThreadIdHandles = record
    ThreadId: Cardinal;
    Handles: THandles;
  end;

  TForm1 = class(TForm)
    StartProgramBtn: TButton;
    procedure StartProgramBtnClick(Sender: TObject);
  private
    { Private declarations }
    procedure GetAllWndHandlesFromThreads; // новая общая процедура перебора
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ThreadIdHandles: array[0..3] of TThreadIdHandles; // допустим, будем запускать четыре копии программы
  function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall;

implementation

{$R *.dfm}

function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall;
var
  ToolWndStyle: Integer;
  PrevHandlesLen: Integer;
begin
  Result := True;
  ToolWndStyle := GetWindowLong(hndl, GWL_EXSTYLE) and WS_EX_TOOLWINDOW;
  { убираем ненужные окна, имеющие стиль WS_EX_TOOLWINDOW }
  if ToolWndStyle = 0 then
     with ThreadIdHandles[lprm] do
     begin
       PrevHandlesLen := Length(Handles);
       SetLength(Handles, PrevHandlesLen + 1);
       Handles[PrevHandlesLen] := hndl;
     end;
end;

procedure TForm1.StartProgramBtnClick(Sender: TObject);
const
  WinDirPath = 'D:\Path_to_program_directory';
  NPadPath = 'D:\Path_to_program_directory\Program.exe';
var
  i: Byte;
  _si: STARTUPINFO;
  _pi: PROCESS_INFORMATION;
  Evnt: THandle;
begin
  for i := 0 to 3 do
  begin
    ThreadIdHandles[i].Handles := nil;
    FillChar(_si, SizeOf(_si), 0);
    _si.cb := SizeOf(_si);
    _si.dwFlags := STARTF_USESHOWWINDOW;
    _si.wShowWindow := SW_SHOWNORMAL;

    CreateProcess(@NPadPath[1], nil, nil, nil, False, NORMAL_PRIORITY_CLASS, nil,
                  @WinDirPath[1], _si, _pi);
    ThreadIdHandles[i].ThreadId := _pi.dwThreadId;
  end;

  // делаем лишь однократную задержку перед вызовом общей процедуры перебора
  Evnt := CreateEvent(nil, False, False, nil);
  WaitForSingleObject(Evnt, 1500);
  CloseHandle(Evnt);
  Form1.GetAllWndHandlesFromThreads();
end;

procedure TForm1.GetAllWndHandlesFromThreads;
var
  i: Byte;
begin
  for i := 0 to 3 do
    EnumThreadWindows(ThreadIdHandles[i].ThreadId, @EnumThrWndProc, i);
end;
Ответить с цитированием