Привет всем.
Мне нужно узнать хэндл окна. Допустим, запускаю я cmd через ShellExecute, и нужно мне получить её хэндл. Через FindWindow нельзя, т.к. заголовок cmd может быть разный - у некоторых в начале приписывается "Администратор" (видимо, сидят под администратором). Я думаю, можно перебирать все окна и искать по слову cmd в заголовке. Попробовал ф-ю EnumWindows, вот так:
var mypointer:pointer;
t:hwnd;
function MyCallbackFunction(Wnd:HWnd; P: Pointer):Bool; stdcall;
begin
messagebox(0,pchar(GetModuleName(GetClassLong(wnd,GCL_HMODULE))),'',0);
if pos('CMD',uppercase(GetModuleName(GetClassLong(wnd,GCL_HMODULE))))>0 then t:=wnd;
end;
...
shellexecute(0,'open','cmd',0,0,sw_show);
EnumWindows(@MyCallbackFunction, LongInt(MyPointer));
if t=0 then messagebox(0,'dfdf','',0);
...
Кто тебе сказал, что имя модуля - это заголовок? Заголовок получается через GetWindowText...
var
t: HWND = 0;
function MyCallbackFunction(myWnd: HWND; lp: LPARAM): Bool; stdcall;
var
szCaption: array[0 .. MAXCHAR - 1] of Char;
begin
GetWindowText(myWnd, szCaption, MAXCHAR);
if pos('CMD', UpperCase(szCaption)) > 0 then t := myWnd;
result := True;
end;
procedure TForm1.Button11Click(Sender: TObject);
begin
ShellExecute(0, 'open', 'cmd', nil, nil, SW_SHOW);
// Подожди какое-то время, чтоб окно успело отобразиться
Sleep(1000);
EnumWindows(@MyCallbackFunction, LPARAM(0));
if t = 0 then MessageBox(0, 'Не нашел', '', MB_OK);
end;
Спасибо, это работает. Ещё вопрос, как можно сменить раскладку в чужом окне? Дело в том, что если эмулировать нажатия клавиатуры, то в другом окне они будут отображаться в соответствии с тамошней раскладкой, т.е. передаю ord('F') - появляется 'а'. На одном форуме я нашёл такую функцию:
(передаю хэндл нужного окна)
function ChangeLayout(
const RemoteHandle: THandle): Boolean;
var
Dumme: DWORD;
Layout: HKL;
begin
Layout := LoadKeyboardLayout('00000409', KLF_ACTIVATE);
Result := SendMessageTimeOut(RemoteHandle, WM_INPUTLANGCHANGEREQUEST,
0, Layout, SMTO_ABORTIFHUNG, 200, Dumme) <> 0;
if Result then Result := SendMessageTimeOut(RemoteHandle, WM_INPUTLANGCHANGEREQUEST,
DEFAULT_CHARSET, Layout, SMTO_ABORTIFHUNG, 200, Dumme) <> 0;
end;
procedure TForm1.Button12Click(Sender: TObject);
var h: HWND;
begin
h := FindWindow(nil, 'Untitled - Notepad');
if h <> 0 then
begin
if ChangeLayout(h) then ShowMessage('True')
else ShowMessage('False');
end
else
ShowMessage('Notepad was not found');
end;
раскладка блокнота тут же поменялась на английскую.
function ChangeRemoteWndKeyboardLayoutToRussian(?
const RemoteHandle: THandle): Boolean;
var
Dumme: DWORD;
Layout: HKL;
begin
Layout := LoadKeyboardLayout('00000419', KLF_ACTIVATE);
Result := SendMessageTimeOut(RemoteHandle, WM_INPUTLANGCHANGEREQUEST,
0, Layout, SMTO_ABORTIFHUNG, 200, Dumme) <> 0;
if Result then
Result := SendMessageTimeOut(RemoteHandle, WM_INPUTLANGCHANGEREQUEST,
RUSSIAN_CHARSET, Layout, SMTO_ABORTIFHUNG, 200, Dumme) <> 0;
end;
changelayout(t);
sleep(1000); //выждал на всякий случай
simulatekeystroke(ord('F'),0);
А... Ну, так ты бы и говорил, что хочешь работать с той самой консолью, которую искал в первом посте... Отсюда и проблемы. С консолью - только через хук WH_SHELL (консоли - это вообще темные лошадки). Ну, или (если консоль активная, в фокусе ввода, в смысле) - то пошли ей комбинацию клавиш, переключающих раскладку.
Но это будет частное решение, на одной машине, чтоб переключиться Ru -> En понадобится одна посылка, на другой - может понадобиться больше, зависит от порядка и количества доступных раскладок... Да и получить раскладку для чужого консольного окна не так просто, так что отсылать Alt+Shift и проверять, не стала ли она английской - не получится... А если другая комбинация клавиш выбрана для переключения раскладки? Вопросом много, ответов - нет...
Ого, ну и заморочки. Я сейчас попробовал работать с ней через буфер обмена, т.е. вставлять туда строку, а в cmd эмулировать Ctrl+V - так нет же, даже вручную ни Ctrl-V, ни Shift-Ins не работают. Спасибо за инфу, почитаю про WH_Shell.
У тебя будет гораздо больше проблем, чем ты думаешь сейчас, если ты еще и с хуками заморочишься. Начиная с того, что на WinNT хуки не работают с консольными приложениями ( это является небезопасным, вот что говорит MS по этому поводу: http://support.microsoft.com/kb/108232 ).
Теперь - вопрос на засыпку: а зачем тебе что-то печатать в консоли, да еще созданной через ShellExecute? Если тебе просто нужна консоль, скажем, для вывода, то есть AllocConsole + GetStdHandle, и пиши сколько хочешь в STD_OUTPUT_HANDLE... О том, как запустить процесс и перехватить его вывод написано в DRKB. Так зачем консоль понадобилась?
Кстати, еще одно: есть более интересный способ получить хендл консоли, чем приведенный тобой: создавать процесс через CreateProcess, а потом http://forum.sources.ru/index.php?showtopic=169614&view=findpost&p=1452609
var t,editt,but:hwnd;
...
keybd_event(vk_LWIN,0,0,0);
keybd_event(byte('R'),0,0,0);
keybd_event(byte('R'),0,KEYEVENTF_KEYUP,0);
keybd_event(vk_LWIN,0,KEYEVENTF_KEYUP,0);
t:=findwindow(nil,'Выполнить');
editt:=findwindowex(t,0,'ComboBox','');
but:=findwindowex(t,0,'Button','OK');
...
Эт чего было? Ты что, серьезно вызываешь окно Run именно так, имитируя всю последовательность действий? Я бы так не делал, есть специализированные методы, которые поддерживаются и будут поддерживаться всеми версиями Windows, независимо от того, как изменится интерфейс. Я про Shell...
Да, и еще:
var
s: string = 'regedit';
procedure TForm1.Button1Click(Sender: TObject);
var
ShellApplication: Variant;
pgui: TGUIThreadinfo;
foreWin: HWND;
begin
ShellApplication := CreateOleObject('Shell.Application');
try
ShellApplication.FileRun; // Будет вызвано именно то окошко, как и при Start -> Run
Sleep(500);
foreWin := GetForegroundWindow();
if foreWin = 0 then ShowMessage('Try FindWindowEx')
else begin
pgui.cbSize := SizeOf(TGUIThreadinfo);
GetGUIThreadInfo(GetWindowThreadProcessId(foreWin), pgui);
SendMessage(pgui.hwndFocus, WM_SETTEXT, Length(s), Integer(@s[1]));
PostMessage(pgui.hwndFocus, WM_KEYDOWN, VK_RETURN, Integer(True));
end;
finally
ShellApplication := Unassigned;
end;
end;
Разобрался, узнал много нового ) Только вот этот код у меня, в консольном приложении (видимо, вся соль именно в этом), не отрабатывал и выбивал ошибку, мол, не вызвано CoInitialize. Я добавил строчку CoInitialize(nil) в самом начале процедуры и CoUninintialize в конце, работать стало, но всё равно выдавало AV в конце программы, пришлось убрать CoUninitialize (это, наверное, и неправильно, но и мне ошибки не нужны)).
Кстати, там при передаче сообщения Эдиту использовалась SendMessage, а при передаче кнопке - Post. Это типа чтобы текст эдиту присвоился, информация об этом вернулась и только потом нажалась бы кнопка?
//проникся блогом, оказалось, я истинный CodeMonkey, прямо по всем признакам
Ага, поставил именно в начале и конце программы - помогло, я твой код просто в процедуру оборачивал. А вот
Ты про Shell.Application? Вот тут: http://msdn.microsoft.com/en-us/library/bb774094%28v=VS.85%29.aspx
Спасибо, в итоге добился, чего хотел
Volvo, а вот, чисто теоретически, можешь предположить, почему твой код из 10-го поста может не сработать? Вариант со слабой машиной и как следствие маленькой задержкой отметается. Просто человек один говорит, что у него просто открывается Выполнить, там висит команда и ничего не происходит, типа, кнопка не нажалась. Или же вообще, что кнопка Ок неактивна, и становится активна только после нажатия кнопки какой-нибудь в поле ввода. У меня и на основной и на виртуальной машинах всё работает.. Пользуюсь этим, убрал проверку:
Procedure writerun(n:string);
begin
ShellApplication := CreateOleObject('Shell.Application');
try
ShellApplication.FileRun;
Sleep(500);
foreWin := GetForegroundWindow();
pgui.cbSize := SizeOf(TGUIThreadinfo);
GetGUIThreadInfo(GetWindowThreadProcessId(foreWin), pgui);
SendMessage(pgui.hwndFocus, WM_SETTEXT, Length(n), Integer(@n[1]));
// postMessage(pgui.hwndFocus, WM_KEYDOWN, VK_SPACE, Integer(True)); //здесь я пробовал дополнительно //ввести пробел, чтобы кнопка активировалась
postMessage(pgui.hwndFocus, WM_KEYDOWN, VK_RETURN, Integer(True));
finally
ShellApplication := Unassigned;
end;
end;
Теоретически - могу, такое будет, когда программа запускается из-под IDE. У меня у самого так было, пока не запустил из-под Эксплорера... Причем GUI-приложение отрабатывает прекрасно и из-под IDE тоже, а вот консольное - не хочет. Все-таки, консоль - это необычное окно... Зря ты с ней затеялся. Другой причины - не вижу пока.
Даа, апи сила) Почитал, переделал (коряво, кажется, но работает)). Размер, правда, остался примерно таким же - вместе с модулями variants,comobj,activex (которые нужны для того кода) и нужным мне Sysutils. Зато - стабильно. Ведь действительно, ну не мог я объяснить, почему у меня 10 раз кряду всё отрабатывает, а у другого - ровно через 3 раза))
Спасибо за советы