Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Перехват Windows сообщений для Console окна.


Автор: TuXAPuK 29.4.2006, 15:36
Вобщем занемаюсь я програмированием серверов много чего изпытывал но всеравно лучше старой доброй консоли нечего не нашол. К сожелению дошол я до такого момента когда консоли мне окозалось мало, но от старого удобного движка отказыватся я некак не хотел. Вот я и решил извратится по полной с помощью ResHack'а создал ресурс, в нём меню. И через винапи прилепил меню к консольному окну. AllocConsole, Repaint и вот долгожданное меню уже прилеплено. Но тут я встал как вкопанный и обзадался вопросом а как-же поймать сообщение от меню?? smile Ну вобщем я уже 2-й день спокойно спать немогу в нете дофига всякой чипухи валяется типо как от формы получить, от TApplication но всё это мне не подходит. Ладно подумал я и поставил старый добрый Turbo Pascal 7.1 нашол то что мне нужно, начал транслировать на свой Delphi 7 и что вы думаете?? Ха! Делфя меня потихоньку послала на тот самый забор где 3 буквы написаны. Вобщем ну нечего у меня путного так и не вышло, и я был-бы очень благодарен любой помощи со стороны жителей вашего форума.
С уважением Евгений a.k.a. TuXAPuK  smile
 

Автор: Quadr0 29.4.2006, 16:45
...

Автор: TuXAPuK 2.5.2006, 17:45
В этом вся суть проблемы. В том что мне не нужна ещё одна форма с менюшкой, и не желательна реализация при помощи крюка (hook). Так как в первом случее пользователю будет не удобно искать а где-же это окошко с менюшкой там пропало.. А во втором случее прийдётся использовать DDE сервер/клиент что-бы передовать сообщения от крюка (hook) к консоли, а через SendMessage на HWND консоли я тоже нечего сделать немогу.

Вот мой сокращённый

Пример:
Код
Program ConMenuExam;
{$APPTYPE CONSOLE}
{$R rMenu.res}

Uses
  Windows;

Const
  sCaption = 'Console Menu Example';

Begin
  SetConsoleTitle(sCaption);
  AllocConsole;
  SetMenu(FindWindow(Nil, sCaption), LoadMenu(hInstance, MakeIntResource(128)));
End.


с Картинкой:
user posted image

Где мы:
1) Устанавливаем Caption для консоли.
2) Заставляем Windows покозать наше окошко и создать для него все заголовки.
3) Устанавливаем меню к нашей консоли загруженное из собственного ресурса с RESNAME = '128'

Пологаю вопрос остаётся в силе: Каким образом получить сообщения от своего меню? smile
С уважением Евгений a.k.a. TuXAPuK
 

Автор: Snowy 2.5.2006, 19:38
Могет такой вариант подойдет?
Код
type
  TForm1 = class(TForm)
    mm1: TMainMenu;
    Menu1: TMenuItem;
    ClickMe1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure ClickMe1Click(Sender: TObject);
  private
  end;

var Form1: TForm1;

implementation

{$R *.dfm}

procedure cons;
var s: string;
begin
  repeat
    write('>'); Readln(s);
    Writeln(s + ' Ok');
  until s = 'exit';
  ExitThread(0);
end;

function GetConsoleWindow: THandle;
var
  S: AnsiString;
  C: Char;
begin
  Result := 0;
  Setlength(S, MAX_PATH + 1);
  if GetConsoleTitle(PChar(S), MAX_PATH) <> 0 then
  begin
    C := S[1];
    S[1] := '$';
    SetConsoleTitle(PChar(S));
    Result := FindWindow(nil, PChar(S));
    S[1] := C;
    SetConsoleTitle(PChar(S));
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  h, t: THandle;
  rect: TRect;
begin
  AllocConsole;
  h := GetConsoleWindow;
  Windows.SetParent(h, Handle);
  SetWindowPos(h, 0, 0, -GetSystemMetrics(SM_CYCAPTION)-4, ClientWidth, ClientHeight, 0);
  GetWindowRect(h, rect);
  SetBounds(rect.Left, rect.Top, rect.Right - rect.Left,
            rect.Bottom - rect.Top + GetSystemMetrics(SM_CYCAPTION)-3);
  CreateThread(nil, 256, @cons, nil, 0, t);
end;

procedure TForm1.ClickMe1Click(Sender: TObject);
begin
  ShowMessage('Меня кликнули!');
end;
  

Автор: Rouse_ 3.5.2006, 12:27
Смотреть нужно примерно в эту сторону:

Код

program Console;

{$APPTYPE CONSOLE}

{$R 'Menu.res' 'Menu.rc'}

uses
  Windows,
  Messages;

const
  sCaption = 'Console Menu Example';

var
  hMainWnd, hMenu, hInput: THandle;
  irConsole: TInputRecord;
  EventCount: Cardinal;

begin
  SetConsoleTitle(sCaption);
  hMenu := LoadMenu(hInstance, MakeIntResource(216));
  hMainWnd := FindWindow(nil, sCaption);
  SetMenu(hMainWnd, hMenu);
  hInput := GetStdHandle(STD_INPUT_HANDLE);
  repeat
    ReadConsoleInput(hInput, irConsole, 1, EventCount);
    case irConsole.EventType of
      MENU_EVENT:
        if irConsole.Event.MenuEvent.dwCommandId = WM_MENUSELECT then
        begin
          // Тут думать...
        end;
    end;
  until False;
end.


К сожалению сабкласинг консоли не проходит, а было бы проще на много... 

Автор: Snowy 3.5.2006, 12:32
Можно конечно "тут думать". Только консоль нам не говорит, какой пункт меню был выбран...
Мы узнаем только о раскрытии и закрытии меню.
Это вообще может быть не наше меню, а системное.
Можно попробовать думать в сторону событий меню. Например следить за отрисовкой и фиксировать последний выбранный пункт... Извращение конечно.
Можно попробовать хук на выбор пункта меню.
Простого решения здесь пока не видно...  

Автор: Rouse_ 3.5.2006, 12:40
В том то и дело что в сторону событий не получается. Дело в том что у консоли два потока. 
Первый ее родной в котором все это и крутиться и второй который доступен непосредственно программисту.
Можно проверить крутя цил выботки сообщений - сообщения туда не проходят. Хот если установить таймер с нулевым хэндлом, то в этом цикле WM_TIME уже ловиться. Сабклассинг, как я уже сказал - не проходит к сожалению...

Добавлено @ 12:41 
Кстати идея с локальным хуком - очень даже может быть... 

Автор: maxim1000 3.5.2006, 12:54
а если попробовать написать обычный цикл сообщений?
правда, тогда нужно будет логику проекта вынести в другой поток

Добавлено @ 12:55 
(сам я этого не пробовал, так что это - просто догадка) 

Автор: Snowy 3.5.2006, 13:05
Цитата(maxim1000 @  3.5.2006,  12:54 Найти цитируемый пост)
а если попробовать написать обычный цикл сообщений?
Пробовали - пусто. Ни одного, даже самого маленького сообщения. Тишина полная.
Консоль только эвенты дает. А там информация о меню не дается. Выдает всего один параметр - reserved.
Из чего возникает нездоровый вопрос: почему программеры из M$ считают, что мы не собираемся юзать меню...
 

Автор: Rouse_ 3.5.2006, 14:37
Мдя, вот так да. Собственно обработчик MENU_EVENT:

Код

VOID
HandleMenuEvent(
    IN PCONSOLE_INFORMATION Console,
    IN DWORD wParam
    )
{
    INPUT_RECORD InputEvent;
    ULONG EventsWritten;

    InputEvent.EventType = MENU_EVENT;
    InputEvent.Event.MenuEvent.dwCommandId = wParam;
    EventsWritten = WriteInputBuffer( Console,
                                      &Console->InputBuffer,
                                      &InputEvent,
                                      1
                                     );
}


Его вызов в ConsoleWindowProc
системное меню:
Код

            case WM_SYSCOMMAND:
                if (wParam >= ScreenInfo->CommandIdLow &&
                    wParam <= ScreenInfo->CommandIdHigh) {
                    HandleMenuEvent(Console,(DWORD)wParam);

Остальное:
Код

            case WM_INITMENU:
                HandleMenuEvent(Console,WM_INITMENU);
                InitializeMenu(Console);
                break;
            case WM_MENUSELECT:
                if (HIWORD(wParam) == 0xffff) {
                    HandleMenuEvent(Console,WM_MENUSELECT);
                }
                break;


Я медленно офигеваю...

Как все сделано: сама консоль находиться в сервере, а не реализуется приложением. Обработчик соответвенно тоже сидит в нем же. Функции работы с консолью вызываются со стороны клиента тобишь приложения...
Если интересно как работает сия кухня - могу кинуть родные исходники... 

Автор: maxim1000 3.5.2006, 16:50
Цитата(Rouse_ @  3.5.2006,  13:37 Найти цитируемый пост)
Как все сделано: сама консоль находиться в сервере, а не реализуется приложением. Обработчик соответвенно тоже сидит в нем же. Функции работы с консолью вызываются со стороны клиента тобишь приложения...

ннндааа... намутили...
хотя, если подумать...
консольные программы далеко не всегда запускаются в своём окне - иногда запускают командную строку, а в ней программу
если каждой запущеной программе давать возможность управлять окном, можетполучиться кашица...
кроме того, если, например, программа зациклилась, любому юзеру будет удобно нажать Ctrl-C
в случае обработки сообщений самой программой ничего не произойдёт (она ж зациклилась)
а сервер, на котором расположена реализация функций консоли, всё сделает, как надо...
а перенаправление потоков ввода/вывода...
что-то в этом есть...

зато у мне пришла в голову другая мысль:
причина такого поведения с консольным окном, насколько я понял в том, чтобы меньше переписывать уже существующий код под консоль...
тогда можно сделать немножко наоборот:
делаем простое окно
на него ставим multiline edit
прикручиваем дополнительные функции
реализуем функции консоли через функции работы с edit'ом
если заменить их не получается - перенаправляем ввод/вывод в каналы и общаемся через них
тут всё зависит от того, насколько широк диапазон используемых функций консоли... 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)