-
Хочу написать програмку для переключения задач для Windows Mobile. Решил поэкспериментировать на обычном windows 7. Вобщем возникли вопросы: 1. Как этот самый список получить. Пробую так, вроде работает, правильно ли: Wnd := GetWindow(Application.Handle, gw_HWndFirst);
while Wnd <> 0 do
begin
if IsWindowVisible(Wnd)
and (GetWindow(Wnd, gw_Owner) = 0)
and (GetWindowText(Wnd, buff, sizeof(buff)) <> 0)
then
begin
GetWindowText(Wnd, buff, sizeof(buff));
ListBox1.Items.AddObject(StrPas(buff), TObject(Wnd));
end;
Wnd := GetWindow(Wnd, gw_hWndNext);
end;
Пример взял из сети. Смущает такой факт: Откуда брать Application.Handle, если писать на winapi? Может есть более правильный пример? ToolHelp не хочу использовать. 2. Как правильно получить заголовок окна. Теоретически ведь между GetWindowTextLength и GetWindowText заголовок окна может измениться, и выделенного буфера не хватит. 3. Как правильно получить иконку приложения. Делаю так: Ico := SendMessage(HWnd(Items.Objects[Index]), WM_GETICON, ICON_SMALL2, 0);
if Ico <= 0 then
Ico := GetClassLong(HWnd(Items.Objects[Index]), GCL_HICONSM);
if Ico > 0 then
DrawIconEx(Canvas.Handle, Rect.Left + 1, Rect.Top + 1, Ico, 16, 16, 0, 0, DI_NORMAL);
Нужно ли как-то освобождать Ico? 4. Как правильно активировать то или иное приложение? Делаю так: Wnd := HWND(Items.Objects[ItemIndex]);
if IsIconic(Wnd) then
SendMessage(Wnd, WM_SYSCOMMAND, SC_RESTORE, 0);
SetForegroundWindow(Wnd);
5. Как правильно закрыть приложение. Тут пока я никак не делаю, но думаю посылать WM_CLOSE или WM_QUIT. Как правильно, не знаю. Подскажите, где что не так, где так и что почитать можно?
-
> Дмитрий С © (06.04.10 09:46)
> 1. Как этот самый список получить. Пробую так, вроде работает, > правильно ли:
Правильно - EnumWindows. Для определения наличия кнопки на таскбаре можно использовать нечто подобное http://rsdn.ru/forum/delphi/525933.aspx > Откуда брать Application.Handle, если писать на winapi?
Брать то свое окно, которое имеет кнопку на таскбаре. > Теоретически ведь между GetWindowTextLength и GetWindowText > заголовок окна может измениться, и выделенного буфера не > хватит.
GetWindowText возвращает количество считанного. Можно взять буфер с заведомым запасом. > Нужно ли как-то освобождать Ico?
IMHO, не нужно. > Wnd := HWND(Items.Objects[ItemIndex]);
Хендлы окон не стоит хранить в списке, бо, необходимо поддерживать его актуальность. Если это необходимо, нужно ставить хуки на потоки, владеющие этими окнами, для получения уведомлений о их разрушении. > 5. Как правильно закрыть приложение. > Тут пока я никак не делаю, но думаю посылать WM_CLOSE или > WM_QUIT. Как правильно, не знаю.
Например, WM_QUIT каждому GUI потоку приложения. Но, это тоже, не совсем корректно, бо с несохраненными данными можно попрощаться. Но, в любом случае, придется держать в запасе TeminateProcess. -- Regards, LVT.
-
> Как этот самый список получить.
EnumWindows() потом WH_SHELL ловушка или таймер и опять тот же EnumWindows(). Explorer похоже использует и таймер и WH_SHELL Я вот так проверял когда то:
function IsAppWindow(Wnd: HWND): BOOL; stdcall;
var
Style, ExtStyle: LongInt;
hOwner, hParent: HWND;
begin
Result := true;
if not IsWindow(Wnd) then
begin
Result := false;
exit;
end;
Style := GetWindowLong(Wnd, GWL_STYLE);
ExtStyle := GetWindowLong(Wnd , GWL_EXSTYLE);
hOwner := GetWindow(Wnd, GW_OWNER);
hParent := GetParent(Wnd);
if (Style and WS_CHILD) <> 0 then result := false;
if (Style and WS_VISIBLE) = 0 then Result := false;
if not IsWindowVisible(Wnd) then Result := false;
if (ExtStyle and WS_EX_TOOLWINDOW) <> 0 then Result := false;
if (ExtStyle and WS_EX_MDICHILD) <> 0 then Result := false;
if (hOwner <> 0) and ((ExtStyle and WS_EX_APPWINDOW) = 0) then result := false;
if (hOwner <> 0) and Result then Result := not IsAppWindow(hOwner);
if hParent <> 0 then Result := false;
if GetWindowLong(Wnd , GWL_USERDATA) = MagicDWord then Result := false;
end;
-
> 2. Как правильно получить заголовок окна. > Теоретически ведь между GetWindowTextLength и GetWindowText > заголовок окна может измениться, и выделенного буфера не > хватит.
WH_SHELL кстати уведомляет об изменение заголовка или иконки
function GetTextFromWindow(hWnd: HWND): string;
var
TextLength: integer;
Text: PChar;
begin
TextLength := GetWindowTextLength(hWnd) + 1;
GetMem(Text, TextLength * SizeOf(Char));
GetWindowText(hWnd, Text, TextLength);
Result := Text;
FreeMem(Text, TextLength * SizeOf(Char));
end;
> 3. Как правильно получить иконку приложения.
function _GetFileAssociatedIcon(FileName: string; bSmall: boolean): HICON;
var
FileInfo: SHFILEINFO;
BIG_OR_SMALL_ICON: integer;
begin
if bSmall then
BIG_OR_SMALL_ICON := SHGFI_SMALLICON
else
BIG_OR_SMALL_ICON := SHGFI_LARGEICON;
SHGetFileInfo(PChar(FileName),
FILE_ATTRIBUTE_NORMAL,
FileInfo,
SizeOf(FileInfo),
SHGFI_ICON or BIG_OR_SMALL_ICON or SHGFI_SYSICONINDEX);
Result := FileInfo.hIcon;
end;
function _GetProcessFileNameByWindowHandle(Wnd: HWND): string;
var
hProcess: THandle;
PID: Cardinal;
FileName: array [1..MAX_PATH] of char;
PE: TProcessEntry32;
Snap: Cardinal;
OsVerInfo: TOSVersionInfo;
begin
Result := '';
GetWindowThreadProcessId(Wnd, @PID);
hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
OsVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
if GetVersionEx(osVerInfo) then
begin
if OsVerInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then
begin
ZeroMemory(@Filename[1], SizeOf(Filename));
GetModuleFileNameEx(hProcess, 0, @Filename[1], SizeOf(Filename));
Result := FileName;
end
else
begin
Snap := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
if Snap <> -1 then
begin
PE.dwSize:=SizeOf(PE);
if Process32First(Snap, PE) then
repeat
if PE.th32ProcessID = PID then Result := PE.szExeFile;
until not Process32Next(Snap, PE);
end;
end;
end;
CloseHandle(hProcess);
end;
function GetIconFromWindow(hWnd: HWND): HICON; stdcall;
const
ICON_SMALL2 = 2;
begin
Result := 0;
SendMessageTimeout(hWnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then Result := GetClassLong(hWnd, GCL_HICONSM);
if Result = 0 then SendMessageTimeout(hWnd, WM_GETICON, ICON_SMALL, 1,
SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_GETICON, ICON_SMALL2, 1,
SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_QUERYDRAGICON, ICON_SMALL,
0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_QUERYDRAGICON, ICON_SMALL2,
0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then Result := GetClassLong(hWnd, GCL_HICON);
if Result = 0 then SendMessageTimeout(hWnd, WM_GETICON, ICON_BIG, 1,
SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then SendMessageTimeout(hWnd, WM_QUERYDRAGICON, ICON_BIG,
0, SMTO_ABORTIFHUNG, 1000, Cardinal(Result));
if Result = 0 then Result := _GetFileAssociatedIcon(_GetProcessFileNameByWindowHandle(hWnd), true);
if Result = 0 then Result := _GetFileAssociatedIcon(_GetProcessFileNameByWindowHandle(hWnd), false);
if Result = 0 then Result := LoadIcon(0, IDI_APPLICATION);
end;
-
> 4. Как правильно активировать то или иное приложение?
Проводник пользуется недокументированной функцией SwitchToThesWindow, так что либо ей либо вот аналог
function ForceForegroundWindow(hWnd: HWND): BOOL; stdcall;
const
SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
OsVerInfo: TOSVersionInfo;
Win32MajorVersion: Integer;
Win32MinorVersion: Integer;
Win32Platform: Integer;
ForegroundThreadID: DWORD;
ThisThreadID: DWORD;
Timeout: DWORD;
begin
OsVerInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
GetVersionEx(osVerInfo);
Win32MajorVersion := OsVerInfo.dwMajorVersion;
Win32MinorVersion := OsVerInfo.dwMinorVersion;
Win32Platform := OsVerInfo.dwPlatformId;
if IsIconic(hWnd) then ShowWindow(hWnd, SW_RESTORE);
if GetForegroundWindow = hWnd then Result := True
else
begin
if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and ((Win32MajorVersion > 4)
or ((Win32MajorVersion = 4) and (Win32MinorVersion > 0)))) then
begin
Result := False;
ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
ThisThreadID := GetWindowThreadPRocessId(hWnd, nil);
if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
begin
BringWindowToTop(hWnd);
SetForegroundWindow(hWnd);
AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
Result := (GetForegroundWindow = hWnd);
end;
if not Result then
begin
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @Timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil,
SPIF_SENDCHANGE);
BringWindowToTop(hWnd);
SetForegroundWindow(hWnd);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(Timeout),
SPIF_SENDCHANGE);
end;
end
else
begin
BringWindowToTop(hWnd);
SetForegroundWindow(hWnd);
end;
Result := (GetForegroundWindow = hWnd);
end;
end;
-
> DVM © (06.04.10 15:57) [4]
Только, только начали избавляться от попрыгунчиков, так ты тут со своим кодом вылез.
-
> Anatoly Podgoretsky © (06.04.10 16:37) [5]
Ну тут как ни крути это надо. Если надо переключать окна как панель задач.
-
> DVM (06.04.2010 16:40:06) [6]
Мне за последствия страшно.
-
> DVM © (06.04.10 15:57) [4]
Это сильно, конечно. AttachThreadInput нужен для SetForegroundWindow?
> DVM © (06.04.10 15:55) [3]
Спасибо за код. Вопрос только: Разве панель задач загружает иконку из ресурса exe файла, если из других мест достать не удалось?
> Leonid Troyanovsky © (06.04.10 11:34) [1]
Спасибо за ссылку, то что нужно. Сделал через EnumWindows, в таком случае своего окна не требуется вовсе.
Так, все-таки как штатно закрыть приложение, чтобы в случае чего, оно могло отобразить диалог (запрос на сохранение), послать WM_CLOSE или WM_SYSCOMMAND, SC_CLOSE? И как не штатно (тут думаю TerminateProcess)?
-
> Дмитрий С © (07.04.10 03:00) [8]
> AttachThreadInput нужен для SetForegroundWindow?
Не нужен. Если SetForegroundWindow вызывается при выполнении условий, описанных в msdn, то фокус будет передан. Иначе, будет FlashWindow.
> Так, все-таки как штатно закрыть приложение, чтобы в случае > чего, оно могло отобразить диалог (запрос на сохранение),
WM_SYSCOMMAND SC_CLOSE окну, чья кнопка на таскбаре, IMHO. Только, наверное, SendMessageTimeout. У оного есть и флаг SMTO_ABORTIFHUNG. .
Ну, и TerminateProcess, на крайний случай.
-- Regards, LVT.
-
-
> DVM (07.04.2010 12:13:10) [10]
И даже наоборот, сначали из ресурса, а потом уж как нибудь. Поэтому в вход в папку с тысячами файлов так медленен, извлекаются иконки и уж как следствие проверка антивирусом.
-
> Anatoly Podgoretsky © (07.04.10 14:42) [11]
Причем тут это-то?
> По моим наблюдениям, которые я проводил лет 7 назад - да.
Кстати в Win7 при группировке кнопок панели задач, таскбар именно из ресурса иконку и берет.
> Leonid Troyanovsky © (07.04.10 10:34) [9]
Чуть позже продолжу разбираться и тестировать на КПК, подниму тему, если будут вопросы, пока вопросов нет, большое спасибо.
-
> Дмитрий С © (07.04.10 14:45) [12]
> Чуть позже продолжу разбираться и тестировать на КПК, подниму > тему, если будут вопросы,
А далее я - пас, там, наверное, какой-нить WinCE.
-- Regards, LVT.
|