Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 1 861

/ вопрос решён /

Здравствуйте, уважаемые эксперты!
Такая вот у меня проблема. Есть функция для имитации нажатия клавиш.
1. Хотелось бы чтобы эта функция посылала нажатие клавиш только в определенное окно.
2. Если легче написать новую чем переделывать старую то прошу.
3. Хотелось бы также получить список всех окон (handle).

Приложение:
  1.  
  2. function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
  3. type
  4. WBytes = array[0..pred(SizeOf(Word))] of Byte;
  5. TSendKey = record
  6. Name: ShortString;
  7. VKey: Byte;
  8. end;
  9. const
  10. MaxSendKeyRecs = 41;
  11. SendKeyRecs: array[1..MaxSendKeyRecs] of TSendKey =
  12. (
  13. (Name: 'BKSP'; VKey: VK_BACK),
  14. (Name: 'BS'; VKey: VK_BACK),
  15. (Name: 'BACKSPACE'; VKey: VK_BACK),
  16. (Name: 'BREAK'; VKey: VK_CANCEL),
  17. (Name: 'CAPSLOCK'; VKey: VK_CAPITAL),
  18. (Name: 'CLEAR'; VKey: VK_CLEAR),
  19. (Name: 'DEL'; VKey: VK_DELETE),
  20. (Name: 'DELETE'; VKey: VK_DELETE),
  21. (Name: 'DOWN'; VKey: VK_DOWN),
  22. (Name: 'END'; VKey: VK_END),
  23. (Name: 'ENTER'; VKey: VK_RETURN),
  24. (Name: 'ESC'; VKey: VK_ESCAPE),
  25. (Name: 'ESCAPE'; VKey: VK_ESCAPE),
  26. (Name: 'F1'; VKey: VK_F1),
  27. (Name: 'F10'; VKey: VK_F10),
  28. (Name: 'F11'; VKey: VK_F11),
  29. (Name: 'F12'; VKey: VK_F12),
  30. (Name: 'F13'; VKey: VK_F13),
  31. (Name: 'F14'; VKey: VK_F14),
  32. (Name: 'F15'; VKey: VK_F15),
  33. (Name: 'F16'; VKey: VK_F16),
  34. (Name: 'F2'; VKey: VK_F2),
  35. (Name: 'F3'; VKey: VK_F3),
  36. (Name: 'F4'; VKey: VK_F4),
  37. (Name: 'F5'; VKey: VK_F5),
  38. (Name: 'F6'; VKey: VK_F6),
  39. (Name: 'F7'; VKey: VK_F7),
  40. (Name: 'F8'; VKey: VK_F8),
  41. (Name: 'F9'; VKey: VK_F9),
  42. (Name: 'HELP'; VKey: VK_HELP),
  43. (Name: 'HOME'; VKey: VK_HOME),
  44. (Name: 'INS'; VKey: VK_INSERT),
  45. (Name: 'LEFT'; VKey: VK_LEFT),
  46. (Name: 'NUMLOCK'; VKey: VK_NUMLOCK),
  47. (Name: 'PGDN'; VKey: VK_NEXT),
  48. (Name: 'PGUP'; VKey: VK_PRIOR),
  49. (Name: 'PRTSC'; VKey: VK_PRINT),
  50. (Name: 'RIGHT'; VKey: VK_RIGHT),
  51. (Name: 'SCROLLLOCK'; VKey: VK_SCROLL),
  52. (Name: 'TAB'; VKey: VK_TAB),
  53. (Name: 'UP'; VKey: VK_UP)
  54. );
  55. VK_NULL = 0;
  56. VK_SemiColon = 186;
  57. VK_Equal = 187;
  58. VK_Comma = 188;
  59. VK_Minus = 189;
  60. VK_Period = 190;
  61. VK_Slash = 191;
  62. VK_BackQuote = 192;
  63. VK_LeftBracket = 219;
  64. VK_BackSlash = 220;
  65. VK_RightBracket = 221;
  66. VK_Quote = 222;
  67. VK_Last = VK_Quote;
  68.  
  69. ExtendedVKeys: set of byte =
  70. [VK_Up,
  71. VK_Down,
  72. VK_Left,
  73. VK_Right,
  74. VK_Home,
  75. VK_End,
  76. VK_Prior,
  77. VK_Next,
  78. VK_Insert,
  79. VK_Delete];
  80.  
  81. const
  82. INVALIDKEY = $FFFF
  83. VKKEYSCANSHIFTON = $01;
  84. VKKEYSCANCTRLON = $02;
  85. VKKEYSCANALTON = $04;
  86. UNITNAME = 'SendKeys';
  87. var
  88. UsingParens, ShiftDown, ControlDown, AltDown, FoundClose: Boolean;
  89. PosSpace: Byte;
  90. I, L: Integer;
  91. NumTimes, MKey: Word;
  92. KeyString: string[20];
  93.  
  94. procedure DisplayMessage(Message: PChar);
  95. begin
  96. MessageBox(0, Message, UNITNAME, 0);
  97. end;
  98.  
  99. function BitSet(BitTable, BitMask: Byte): Boolean;
  100. begin
  101. Result := ByteBool(BitTable and BitMask);
  102. end;
  103.  
  104. procedure SetBit(var BitTable: Byte; BitMask: Byte);
  105. begin
  106. BitTable := BitTable or Bitmask;
  107. end;
  108.  
  109. procedure KeyboardEvent(VKey, ScanCode: Byte; Flags: Longint);
  110. var
  111. KeyboardMsg: TMsg;
  112. begin
  113. keybd_event(VKey, ScanCode, Flags, 0);
  114. if (Wait) then
  115. while (PeekMessage(KeyboardMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do
  116. begin
  117. TranslateMessage(KeyboardMsg);
  118. DispatchMessage(KeyboardMsg);
  119. end;
  120. end;
  121.  
  122. procedure SendKeyDown(VKey: Byte; NumTimes: Word; GenUpMsg: Boolean);
  123. var
  124. Cnt: Word;
  125. ScanCode: Byte;
  126. NumState: Boolean;
  127. KeyBoardState: TKeyboardState;
  128. begin
  129. if (VKey = VK_NUMLOCK) then
  130. begin
  131. NumState := ByteBool(GetKeyState(VK_NUMLOCK) and 1);
  132. GetKeyBoardState(KeyBoardState);
  133. if NumState then
  134. KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] and not 1)
  135. else
  136. KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] or 1);
  137. SetKeyBoardState(KeyBoardState);
  138. exit;
  139. end;
  140.  
  141. ScanCode := Lo(MapVirtualKey(VKey, 0));
  142. for Cnt := 1 to NumTimes do
  143. if (VKey in ExtendedVKeys) then
  144. begin
  145. KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
  146. if (GenUpMsg) then
  147. KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
  148. end
  149. else
  150. begin
  151. KeyboardEvent(VKey, ScanCode, 0);
  152. if (GenUpMsg) then
  153. KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
  154. end;
  155. end;
  156.  
  157. procedure SendKeyUp(VKey: Byte);
  158. var
  159. ScanCode: Byte;
  160. begin
  161. ScanCode := Lo(MapVirtualKey(VKey, 0));
  162. if (VKey in ExtendedVKeys) then
  163. KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)
  164. else
  165. KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
  166. end;
  167.  
  168. procedure SendKey(MKey: Word; NumTimes: Word; GenDownMsg: Boolean);
  169. begin
  170. if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
  171. SendKeyDown(VK_SHIFT, 1, False);
  172. if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
  173. SendKeyDown(VK_CONTROL, 1, False);
  174. if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
  175. SendKeyDown(VK_MENU, 1, False);
  176. SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
  177. if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
  178. SendKeyUp(VK_SHIFT);
  179. if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
  180. SendKeyUp(VK_CONTROL);
  181. if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
  182. SendKeyUp(VK_MENU);
  183. end;
  184.  
  185. function StringToVKey(KeyString: ShortString): Word;
  186. var
  187. Found, Collided: Boolean;
  188. Bottom, Top, Middle: Byte;
  189. begin
  190. Result := INVALIDKEY;
  191. Bottom := 1;
  192. Top := MaxSendKeyRecs;
  193. Found := false;
  194. Middle := (Bottom + Top) div 2;
  195. repeat
  196. Collided := ((Bottom = Middle) or (Top = Middle));
  197. if (KeyString = SendKeyRecs[Middle].Name) then
  198. begin
  199. Found := True;
  200. Result := SendKeyRecs[Middle].VKey;
  201. end
  202. else
  203. begin
  204. if (KeyString > SendKeyRecs[Middle].Name) then
  205. Bottom := Middle
  206. else
  207. Top := Middle;
  208. Middle := (Succ(Bottom + Top)) div 2;
  209. end;
  210. until (Found or Collided);
  211. if (Result = INVALIDKEY) then
  212. DisplayMessage('Invalid Key Name');
  213. end;
  214.  
  215. procedure PopUpShiftKeys;
  216. begin
  217. if (not UsingParens) then
  218. begin
  219. if ShiftDown then
  220. SendKeyUp(VK_SHIFT);
  221. if ControlDown then
  222. SendKeyUp(VK_CONTROL);
  223. if AltDown then
  224. SendKeyUp(VK_MENU);
  225. ShiftDown := false;
  226. ControlDown := false;
  227. AltDown := false;
  228. end;
  229. end;
  230.  
  231. begin
  232. AllocationSize := MaxInt;
  233. Result := false;
  234. UsingParens := false;
  235. ShiftDown := false;
  236. ControlDown := false;
  237. AltDown := false;
  238. I := 0;
  239. L := StrLen(SendKeysString);
  240. if (L > AllocationSize) then
  241. L := AllocationSize;
  242. if (L = 0) then
  243. Exit;
  244.  
  245. case SendKeysString[I] of
  246. '(':
  247. begin
  248. UsingParens := True;
  249. Inc(I);
  250. end;
  251. ')':
  252. begin
  253. UsingParens := False;
  254. PopUpShiftKeys;
  255. Inc(I);
  256. end;
  257. '%':
  258. begin
  259. AltDown := True;
  260. SendKeyDown(VK_MENU, 1, False);
  261. Inc(I);
  262. end;
  263. '+':
  264. begin
  265. ShiftDown := True;
  266. SendKeyDown(VK_SHIFT, 1, False);
  267. Inc(I);
  268. end;
  269. '^':
  270. begin
  271. ControlDown := True;
  272. SendKeyDown(VK_CONTROL, 1, False);
  273. Inc(I);
  274. end;
  275. '{':
  276. begin
  277. NumTimes := 1;
  278. if (SendKeysString[Succ(I)] = '{') then
  279. begin
  280. MKey := VK_LEFTBRACKET;
  281. SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
  282. SendKey(MKey, 1, True);
  283. PopUpShiftKeys;
  284. Inc(I, 3);
  285. end;
  286. KeyString := '';
  287. FoundClose := False;
  288. while (I <= L) do
  289. begin
  290. Inc(I);
  291. if (SendKeysString[I] = '}') then
  292. begin
  293. FoundClose := True;
  294. Inc(I);
  295. Break;
  296. end;
  297. KeyString := KeyString + Upcase(SendKeysString[I]);
  298. end;
  299. if (not FoundClose) then
  300. begin
  301. DisplayMessage('No Close');
  302. Exit;
  303. end;
  304. if (SendKeysString[I] = '}') then
  305. begin
  306. MKey := VK_RIGHTBRACKET;
  307. SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
  308. SendKey(MKey, 1, True);
  309. PopUpShiftKeys;
  310. Inc(I);
  311. end;
  312. PosSpace := Pos(' ', KeyString);
  313. if (PosSpace <> 0) then
  314. begin
  315. NumTimes := StrToInt(Copy(KeyString, Succ(PosSpace), Length(KeyString)
  316. - PosSpace));
  317. KeyString := Copy(KeyString, 1, Pred(PosSpace));
  318. end;
  319. if (Length(KeyString) = 1) then
  320. MKey := vkKeyScan(KeyString[1])
  321. else
  322. MKey := StringToVKey(KeyString);
  323. if (MKey <> INVALIDKEY) then
  324. begin
  325. SendKey(MKey, NumTimes, True);
  326. PopUpShiftKeys;
  327. end;
  328. end;
  329. '~':
  330. begin
  331. SendKeyDown(VK_RETURN, 1, True);
  332. PopUpShiftKeys;
  333. Inc(I);
  334. end;
  335. else
  336. begin
  337. MKey := vkKeyScan(SendKeysString[I]);
  338. if (MKey <> INVALIDKEY) then
  339. begin
  340. SendKey(MKey, 1, True);
  341. PopUpShiftKeys;
  342. end
  343. else
  344. DisplayMessage('Invalid KeyName');
  345. Inc(I);
  346. end;
  347. end;
  348.  
  349. Result := true;
  350. PopUpShiftKeys;
  351. end;


Gooddy Вопрос решён, но можно продолжить его обсуждение в мини-форуме

Вопрос задал: 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. Желаю удачи.

Приложение:
  1. unit SendKeys;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  7.  
  8. type
  9. TSendKeys = class(TComponent)
  10. private
  11. fhandle:HWND;
  12. L:Longint;
  13. fchild: boolean;
  14. fChildText: string;
  15. procedure SetIsChildWindow(const Value: boolean);
  16. procedure SetChildText(const Value: string);
  17. procedure SetWindowHandle(const Value: HWND);
  18. protected
  19.  
  20. public
  21.  
  22. published
  23. Procedure GetWindowHandle(Text:String);
  24. Procedure SendKeys(buffer:string);
  25. Property WindowHandle:HWND read fhandle write SetWindowHandle;
  26. Property IsChildWindow:boolean read fchild write SetIsChildWindow;
  27. Property ChildWindowText:string read fChildText write SetChildText;
  28. end;
  29.  
  30. procedure Register;
  31.  
  32. implementation
  33.  
  34.  
  35. utilizadas como callbacks}
  36. HTemp:Hwnd;
  37. ChildText:string;
  38. ChildWindow:boolean;
  39.  
  40. procedure Register;
  41. begin
  42. RegisterComponents('Standard', [TSendKeys]);
  43. end;
  44.  
  45. { TSendKeys }
  46.  
  47.  
  48. function PRVGetChildHandle(H:HWND; L: Integer): LongBool;
  49. var p:pchar;
  50. I:integer;
  51. s:string;
  52. begin
  53. I:=length(ChildText)+2;
  54. GetMem(p,i+1);
  55. SendMessage(H,WM_GetText,i,integer(p));
  56. s:=strpcopy(p,s);
  57. if pos(ChildText,s)<>0 then
  58. begin
  59. HTemp:=H;
  60. Result:=False
  61. end else
  62. Result:=True;
  63. FreeMem(p);
  64. end;
  65.  
  66. function PRVSendKeys(H: HWND; L: Integer): LongBool;stdcall;
  67. var s:string;
  68. i:integer;
  69. begin
  70. i:=length(temps);
  71. if i<>0 then
  72. begin
  73. SetLength(s,i+2);
  74. GetWindowText(H, pchar(s),i+2);
  75. if Pos(temps,string(s))<>0 then
  76. begin
  77. Result:=false;
  78. if ChildWindow then
  79. EnumChildWindows(H,@PRVGetChildHandle,L)
  80. else
  81. HTemp:=H;
  82. end
  83. else
  84. Result:=True;
  85. end else
  86. Result:=False;
  87. end;
  88.  
  89. procedure TSendKeys.GetWindowHandle(Text: String);
  90. begin
  91. temps:=Text;
  92. ChildText:=fChildText;
  93. ChildWindow:=fChild;
  94. EnumWindows(@PRVSendKeys,L);
  95. fHandle:=HTemp;
  96. end;
  97.  
  98.  
  99. procedure TSendKeys.SendKeys(buffer: string);
  100. var i:integer;
  101. w:word;
  102. D:DWORD;
  103. P:^DWORD;
  104. begin
  105. P:=@D;
  106. SystemParametersInfo( //get flashing timeout on win98
  107. SPI_GETFOREGROUNDLOCKTIMEOUT,
  108. 0,
  109. P,
  110. 0);
  111. SetForeGroundWindow(fHandle);
  112. for i:=1 to length(buffer) do
  113. begin
  114. w:=VkKeyScan(buffer[i]);
  115. keybd_event(w,0,0,0);
  116. keybd_event(w,0,KEYEVENTF_KEYUP,0);
  117. end;
  118. SystemParametersInfo( //set flashing TimeOut=0
  119. SPI_SETFOREGROUNDLOCKTIMEOUT,
  120. 0,
  121. nil,
  122. 0);
  123. SetForegroundWindow(TWinControl(TComponent(Self).Owner).Handle);
  124. //->typecast working...
  125. SystemParametersInfo( //set flashing TimeOut=previous value
  126. SPI_SETFOREGROUNDLOCKTIMEOUT,
  127. D,
  128. nil,
  129. 0);
  130. end;
  131.  
  132. procedure TSendKeys.SetChildText(const Value: string);
  133. begin
  134. fChildText := Value;
  135. end;
  136.  
  137. procedure TSendKeys.SetIsChildWindow(const Value: boolean);
  138. begin
  139. fchild := Value;
  140. end;
  141.  
  142. procedure TSendKeys.SetWindowHandle(const Value:HWND);
  143. begin
  144. fHandle:=WindowHandle;
  145. end;
  146. end.
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155. procedure TForm1.Button1Click(Sender: TObject);
  156. begin
  157.  
  158. WinExec('NotePad.exe', SW_SHOW);
  159.  
  160.  
  161. SendKeys1.GetWindowHandle('Untitled - Notepad');
  162.  
  163. if SendKeys1.WindowHandle <> 0 then
  164. SendKeys1.SendKeys('This is a test');
  165.  
  166.  
  167. // SendKeys1.SendKeys(Chr(13));
  168. end;


Ответ отправил: Feniks (статус: Бакалавр)
Время отправки: 29 августа 2008, 11:27
Оценка за ответ: 5

Комментарий к оценке: супер

Мини-форум вопроса

Всего сообщений: 5; последнее сообщение — 30 августа 2008, 12:40; участников в обсуждении: 4.
Gooddy

Gooddy (статус: 3-ий класс), 29 августа 2008, 17:13 [#1]:

FindWindow('заголовок окна','тип') типы какие есть?
Чисти код! Чисти код! Чисти код!
Feniks

Feniks (статус: Бакалавр), 29 августа 2008, 17:22 [#2]:

Вадик чутка ошибся :)
HWND FindWindow(
    LPCTSTR lpClassName,
    LPCTSTR lpWindowName
);
Из Хелпа:
lpClassName
[in] Pointer to a null-terminated string that specifies the class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero.
If lpClassName points to a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names.

If lpClassName is NULL, it finds any window whose title matches the lpWindowName parameter.

lpWindowName
[in] Pointer to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match.

Если понравилось, не забываем о благодарности ;-)
Вадим К

Вадим К (статус: Академик), 29 августа 2008, 17:39 [#3]:

я постоянно их путаю. когда есть делфи - можно всегда подсмотреть в списке параметов.
Но оставить на английском, Феникс, это немного жестоко:).
строка FindWindow(nil, 'Блокнот') найдет хендл первого окна, которое имеет заголовок "Блокнот". А вот что такое первое окно - оставте на совесть windows.
Один с параметров можно всегда поставить nil = это значит, что вы не хотите его заполнять или не знаете. Но оба - нельзя.
Галочка "подтверждения прочтения" - вселенское зло.
Паровоз

Паровоз (статус: 10-ый класс), 29 августа 2008, 18:41 [#4]:

"Но оставить на английском, Феникс, это немного жестоко"
Тяжело в учении, легко в бою.
Gooddy

Gooddy (статус: 3-ий класс), 30 августа 2008, 12:40 [#5]:

"Но оставить на английском, Феникс, это немного жестоко"
я знаю английский, спасибо:)
Чисти код! Чисти код! Чисти код!

31 января 2011, 19:31: Статус вопроса изменён на решённый (изменил модератор Ерёмин А.А.): Автоматическая обработка (2 и более ответов с оценкой 5)

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:26
Выполнено за 0.03 сек.