Delphi сделать окно активным

Обновлено: 29.04.2024

Основы работы с Windows API - Формы Delphi и окна Windows

Содержание материала

Принято считать, что класс TForm реализует окно. Это не совсем верно, потому что TForm реализует лишь часть тех объектов, которые принято называть окнами. Например, кнопка - это тоже окно, но реализуется она классом TButton.

Кроме имени, класс включает в себя другие параметры, такие как стиль, кисть и т. д. Они подробно перечислены в справке по теме WNDCLASS.

Кроме класса нужно указать стиль окна и расширенный стиль. Они определяют поведение конкретного окна и не имеют ничего общего со стилем класса. Возможные значения этих стилей перечислены в справке по функциям CreateWindow и CreateWindowEx. Результатом работы этих функций является дескриптор созданного ими окна.

Функции, создающие окна, требуют указать дескриптор приложения. В Delphi этот дескриптор хранится стразу в двух переменных - MainInstance модуля System и HInstance модуля SysInit. Оба эти модуля автоматически подключаются к любому модулю, созданному в Delphi, так что можно использовать ту или иную переменную по своему вкусу. Кстати, не следует путать автоматическое подключение этих модулей с автоматической генерацией кода IDE Delphi для под-ключения таких модулей, как Windows, Forms, SysUtils и т. д. В первом случае модули подключаются несмотря на то, что не упомянуты в списке uses. Более того, их упоминание там приведёт к ошибке. Во втором случае эти модули явным образом подключаются, просто Delphi автоматически пишет эту часть программы за программиста. Можно написать модуль или даже целую программу, которые не будут использовать SysUtils, но нельзя написать такие, которые не будут использовать System.

Создание окон через Win API требует кропотливой работы. VCL Delphi справляется с этой задачей замечательно, поэтому создавать окна самостоятельно приходится только тогда, когда использование VCL нежелательно, например, если необходимо написать как можно более компактное приложение. Во всех остальных случаях приходится только слегка подправлять работу VCL. Например, с помощью Win API можно изменить форму окна или убрать из него заголовок, оставив рамку. Подобные действия не требуют от программиста создания нового окна, можно воспользоваться тем, что уже создано VCL.

Другой случай, когда могут понадобиться функции Win API для окон - если приложение должно что-то делать с чужими окнами. Например, хотя бы просто перечислить все окна, открытые в данный момент, как это делает WinSight32. Но в этом случае также не приходится самому создавать окна, работа идёт с уже имеющимися.

"A good example is a debugger when it hits a breakpoint."


Добавлено
не работает, причем честно об этом говорит (

Сначала нужно вызвать

SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID)0, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);

Зтем можно вызывать SetForegroundWindow.

Но это несовсем корректная техника. Для того и изменили поведение функции SetForegroundWindow, чтобы некоторые не доставали своими выскакивающими табличками.

сначала сворачиваем нашу прогу
Application.Minimize;

если "сворачивали в трей", то
if not IsWindowVisible(Application.Handle) then
ShowWindow(Application.Handle, SW_SHOW);

ну и восстанавливаем.
Application.Restore;
а уже при ресторе окно вылазит вперёд

кстати, если кто-то предложит лучший метод (без Minimize), буду очень благодарен

Что значит что с ним что без него?
Функция SystemParametersInfo вообще отработала корректно или ты не проверял на предмет ошибки?

Так вот если не проверял то проверь. Это делается с помощью GetLastError если функция SystemParametersInfo вернёт тебе ноль.

Если же функция отработала успешно, то вызови её же но с параметром SPI_GETFOREGROUNDLOCKTIMEOUT и убедись что она возвратит ноль.

После корректного вызова этой функции и установки свойства SETFOREGROUNDLOCKTIMEOUT в ноль система будет сразу делать окно активным с помощью SetForegroundWindow. Система не будет вызывать функцию FlashWindowEx.

я не знаю, какие у вас винды, а уменя WinXp.
под Win2000 всё точно так же.

Да, такая ситуация тоже возможна и она является штатной. Если текущий foreground-процесс вызвал функцию LockSetForegroundWindow.

Согласен что нужно. Но это просто один из вариантов как всё-таки сделать своё окно поверх всех, даже тогда когда никто этого нехочет и всеми средствами препятствует этому. Появится активное системное модальное окно, а после его закрытия уже делай основым то из своих окно которое необходимо.

На некорректные параметры такую ошибку система не выдаст. С параметрами у него судя по всему всё в порядке. А вот поотключать все лишние запущенные приложения попробовать стоит. Фалит из-за одного из них.

← →
AKA ( 2004-10-15 02:13 ) [0]

Как сделать приложение активным т.е. чтобы его окно было поверх всех других если запустить его так:

if CreateProcess(nil,PChar("test.exe"),nil,nil,False,0,nil,PChar("c:\"),SI,PI)

← →
AKA ( 2004-10-15 18:47 ) [1]

Блин пробовал разными способами:

PostMessage(Handle, WM_ACTIVATE, 0, 0);
SendMessage(Handle, WM_ACTIVATE, WA_CLICKACTIVE, 0);
BringWindowToTop(Handle);

работает - но не устраивает то что окно сначало сворачивается .

Есть у кого еще какие-нибудь версии .

← →
GanibalLector © ( 2004-10-15 20:00 ) [4]

>Как сделать приложение активным ?
if GetActiveWindow<>некоторый HWND then SetActiveWindow(некоторый HWND);

si.wShowWindow:=sw_show;
PostMessage (Handle, WM_SYSCOMMAND, SC_restore, 0);
BringWindowToTop(Handle);
si.dwFlags := STARTF_RUNFULLSCREEN;
si.wShowWindow := 4;
ARC30.Enabled:=true;
FlashWindow(Handle,true);
Showwindow(Handle,sw_show);
SetWindowPos(Handle,HWND_BOTTOM,1,1,200,200,SWP_SHOWWINDOW );
PostMessage(Handle, WM_ACTIVATE, 0, 0);
ShowWindow(Handle, SW_RESTORE);
SendMessage(Handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
SendMessage(Handle, WM_ACTIVATE, WA_CLICKACTIVE, 0);
BringWindowToTop(Handle);
SetForegroundWindow(.Handle);
SendMessage(Handle,WM_LButtonDown,1,1);
SendMessage(Handle,WM_LButtonUP,1,1);
SendMessage(Handle, WM_ACTIVATEAPP, 0, 0);
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE + SWP_NOMOVE);

Работает только это:
Application.Minimize;
Application.Restore;
Application.BringToFront;

Ниужеле нет никакого способа .

← →
GanibalLector © ( 2004-10-16 02:03 ) [8]

Работает только это
Не верю.
Вы [6] пробовали .

← →
DDA © ( 2004-10-16 16:27 ) [9]

тебе поможет вот это,точно

//-------------------------------
function ForceForegroundWindow(hwnd: THandle): boolean;
const
SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
ForegroundThreadID: DWORD;
ThisThreadID: DWORD;
timeout: DWORD;
begin
if IsIconic(hwnd) then
ShowWindow(hwnd, SW_RESTORE);

if not Result then
begin
// Code by Daniel P. Stasinski
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0), SPIF_SENDCHANGE);
BringWindowToTop(hwnd); // IE 5.5 related hack
SetForegroundWindow(hWnd);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
end;
end
else
begin
BringWindowToTop(hwnd); // IE 5.5 related hack
SetForegroundWindow(hwnd);
end;

Result := (GetForegroundWindow = hwnd);
end;
end; < ForceForegroundWindow >
//-----------------------------------------
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ForceForegroundWindow(Application.Handle);
end;

← →
AKA ( 2004-10-16 21:46 ) [10]

НАКОНЕЦ-ТО работает .
ОГРОМНОЕ СПАСИБО тебе DDA .

← →
DelphiLexx © ( 2006-10-16 11:41 ) [0]

Мое приложение должно работать в одном экземпляре для этого я используя:

function
CheckAppRun:
integer;
var
Map: THandle;
begin
Map := CreateFileMapping($FFFFFFFF, nil, PAGE_READONLY, 0, 32,
"%af_src_comm_0");
if Map = 0 then
Result := -1
else if GetLastError = ERROR_ALREADY_EXISTS then
Result := 1
else
Result := 0
end;

function
ActivatePreviousInstance(WndClass: PChar): boolean;
var
Wnd: HWND;
begin

if ActivatePreviousInstance("TMainForm") then Halt(1);

Как-то так:

Application.Restore;
if Application.MainForm.WindowState = wsMinimized then
Application.MainForm.WindowState := wsNormal;
Application.MainForm.Visible := True;
SetForegroundWindow(Application.Handle);

← →
Leonid Troyanovsky © ( 2006-10-16 17:22 ) [6]


> DelphiLexx © (16.10.06 17:09) [4]

> Если окно приложения свернуто, то твой код не отображает

Только, окно приложения - это окно класса (windows) TApplication.
Если есть хендл любого другого окна приложения (дельфи, билдер),
то окно приложения можно найти как
happ := GetWindowLong(wnd, GWL_HWNDPARENT)

Не так.
Не foreground приложение не может сделать себя
(другого) foreground.

--
Regards, LVT.

← →
RWolf © ( 2006-10-16 17:39 ) [8]

мое предложение заключалось в том, что делать foreground-ом приложение будет именно себя, свой экземпляр; см. [1]

← →
Leonid Troyanovsky © ( 2006-10-16 17:50 ) [9]


> RWolf © (16.10.06 17:39) [8]
> мое предложение заключалось в том, что делать foreground-
> ом приложение будет именно себя, свой экземпляр; см. [1]

Просто я как раз такой механизм использую для восстановления программы из трея при запуске 2 экземпляра.
Или мы говорим о разных вещах?

← →
Leonid Troyanovsky © ( 2006-10-16 18:35 ) [11]


> RWolf © (16.10.06 18:01) [10]

> Или мы говорим о разных вещах?

Мои утверждения вполне, IMHO, применимы
к любым не-foreground windows приложениям.

var
MIError: Integer;

const
MI_ERROR_NONE = 0;
MI_ERROR_CREATINGMAP = 1;
MI_ERROR_MAPPINGFILE = 2;

function GetMEError(): Integer;

uses Windows, Forms;

var
hMap: THandle;
UniqueMes: Integer;

function GetMEError(): Integer;
begin
Result := MIError;
end;

procedure InitInstance;
var
pFile: Pointer;
hApp: HWND;
const
pFileSize = SizeOf(HWND);
begin
hMap := CreateFileMapping(INVALID_HANDLE_VALUE,
nil,
PAGE_READWRITE,
0,
pFileSize,
UNIQUE_APP_STR);
if GetLastError = ERROR_ALREADY_EXISTS
then
begin
Application.ShowMainForm := False;
pFile := MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if pFile <> nil
then
begin
hApp := HWND(pFile^);
ShowWindow(hApp, SW_SHOWNORMAL);
SetForegroundWindow(hApp);
UnmapViewOfFile(pFile);
end
else MIError := MIError or MI_ERROR_MAPPINGFILE;
Application.Terminate;
end
else
if hMap = 0
then MIError := MIError or MI_ERROR_CREATINGMAP
else
begin
pFile := MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
if pFile <> nil
then
begin
CopyMemory(pFile, @Application.Handle, pFileSize);
UnmapViewOfFile(pFile);
end
else MIError := MIError or MI_ERROR_MAPPINGFILE;
end;
end;

initialization
UniqueMes := RegisterWindowMessage(UNIQUE_APP_STR);
InitInstance;

finalization
if hMap <> 0
then CloseHandle(hMap);

Не желательно, а : Обязательно изменить!

Вообще-то, подобные вещи должны браться из реестра.
Т.е., сделать в HKCU раздел формата
\Software\CompanyName\ProductName\..
где второе, третье и др. брать из Version Info,
ключ ActivatePrevInstance (REG_DWORD) 0x1.
Т.е., имя проекции файла формировать из уникальной цепочки.

Кроме того, для активации предыдущей копии полезно
передавать дополнительно параметры комстроки.
Этого можно добиться, например, перечитыванием mmf
при Application.OnActivate. Хотя, это потребует некоторых
усилий по синхронизации. Например, четыре байта файла
использовать для переменной, которая будет
InterLockedCompareExchange.

Ну, и для приложений с формами можно предложить
вполне сравнимый способ без использования
объектов ядра.

> Ну, и для приложений с формами можно предложитьвполне сравнимый
> способ

>

Так предложите. Или хотя бы намекните :-)

>> Ну, и для приложений с формами можно предложить

> Так предложите. Или хотя бы намекните :-)

В CreateWnd (override) главной формы делаем (до inherited)
EnumWindows для поиска окон формы по ее классу, и,
в случае обнаружения такого окна, посылаем ему через
SendMessageTimeOut(wnd, WM_COPYDATA,
параметры комстроки, а также, в cds.dwData - искомый uid.

А в обработчике WM_COPYDATA главной формы полученный uid
сравнивается с собственным, и, в случае равенства,
возвращается (после приема комстроки) Result <> 0.

Т.е., если SendMessageTO успешно - активируем предыдущую копию,
иначе - вызываем inherited для создания окна формы.

Вот, собс-но, вся схема.

> Т.е., если SendMessageTO успешно - активируем предыдущую
> копию,

Для того, чтобы сделать что-нибудь над каким-либо окном нужно сначала получить его дескриптор, т.е. его положение в оперативной памяти. Для этого нужно использовать функцию FindWindow. Ей нужно указать всего два параметра: сначала класс искомого окна, затем его заголовок. Ну с заголовком проблем вообщем-то нет - его мы видим, но вот как определить класс. ведь он скрыт от глас пользователя. В действительности мы может указать только заголовок окна, а вместо класса ставим nil.

Для начала запустите стандартную программу "Блокнот" - и что же мы видим? В блокноте в заголовке окна отслеживается имя текущего файла. Изначально, т.к. файла нет в использовании, заголовок блокнота выглядит так: "Безымянный - Блокнот". Постараемся по этому критерию найти окно блокнота. Выглядеть это будет так:

if FindWindow( nil , 'Безымянный - Блокнот' ) <> 0 then

ShowMessage( 'Окно найдено' )

ShowMessage( 'Окно НЕнайдено' );

Далее попробуем передвинуть это окно

h := findwindow( nil , 'Безымянный - Блокнот' );

SetWindowPos(h, HWND_BOTTOM, 1 , 1 , 20 , 20 , swp_nosize);

Опять находим блокнот. Его дескриптор помещаем в переменную класса HWND[С английского Handle Window - дескриптор окна]. Далее используем функцию SetWindowPos для задания позиции. В качестве параметров нужно указать:

Дескриптор окна, которое хотим переместить

Идентификатор окна, которое предшествует перемещаемому окну в Z-последовательности. Z-последовательность это порядок, в котором формировались окна. Данный параметр указывает с какого именно окна необходимо начинать писк. В качестве значений может принимать либо дескриптор какого-либо окна в системе, либо одно из нижеследующих значений:

HWND_BOTTOM Начало Z-последовательности

HWND_NOTOPMOST Первое окно которое располагается не "поверх все окон"

HWND_TOP Вершина Z-последовательности

HWND_TOPMOST Первое окно которое располагается "поверх все окон"

Позиция окна по горизонтали

Позиция окна по вертикали

Спецификаторы изменения позиции и размеров окна[флаги]. Для задания значения можно комбинировать следующие константы

SWP_DRAWFRAME Прорисовка фрейма вокруг окна.

SWP_HIDEWINDOW Скрывает окно.

SWP_NOACTIVATE Не активизирует окно. Если же этот флаг не будет поставлен, окно активизируется и будет перемещено поверх всех окон. А вот встанет ли окно даже выше тех окон, которым задано HWND_TOPMOST или нет зависит от параметра hWndInsertAfter.

SWP_NOCOPYBITS Если этот спецификатор не будет установлен, тогда содержимое клиентской области окна будет скопировано и вставлено во вновь отобразившееся окно после его перемещения.

SWP_NOMOVE Сообщает, что нужно игнорировать параметры задания позиции окну.

SWP_NOOWNERZORDER Сообщает, что не следует изменять позицию окна владельца в Z-последовательности.

SWP_NOREDRAW Не перерисовывает окно.

SWP_NOREPOSITION Такой же как и SWP_NOOWNERZORDER.

SWP_NOSIZE Сообщает, что нужно игнорировать параметры задания размеров окну.

SWP_SHOWWINDOW Отображает окно.

Если данная функция выполнится успешно, она возвратит отличное от нуля значение. Ну, вот, теперь мы можем передвигать и изменять в размерах чужие окна. Для того, чтобы изменить заголовок окна напишем следующий код:

SetWindowText(FindWindow( nil , 'Безымянный - Блокнот' ),

'Дарова, ламерюга, типа ты попал. ' );

Функции setwindowtext нужно указать только два параметра: это дескриптор нужного окна и новое значение для заголовка. Вот вообщем-то и всё!

Есть ещё одна интересная функция ShowWindow, которая позволяет скрывать или отображать окна. Использовать её нужно так::

ShowWindow(FindWindow( nil , 'Безымянный - Блокнот' ), sw_hide);

В скобках указываем сначала над каким именно окном хотим издеваться, а затем что именно мы хотим с ним сделать. В качестве возможных действий можем указать:

SW_HIDE Скрывает окно и активизирует другое.

SW_MAXIMIZE Разворачивает окно.

SW_MINIMIZE Сворачивает окно.

SW_RESTORE Активизирует и выводит окно. Если окно было развёрнуто или свёрнуто - восстанавливает исходный размер и позицию.

SW_SHOW Активизирует и выводит окно с его оригинальным размером и положением.

SW_SHOWDEFAULT Активизирует с установками, заданными в структуре STARTUPINFO, которая была передана при создании процесса приложением запускающим нужную программу.

SW_SHOWMAXIMIZED Выводит окно в развёрнутом виде.

SW_SHOWMINIMIZED Выводит окно в виде пиктограммы на панели задач.

SW_SHOWMINNOACTIVE Выводит окно в свёрнутом виде на панели задач и не передаёт ему фокус ввода, т.е. окно, которое до этого было активно остаётся активно по прежнему.

SW_SHOWNA Отображает окно в его текущем состоянии. Активное окно остаётся активным по прежнему.

SW_SHOWNOACTIVATE Выводит окно в его последнем положении и с последними используемыми размерами. Активное окно остаётся активным по прежнему.

SW_SHOWNORMAL Выводит окно. Если оно было свёрнуто или развёрнуто - восстанавливает его оригинальные размеры и позицию

Но вся сложность действий заключается в том, что в заголовке Блокнота отслеживается имя текущего файла и использовать значение "Безымянный - Блокнот" мы можем не всегда : (. Тем более это не только в случае с блокнотом. Но есть выход: ведь функции FindWindow для поиска окна мы указываем не только заголовок нужного окна, но ещё его класс. Какой же это выход скажете вы, заголовок окна мы видим, значит знаем, что указывать - а класс окна. в действительности тоже может найти приложив немного усилий!

В пакет Delphi входим специальная утилита для отслеживание всех активных процессов, она называется WinSight32. Вот ею мы и воспользуемся. Запустите её, покопайтесь в списке процессов, ищите строку где значится текущий заголовок нужного окна, например Блокнота, и в левой части этой строки в фигурных скобках вы найдёте имя класса окна. Для блокнота это будет "Notepad". Теперь зная имя класса окна мы можем переписать поиск окна таким способом:

ShowWindow(FindWindow( 'Notepad' , nil ), sw_hide);

Теперь мы вместо заголовка окна указываем значение nil, игнорируя данный параметр.

Есть ещё один замечательный способ передачи команд окнам.- функция PostMessage. Ей в качестве параметров нужно указать:

NULL Ведёт себя как функция PostThreadMessage с переданным ей dwThreadId параметром.

Читайте также: