| 
| 
 | Вопрос # 1 861/ вопрос решён / | 
 |  Здравствуйте, уважаемые эксперты!Такая вот у меня проблема. Есть функция для имитации нажатия клавиш.
 1. Хотелось бы чтобы эта функция посылала нажатие клавиш только в определенное окно.
 2. Если легче написать новую чем переделывать старую то прошу.
 3. Хотелось бы также получить список всех окон (handle).
 Приложение:Переключить в обычный режим  function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;type  WBytes = array[0..pred(SizeOf(Word))] of Byte;  TSendKey = record    Name: ShortString;    VKey: Byte;  end;const  MaxSendKeyRecs = 41;  SendKeyRecs: array[1..MaxSendKeyRecs] of TSendKey =  (    (Name: 'BKSP'; VKey: VK_BACK),    (Name: 'BS'; VKey: VK_BACK),    (Name: 'BACKSPACE'; VKey: VK_BACK),    (Name: 'BREAK'; VKey: VK_CANCEL),    (Name: 'CAPSLOCK'; VKey: VK_CAPITAL),    (Name: 'CLEAR'; VKey: VK_CLEAR),    (Name: 'DEL'; VKey: VK_DELETE),    (Name: 'DELETE'; VKey: VK_DELETE),    (Name: 'DOWN'; VKey: VK_DOWN),    (Name: 'END'; VKey: VK_END),    (Name: 'ENTER'; VKey: VK_RETURN),    (Name: 'ESC'; VKey: VK_ESCAPE),    (Name: 'ESCAPE'; VKey: VK_ESCAPE),    (Name: 'F1'; VKey: VK_F1),    (Name: 'F10'; VKey: VK_F10),    (Name: 'F11'; VKey: VK_F11),    (Name: 'F12'; VKey: VK_F12),    (Name: 'F13'; VKey: VK_F13),    (Name: 'F14'; VKey: VK_F14),    (Name: 'F15'; VKey: VK_F15),    (Name: 'F16'; VKey: VK_F16),    (Name: 'F2'; VKey: VK_F2),    (Name: 'F3'; VKey: VK_F3),    (Name: 'F4'; VKey: VK_F4),    (Name: 'F5'; VKey: VK_F5),    (Name: 'F6'; VKey: VK_F6),    (Name: 'F7'; VKey: VK_F7),    (Name: 'F8'; VKey: VK_F8),    (Name: 'F9'; VKey: VK_F9),    (Name: 'HELP'; VKey: VK_HELP),    (Name: 'HOME'; VKey: VK_HOME),    (Name: 'INS'; VKey: VK_INSERT),    (Name: 'LEFT'; VKey: VK_LEFT),    (Name: 'NUMLOCK'; VKey: VK_NUMLOCK),    (Name: 'PGDN'; VKey: VK_NEXT),    (Name: 'PGUP'; VKey: VK_PRIOR),    (Name: 'PRTSC'; VKey: VK_PRINT),    (Name: 'RIGHT'; VKey: VK_RIGHT),    (Name: 'SCROLLLOCK'; VKey: VK_SCROLL),    (Name: 'TAB'; VKey: VK_TAB),    (Name: 'UP'; VKey: VK_UP)    );  VK_NULL = 0;  VK_SemiColon = 186;  VK_Equal = 187;  VK_Comma = 188;  VK_Minus = 189;  VK_Period = 190;  VK_Slash = 191;  VK_BackQuote = 192;  VK_LeftBracket = 219;  VK_BackSlash = 220;  VK_RightBracket = 221;  VK_Quote = 222;  VK_Last = VK_Quote;   ExtendedVKeys: set of byte =  [VK_Up,    VK_Down,    VK_Left,    VK_Right,    VK_Home,    VK_End,    VK_Prior,  VK_Next,  VK_Insert,    VK_Delete]; const  INVALIDKEY = $FFFF  VKKEYSCANSHIFTON = $01;  VKKEYSCANCTRLON = $02;  VKKEYSCANALTON = $04;  UNITNAME = 'SendKeys';var  UsingParens, ShiftDown, ControlDown, AltDown, FoundClose: Boolean;  PosSpace: Byte;  I, L: Integer;  NumTimes, MKey: Word;  KeyString: string[20];   procedure DisplayMessage(Message: PChar);  begin    MessageBox(0, Message, UNITNAME, 0);  end;   function BitSet(BitTable, BitMask: Byte): Boolean;  begin    Result := ByteBool(BitTable and BitMask);  end;   procedure SetBit(var BitTable: Byte; BitMask: Byte);  begin    BitTable := BitTable or Bitmask;  end;   procedure KeyboardEvent(VKey, ScanCode: Byte; Flags: Longint);  var    KeyboardMsg: TMsg;  begin    keybd_event(VKey, ScanCode, Flags, 0);    if (Wait) then      while (PeekMessage(KeyboardMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do      begin        TranslateMessage(KeyboardMsg);        DispatchMessage(KeyboardMsg);      end;  end;   procedure SendKeyDown(VKey: Byte; NumTimes: Word; GenUpMsg: Boolean);  var    Cnt: Word;    ScanCode: Byte;    NumState: Boolean;    KeyBoardState: TKeyboardState;  begin    if (VKey = VK_NUMLOCK) then    begin      NumState := ByteBool(GetKeyState(VK_NUMLOCK) and 1);      GetKeyBoardState(KeyBoardState);      if NumState then        KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] and not 1)      else        KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] or 1);      SetKeyBoardState(KeyBoardState);      exit;    end;     ScanCode := Lo(MapVirtualKey(VKey, 0));    for Cnt := 1 to NumTimes do      if (VKey in ExtendedVKeys) then      begin        KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);        if (GenUpMsg) then          KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)      end      else      begin        KeyboardEvent(VKey, ScanCode, 0);        if (GenUpMsg) then          KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);      end;  end;   procedure SendKeyUp(VKey: Byte);  var    ScanCode: Byte;  begin    ScanCode := Lo(MapVirtualKey(VKey, 0));    if (VKey in ExtendedVKeys) then      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)    else      KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);  end;   procedure SendKey(MKey: Word; NumTimes: Word; GenDownMsg: Boolean);  begin    if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then      SendKeyDown(VK_SHIFT, 1, False);    if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then      SendKeyDown(VK_CONTROL, 1, False);    if (BitSet(Hi(MKey), VKKEYSCANALTON)) then      SendKeyDown(VK_MENU, 1, False);    SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);    if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then      SendKeyUp(VK_SHIFT);    if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then      SendKeyUp(VK_CONTROL);    if (BitSet(Hi(MKey), VKKEYSCANALTON)) then      SendKeyUp(VK_MENU);  end;   function StringToVKey(KeyString: ShortString): Word;  var    Found, Collided: Boolean;    Bottom, Top, Middle: Byte;  begin    Result := INVALIDKEY;    Bottom := 1;    Top := MaxSendKeyRecs;    Found := false;    Middle := (Bottom + Top) div 2;    repeat      Collided := ((Bottom = Middle) or (Top = Middle));      if (KeyString = SendKeyRecs[Middle].Name) then      begin        Found := True;        Result := SendKeyRecs[Middle].VKey;      end      else      begin        if (KeyString > SendKeyRecs[Middle].Name) then          Bottom := Middle        else          Top := Middle;        Middle := (Succ(Bottom + Top)) div 2;      end;    until (Found or Collided);    if (Result = INVALIDKEY) then      DisplayMessage('Invalid Key Name');  end;   procedure PopUpShiftKeys;  begin    if (not UsingParens) then    begin      if ShiftDown then        SendKeyUp(VK_SHIFT);      if ControlDown then        SendKeyUp(VK_CONTROL);      if AltDown then        SendKeyUp(VK_MENU);      ShiftDown := false;      ControlDown := false;      AltDown := false;    end;  end; begin  AllocationSize := MaxInt;  Result := false;  UsingParens := false;  ShiftDown := false;  ControlDown := false;  AltDown := false;  I := 0;  L := StrLen(SendKeysString);  if (L > AllocationSize) then    L := AllocationSize;  if (L = 0) then    Exit;   case SendKeysString[I] of    '(':      begin        UsingParens := True;        Inc(I);      end;    ')':      begin        UsingParens := False;        PopUpShiftKeys;        Inc(I);      end;    '%':      begin        AltDown := True;        SendKeyDown(VK_MENU, 1, False);        Inc(I);      end;    '+':      begin        ShiftDown := True;        SendKeyDown(VK_SHIFT, 1, False);        Inc(I);      end;    '^':      begin        ControlDown := True;        SendKeyDown(VK_CONTROL, 1, False);        Inc(I);      end;    '{':      begin        NumTimes := 1;        if (SendKeysString[Succ(I)] = '{') then        begin          MKey := VK_LEFTBRACKET;          SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);          SendKey(MKey, 1, True);          PopUpShiftKeys;          Inc(I, 3);        end;        KeyString := '';        FoundClose := False;        while (I <= L) do        begin          Inc(I);          if (SendKeysString[I] = '}') then          begin            FoundClose := True;            Inc(I);            Break;          end;          KeyString := KeyString + Upcase(SendKeysString[I]);        end;        if (not FoundClose) then        begin          DisplayMessage('No Close');          Exit;        end;        if (SendKeysString[I] = '}') then        begin          MKey := VK_RIGHTBRACKET;          SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);          SendKey(MKey, 1, True);          PopUpShiftKeys;          Inc(I);        end;        PosSpace := Pos(' ', KeyString);        if (PosSpace <> 0) then        begin          NumTimes := StrToInt(Copy(KeyString, Succ(PosSpace), Length(KeyString)            - PosSpace));          KeyString := Copy(KeyString, 1, Pred(PosSpace));        end;        if (Length(KeyString) = 1) then          MKey := vkKeyScan(KeyString[1])        else          MKey := StringToVKey(KeyString);        if (MKey <> INVALIDKEY) then        begin          SendKey(MKey, NumTimes, True);          PopUpShiftKeys;        end;      end;    '~':      begin        SendKeyDown(VK_RETURN, 1, True);        PopUpShiftKeys;        Inc(I);      end;  else    begin      MKey := vkKeyScan(SendKeysString[I]);      if (MKey <> INVALIDKEY) then      begin        SendKey(MKey, 1, True);        PopUpShiftKeys;      end      else        DisplayMessage('Invalid KeyName');      Inc(I);    end;  end;   Result := true;  PopUpShiftKeys;end;
|  |   Вопрос задал: Gooddy (статус: 3-ий класс)Вопрос отправлен: 28 августа 2008, 20:40
 Состояние вопроса: решён, ответов: 2.
 |  Ответ #1. Отвечает эксперт: Вадим К Здравствуйте, Failure!Отсылать "нажатия клавиш" можно разными способами. Тот код, который Вы привели симулирует обычную клавиатуру. То есть типа как бы Вы нажимали кнопки на клавиатуре. Естественно, их перехватит то приложение, которое в данный момент на переднем фоне (активно) или "ждёт их нажатия" - некоторые приложения могут перехватывать нажатия кнопок. Понятно, что бы этот код работал, надо сделать, что бы окно-получатель было активно. Раньше работала функция SetForeGroundWindow, но многие плохие программы любят выкидывать свои окна "на верх" и Майкрософт прикрыла лавочку:).
 
 Если надо просто отправлять нажатия, то можно делать так. Вначале с помощью FindWindow('заголовок окна','тип'); нужно получить хендл окна. если надо не конкретному окну, а какому то дочернему окну это окна (кнопки, эдиты...), то с помощью FindWindowEx ищем нужное дочернее окно.
 После того, как  хендл будет найден, ему можно с помощью SendMessage(хендл, WM_CHAR, ord('A'), 0); послать нажатие кнопки А. Иногда надо симулировать нажатие-отпускание. Для этого есть свои события (WM_KEYUP, WM_KEYDOWN). Можно и нажатие мышки симулировать. Надо только сообщения нужные найти.
 
 Составить список всех хендлов - сложноватая задача. Не забываем, что все кнопки тоже есть окнами.
 начните с этой статьистатьи.
 
 А потом, естественно возникнут вопросы. И когда  они конкретизируются - задавайте, будем разбираться.
 
|  | Ответ отправил: Вадим К (статус: Академик)Время отправки: 29 августа 2008, 00:15
 Оценка за ответ: 5
 Комментарий к оценке: жалко без примера но ответ исчерпывающий |  Ответ #2. Отвечает эксперт: Feniks Здравствуйте, Failure!Вот вам примерчик, что бы получить список запущенных приложения/окон:
 
 procedure TForm1.Button1Click(Sender: TObject);
VAR
Wnd : hWnd;
buff: ARRAY [0..127] OF Char;
begin
ListBox1.Clear;
Wnd := GetWindow(Handle, gw_HWndFirst);
WHILE Wnd <> 0 DO 
BEGIN {Не показываем:}
IF (Wnd <> Application.Handle) AND {-Собственное окно}
IsWindowVisible(Wnd) AND {-Невидимые окна}
(GetWindow(Wnd, gw_Owner) = 0) AND {-Дочернии окна}
(GetWindowText(Wnd, buff, sizeof(buff)) <> 0) {-Окна без заголовков}
THEN BEGIN
GetWindowText(Wnd, buff, sizeof(buff));
ListBox1.Items.Add(StrPas(buff));
END;
Wnd := GetWindow(Wnd, gw_hWndNext);
END;
ListBox1.ItemIndex := 0;
end;А в Приложении смотрите компонент SendKeys для отправки любых комбинаций нажатия клавиш в чужие окна.
 P.S. Желаю удачи.
 Приложение:Переключить в обычный режим unit SendKeys; interface usesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; typeTSendKeys = class(TComponent)private   fhandle:HWND;   L:Longint;   fchild: boolean;   fChildText: string;   procedure SetIsChildWindow(const Value: boolean);   procedure SetChildText(const Value: string);   procedure SetWindowHandle(const Value: HWND);protected public published   Procedure GetWindowHandle(Text:String);   Procedure SendKeys(buffer:string);   Property WindowHandle:HWND read fhandle write SetWindowHandle;   Property IsChildWindow:boolean read fchild write SetIsChildWindow;   Property ChildWindowText:string read fChildText write SetChildText;end; procedure Register; implementation                   utilizadas como callbacks}   HTemp:Hwnd;   ChildText:string;   ChildWindow:boolean; procedure Register;beginRegisterComponents('Standard', [TSendKeys]);end; { TSendKeys }  function PRVGetChildHandle(H:HWND; L: Integer): LongBool;var p:pchar;   I:integer;   s:string;beginI:=length(ChildText)+2;GetMem(p,i+1);SendMessage(H,WM_GetText,i,integer(p));s:=strpcopy(p,s);if pos(ChildText,s)<>0 then  begin    HTemp:=H;    Result:=False  end else   Result:=True;FreeMem(p);end; function PRVSendKeys(H: HWND; L: Integer): LongBool;stdcall;var s:string;   i:integer;begini:=length(temps);if i<>0 thenbegin   SetLength(s,i+2);   GetWindowText(H, pchar(s),i+2);   if Pos(temps,string(s))<>0 then   begin     Result:=false;     if ChildWindow then       EnumChildWindows(H,@PRVGetChildHandle,L)     else       HTemp:=H;   end   else     Result:=True;end else   Result:=False;end; procedure TSendKeys.GetWindowHandle(Text: String);begintemps:=Text;ChildText:=fChildText;ChildWindow:=fChild;EnumWindows(@PRVSendKeys,L);fHandle:=HTemp;end;  procedure TSendKeys.SendKeys(buffer: string);var i:integer;   w:word;   D:DWORD;   P:^DWORD;beginP:=@D;SystemParametersInfo(                      //get flashing timeout on win98        SPI_GETFOREGROUNDLOCKTIMEOUT,        0,        P,        0);SetForeGroundWindow(fHandle);for i:=1 to length(buffer) dobegin   w:=VkKeyScan(buffer[i]);   keybd_event(w,0,0,0);   keybd_event(w,0,KEYEVENTF_KEYUP,0);end;SystemParametersInfo(                     //set flashing TimeOut=0        SPI_SETFOREGROUNDLOCKTIMEOUT,        0,        nil,        0);SetForegroundWindow(TWinControl(TComponent(Self).Owner).Handle);                                           //->typecast working...SystemParametersInfo(                     //set flashing TimeOut=previous value        SPI_SETFOREGROUNDLOCKTIMEOUT,        D,        nil,        0);end; procedure TSendKeys.SetChildText(const Value: string);beginfChildText := Value;end; procedure TSendKeys.SetIsChildWindow(const Value: boolean);beginfchild := Value;end; procedure TSendKeys.SetWindowHandle(const Value:HWND);beginfHandle:=WindowHandle;end;end.        procedure TForm1.Button1Click(Sender: TObject); begin  WinExec('NotePad.exe', SW_SHOW);   SendKeys1.GetWindowHandle('Untitled - Notepad');  if SendKeys1.WindowHandle <> 0 then SendKeys1.SendKeys('This is a test');   // SendKeys1.SendKeys(Chr(13)); end;
|  | Ответ отправил: Feniks (статус: Бакалавр)Время отправки: 29 августа 2008, 11:27
 Оценка за ответ: 5
 Комментарий к оценке: супер |  
 Мини-форум вопросаВсего сообщений: 5; последнее сообщение — 30 августа 2008, 12:40; участников в обсуждении: 4. 
|   | Gooddy (статус: 3-ий класс), 29 августа 2008, 17:13 [#1]:FindWindow('заголовок окна','тип') типы какие есть? Чисти код! Чисти код! Чисти код! |  
|   | Вадим К (статус: Академик), 29 августа 2008, 17:39 [#3]:я постоянно их путаю. когда есть делфи - можно всегда подсмотреть в списке параметов. Но оставить на английском, Феникс, это немного жестоко
  . строка FindWindow(nil, 'Блокнот') найдет хендл первого окна, которое имеет заголовок "Блокнот". А вот что такое первое окно - оставте на совесть windows.
 Один с параметров можно всегда поставить nil = это значит, что вы не хотите его заполнять или не знаете. Но оба - нельзя.
 Галочка "подтверждения прочтения" - вселенское зло. |  
|   | Паровоз (статус: 10-ый класс), 29 августа 2008, 18:41 [#4]:"Но оставить на английском, Феникс, это немного жестоко" Тяжело в учении, легко в бою.
 |  
|   | Gooddy (статус: 3-ий класс), 30 августа 2008, 12:40 [#5]:"Но оставить на английском, Феникс, это немного жестоко" я знаю английский, спасибо
   Чисти код! Чисти код! Чисти код! |  31 января 2011, 19:31: Статус вопроса изменён на решённый (изменил модератор Ерёмин А.А.): Автоматическая обработка (2 и более ответов с оценкой 5) Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте. |