Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Как сделать PreTranslateMessage в своем диалоге?  (Прочитано 30804 раз)
0 Пользователей и 1 Гость смотрят эту тему.
jur
Помогающий

lt
Offline Offline

« : 06-03-2009 21:08 » 

Привет!

Работа ведется в MS Visual Studio.

Когда создается программа с использованием MFC типа Dialog based, то все просто и понятно. Перехватываешь PreTranslateMessage и творишь, что угодно. Но если создаешь свой диалог посредством макроса DialogBox (например в DLL-ке без всяких MFC), то непонятно... Ему передается адрес функции, обрабатывающей сообщения. Но эти сообщения уже как-то фильтруются заранее. Поэтому перехватить всякие там ESC или ENTER не получается.

Как в такой ситуации (причем, без MFC) внедрить свою функцию, позволяющую получать сообщения пораньше? Возможно ли вообще такое? Хотелось бы породить свою DLL-ку, написанную на чистом API, показывающую диалог, который не закрывается по ESC. И, соответственно, позволяющий обрабатывать другие сообщения как угодно.

Спасибо!

Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 06-03-2009 21:26 » 

jur, как внедрить - не разбирался, я бы свой модальный диалог сделал да и не парился Улыбаюсь

вернуть значение -

void EndDialog(
   int nResult
);
« Последнее редактирование: 06-03-2009 21:29 от Алексей1153++ » Записан

jur
Помогающий

lt
Offline Offline

« Ответ #2 : 06-03-2009 21:37 » 

2 Алексей1153++

Так все дело в том, что пользователь нажмет ненароком ESC, а диалог и закроется. А хотелось бы сказать следующее: "Слушай, Юзер! Ты ведь данные изменил! Кто должен заботиться об их обновлении: ты или я?!!!" ;-) И не закрывать диалог, если нельзя.

Опять же, тиснет он ненароком ENTER. Диалог закроется. А ну как происходит ввод текстовой строки? Тогда я должен заблокировать дефолтную реакцию на ENTER и позволить бедному юзеру ввести свою злосчастную строку... ;-)
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 06-03-2009 21:41 » 

забей обработчик OnCancel().

Только в OnClose() добавь вызов CDialog::OnCancel() , а то вообще не закроет ))

а с ентером - OnOK()

положи на диалог кнопки с идентификаторам IDOK и IDCANCEL (по умолчанию они там уже) , в редакторе щёлкай 2 раза по ним - обработчики вставятся автоматом. Кнопки можно удалить, если не нужны
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 06-03-2009 21:44 » 

но это всё не правильно: модальный диалог не должен такого вопроса задавать. Если юзер вводит настройки, то модальный диалог должен лишь накопить введённые данные, а после закрытия диалога программа должна считать даные. А если отменили - проигнорировать. Иначе юзер будет недоволен )

зы (кстати, в мсдн сказано, что PreTranslateMessage  не вызывается для модальных диалогов )
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #5 : 06-03-2009 21:49 » 

забей обработчик OnCancel().

Как?! :-) Нет у меня MFC, я на чистом API пишу (DLL-ка порождает собственный диалог). Дословно:

Код:
  if (DialogBox(Debugger_hModule, MAKEINTRESOURCE(IDD_DIALOG_Debugger), NULL, (DLGPROC)DebuggerProc)==IDOK) {
    // Complete the command
  }
  else {
    // Cancel the command.
  }

Вот эта самая "DebuggerProc" и не видит всяких там ESC-пов...
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 06-03-2009 21:57 » 

WM_CLOSE лови
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #7 : 06-03-2009 22:19 » 

WM_CLOSE лови

Уверен? Попробую завтра. Наверное соответствующей реакцией можно будет приостановить закрытие диалога? Типа, если юзер ввел что-то не того?

Спасибо за помощь! Обязательно сообщу завтра о результате. Возможно это будет полезно другим коллегам.
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 06-03-2009 22:31 » 

это сообщение шлётся окну, когда его хотят закрыть. Если вернуться, не передавая в дефолтную обработку в  DefWindowProc , то не произойдёт вызов DestroyWindow

а вообще, пользуй Spy++ из утилит студии - там все сообщения видно )
Записан

Basurman
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 07-03-2009 13:16 » 

Классику почитайте, что-ли.
Что нибудь вроде Петзолда "Программирование для Windows 95", том 1, глава 11.
Записан
jur
Помогающий

lt
Offline Offline

« Ответ #10 : 07-03-2009 16:02 » 

а вообще, пользуй Spy++ из утилит студии - там все сообщения видно )

Че-то темно все... Сделал простейший диалог с полем ввода (editbox) и двумя стандартными кнопками 'OK' и 'Cancel'. Поставил фокус ввода на эдитбокс и ввожу символы. Затем нажимаю 'Enter'. И все, диалог закрывается.  Вот лог-файл:

Код:
Включил шпионство за сообщениями

<00001> 0002090C S WM_WINDOWPOSCHANGING lpwp:0012F980
<00002> 0002090C R WM_WINDOWPOSCHANGING
...
<00031> 0002090C S WM_CTLCOLORBTN hdcButton:81011779 hwndButton:00020912
<00032> 0002090C R WM_CTLCOLORBTN hBrush:01100059

Нажал букву 'a'

<00033> 0002090C S WM_COMMAND wNotifyCode:EN_UPDATE wID:1012 hwndCtl:0002090E
<00034> 0002090C R WM_COMMAND
<00035> 0002090C S WM_CTLCOLOREDIT hdcEdit:81011779 hwndEdit:0002090E
<00036> 0002090C R WM_CTLCOLOREDIT hBrush:01100060
<00037> 0002090C S WM_COMMAND wNotifyCode:EN_CHANGE wID:1012 hwndCtl:0002090E
<00038> 0002090C R WM_COMMAND

Нажал 'Enter'

<00039> 0002090C S DM_GETDEFID
<00040> 0002090C R DM_GETDEFID wHasDef:DC_HASDEFID wDefID:0001
<00041> 0002090C S WM_COMMAND wNotifyCode:BN_CLICKED wID:IDOK hwndCtl:00020912
<00042> 0002090C S WM_COMMAND wNotifyCode:EN_KILLFOCUS wID:1012 hwndCtl:0002090E
<00043> 0002090C R WM_COMMAND
<00044> 0002090C S WM_SETFOCUS hwndLoseFocus:0002090E
<00045> 0002090C R WM_SETFOCUS
<00046> 0002090C S WM_WINDOWPOSCHANGING lpwp:0012F3A8
<00047> 0002090C R WM_WINDOWPOSCHANGING
<00048> 0002090C S WM_WINDOWPOSCHANGED lpwp:0012F3A8
<00049> 0002090C S WM_GETICON fType:True
<00050> 0002090C R WM_GETICON hicon:00000000
<00051> 0002090C S WM_GETICON fType:False
<00052> 0002090C R WM_GETICON hicon:00000000
<00053> 0002090C S WM_GETICON fType:True
<00054> 0002090C R WM_GETICON hicon:00000000
<00055> 0002090C R WM_WINDOWPOSCHANGED
<00056> 0002090C R WM_COMMAND
<00057> 0002090C S WM_DESTROY
<00058> 0002090C R WM_DESTROY
<00059> 0002090C S WM_NCDESTROY
<00060> 0002090C R WM_NCDESTROY

Как же все-таки внедриться в обработку сообщений такого диалога? Конкретно 'Enter' наверное решается легко, но хотелось бы и остальные команды контролировать (ну там 'ESC' всякие).
Записан

MPEG-4 - в массы!
jur
Помогающий

lt
Offline Offline

« Ответ #11 : 07-03-2009 16:02 » 

Классику почитайте, что-ли.
Что нибудь вроде Петзолда "Программирование для Windows 95", том 1, глава 11.

Можно ссылку?

А если в двух словах пояснить, то как?
Записан

MPEG-4 - в массы!
jur
Помогающий

lt
Offline Offline

« Ответ #12 : 07-03-2009 18:18 » 

Можно ссылку?

Ссылку уже не нужно, книжку нашел :-) Большое спасибо! Почитаю эту 11-ю главу...
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 08-03-2009 06:42 » 

<00041> 0002090C S WM_COMMAND wNotifyCode:BN_CLICKED wID:IDOK hwndCtl:00020912
- реакция на нажатие ОК, сообщение WM_COMMAND с параметрами

<00057> 0002090C S WM_DESTROY
 - сообщение прилетает перед уничтожением окна. При этом, окно ещё не разрушено

WM_NCDESTROY - это сообщение придёт, когда окно уже разрушено
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #14 : 09-03-2009 05:16 » 

<00041> 0002090C S WM_COMMAND wNotifyCode:BN_CLICKED wID:IDOK hwndCtl:00020912
- реакция на нажатие ОК, сообщение WM_COMMAND с параметрами

Так в том-то и засада, что никакого ОК я не нажимал. Я ввел строку текста в эдитбокс и нажал ентер. А дефолтная диалоговая процедура посчитала, что раз я ентер нажал, значит следует воспринимать это нажатие, как нажатие дефолтной кнопки - т.е. этой самой ОК... Та же бадяга и с ESC'ом... Хотелось бы перехватить...
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 09-03-2009 07:14 » 

ну так всё и есть - при нажатии ентер посылается WM_COMMAND BN_CLICKED IDOK , при ecs - с IDCANCEL . А IDYES, IDNO, IDRETRY - это когда конкретно по этим кнопкам нажали

если неохота разбирать, попробуй перехватить WM_DESTROY, и развернуть, если не надо закрывать окно )
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #16 : 09-03-2009 12:07 » 

ну так всё и есть - при нажатии ентер посылается WM_COMMAND BN_CLICKED IDOK , при ecs - с IDCANCEL . А IDYES, IDNO, IDRETRY - это когда конкретно по этим кнопкам нажали
если неохота разбирать, попробуй перехватить WM_DESTROY, и развернуть, если не надо закрывать окно )

Перехват WM_DESTROY не помогает, диалог все-равно закрывается.

Пока сделал по-топорному :-) Дефолтные кнопки в процедуре диалога просто убиваю, а выхожу из диалога посредством нажатия простой юзеровской кнопки "Exit":

Код:
    case WM_COMMAND: 
      switch (LOWORD(wParam)) {
        case IDOK:
//        return TRUE;
          return FALSE;
        case IDCANCEL:
//        EndDialog(hwndDlg, wParam);
//        return TRUE;
          return FALSE;
        case IDC_BUTTON_Exit:
          EndDialog(hwndDlg, wParam);
          return TRUE;
      }

Выглядит некрасиво, конечно, но хотя бы диалог не закрывает, когда не надо! :-) А над красивым решением думаю дальше...
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 09-03-2009 13:24 » 

блин, я глюпость ляпнул, не WM_DESTROY же надо, а WM_CLOSE
Код:
The WM_CLOSE message is sent as a signal that a window or an application should terminate.
...
An application can prompt the user for confirmation, prior to destroying a window, by processing the WM_CLOSE message and calling the DestroyWindow function only if the user confirms the choice.
By default, the DefWindowProc function calls the DestroyWindow function to destroy the window.

попробуй
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #18 : 09-03-2009 14:04 » 

блин, я глюпость ляпнул, не WM_DESTROY же надо, а WM_CLOSE

попробуй

Попробовал. Не работает. Процедура диалога не получает этого сообщения. Пока работает только блокирование дефолтных кнопок.
Записан

MPEG-4 - в массы!
jur
Помогающий

lt
Offline Offline

« Ответ #19 : 09-03-2009 16:12 » 

Поступил еще проще. Сделал следующее: заменил оконную функцию моего диалога. Теперь могу вызывать или не вызывать прежнюю функцию. Вот как я поступил: завел переменную OldWindowProc, куда поместил старую функцию. А затем с помощью SetWindowLong подставил свою. Вот код:

Код:
WNDPROC OldWindowProc = NULL;

LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  if(OldWindowProc) {
    // Мои действия или вызов старой функции
    return OldWindowProc( hwnd, uMsg, wParam, lParam);
  }
  return 0;
}

Подстановку своей функции делаю в WM_INITDIALOG. Вот так:

Код:
    case WM_INITDIALOG:
        OldWindowProc = (WNDPROC)SetWindowLong(hwndDlg,GWL_WNDPROC,(LONG)MyWindowProc);

Вроде работает. Теперь буду исследовать, какие сообщения через этот механизм проходят.
Записан

MPEG-4 - в массы!
Basurman
Опытный

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 10-03-2009 15:32 » 

А к чему такие сложности?
Если диалоговая функция (написанная программистом) возвращает True, то сообщение считается обработанным.
Если возвращается False, то обработка передаётся стандартной оконной процедуре.
Т.е. в диалоговой функции - если не хочешь стандартной обработки, верни True.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #21 : 10-03-2009 16:44 » 

действительно, я об этом ранее сказал. Но хочется человеку покопаться - флак в руки Улыбаюсь
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #22 : 10-03-2009 19:40 » 

А к чему такие сложности?
Если диалоговая функция (написанная программистом) возвращает True, то сообщение считается обработанным.

Да какие сложности, друзья?! Нет человека более любЯщего простоту, чем я! :-)

Но все дело в том, что написанная мною диалоговая процедура DebuggerProc вообще не получает управления по нажатию Enter. Она просто получает команды, будто бы нажатия клавиши IDOK, что вызывает закрытие диалога. А ведь я не хочу его закрывать, я просто в конце вводимой строки этот Enter нажал... Даже если я определю, что для конкретного эдитбокса нужен Enter - это все равно не решает проблемы, т.к. юзер может ненароком перейти из эдитбокса в какой-нить чекбокс и нечаянно нажать Enter.

IMHO, моя ситуация должна была бы быть весьма распространена... Неужели все программируют диалоги с их закрытием по этой злосчастной клавише?! Казалось бы, должны быть ситуации, когда от юзера ожидается некоторый ввод и по его завершению следует еще один ввод, а не закрытие диалога... Не понимаю...

Я-ж потому и поднял этот вопрос, что мне нужно что-то ввести в диалоге, возможно понажимать контролы, еще что-то поделать. А когда работа завершена - закрыть диалог нажатием клавиши Exit (или еще какой - неважно). Мало того, мне хотелось бы и от ESC юзера защитить. Кликнет он ненароком ESC, а я ему: "Погодь! Ты чего это творишь, а?!" ;-) А у меня диалог просто молча закрывается...

(Подозреваю, что я как-то очень коряво высказал свои пожелания, откуда и возникло ложное понимание насчет сложностей...)

P.S. В программе на MFC для ввода строки я использую что-то навроде вот этого:

Код:
BOOL CSLE901Dlg::PreTranslateMessage(MSG* pMsg)
{
  // TODO: Add your specialized code here and/or call the base class
  if (pMsg->message == WM_KEYDOWN) {

    ...
    CWnd *wnd = FromHandle(pMsg->hwnd);
    if(wnd->GetDlgCtrlID() == IDC_EDIT_HOSPITAL) {
      if(enter_text_done(pMsg->wParam,wnd,GlobalData.Hospital,HOSPITAL_STR_LEN))
        return TRUE;
      return CDialog::PreTranslateMessage(pMsg);
    }

где enter_text_done - элементарнейшая процедура вводящая текст в поле до нажатия ENTER или ESC. По первой текст запоминается, по второй вводимый текст игнорируется и восстанавливается предыдущий.

Код:
BOOL enter_text_done(WPARAM wParam,CWnd* wnd,char* str,int len)
{
  if(wParam == VK_ESCAPE) {
    wnd->SetWindowText(str);
    pMainDlg->GetDlgItem(IDC_STATIC_MSGWIN)->SetFocus();
    return TRUE;
  }
  if(wParam == VK_RETURN) {
    CString s,s2;
    wnd->GetWindowText(s);
    s2 = s.Trim();
    s  = s2.Left(len);
    strcpy_s(str,len,s.GetBuffer());
    pMainDlg->GetDlgItem(IDC_STATIC_MSGWIN)->SetFocus();
    return TRUE;
  }
  return FALSE;
}


И вот именно подобная простейшая задача у меня не получается без MFC, на чистом API...

« Последнее редактирование: 10-03-2009 19:42 от jur » Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #23 : 11-03-2009 04:06 » 

jur, ну на MFC закрытие по ентер и ескейп решается так, как ты сделал в посте #16 . Только тут виртуальные функции

Код:
CMyDialog::OnOk()
{
  //CDialog::OnOk();//забито
}

CMyDialog::OnCancel()
{
  //CDialog::OnCancel();//забито
}

CMyDialog::OnClose()
{
   //спрашиваем, точно ли хочется закрыть?
   ///...


   //закрытие диалога
   CDialog::OnCancel();
}
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #24 : 11-03-2009 10:01 » 

jur, ну на MFC закрытие по ентер и ескейп решается так, как ты сделал в посте #16 . Только тут виртуальные функции

Так на MFC я знаю, мне бы на чистом API покрасивше/поправильнее сделать... Пока вижу, что есть два варианта: в ответе #16 и в ответе #19. Проведя исследование проходящих через них сообщений пришел к выводу, что разницы между ними нет. Поэтому пока думаю, что проще использовать первый вариант.

А как вообще делаются свои диалоги без MFC? Ведь делает же кто-то диалоги, которые не закрываются по ESC или ENTER?
Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 11-03-2009 11:11 » 

я думаю, #16 тоже более правильный

jur, создают, полагаю, примерно так:

Берётся хендл окна
HWND h=0;

создаётся окно
HWND CreateWindow(
    LPCTSTR lpClassName,
    LPCTSTR lpWindowName,
    DWORD dwStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpParam
);

назначается оконная процедура
(ты выше писал код уже)
SetWindowLong(h,GWL_WNDPROC,(LONG)MyWindowProc)

окно двигается куда нужно
BOOL MoveWindow(
    HWND hWnd,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    BOOL bRepaint
);

окно показывается, если нужно
BOOL ShowWindow(
    HWND hWnd,
    int nCmdShow
);


только я бы сразу это всё в класс обернул, ибо запутаться в этом - как 2 пальца )

Что и сделали в MFC.  Можешь покопаться в исходниках ихних.
« Последнее редактирование: 11-03-2009 11:13 от Алексей1153++ » Записан

jur
Помогающий

lt
Offline Offline

« Ответ #26 : 11-03-2009 12:58 » 

Алексей1153++

И кто-то упрекал меня в любви к сложности?! Ага А ведь я без всяких CreateWindow обхожусь...

Конечно, ты полностью прав, коллега. Поисследовав этот вопрос я, наконец, понял, что заменять оконную процедуру диалогового окна не нужно. Что вот такой код:

Код:
if (DialogBox(Debugger_hModule, MAKEINTRESOURCE(IDD_DIALOG_Debugger), NULL, (DLGPROC)DebuggerProc)==IDOK) {
    // Complete the command
  }
  else {
    // Cancel the command.
  }

будет, с одной стороны, относительно простым, а с другой стороны, позволит творить много всякого. Только мне еще нужно будет побольше проникнуться направлениями путешествий всех этих многочисленных сообщений.

Большое спасибо за помощь, коллеги!

Записан

MPEG-4 - в массы!
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #27 : 11-03-2009 13:04 » 

И кто-то упрекал меня в любви к сложности?! Ага А ведь я без всяких CreateWindow обхожусь...

ну я и не упрекал, без MFC - это и так сложно, даже говорит не надо )

ты создаёшь модальный диалог при помощи АПИшной функции

Код:
The DialogBox macro creates a modal dialog box from a dialog box template resource. DialogBox does not return control until the specified callback function terminates the modal dialog box by calling the EndDialog function. The DialogBox macro uses the DialogBoxParam function.
WINUSERAPI
INT_PTR
WINAPI
DialogBoxParamA(
    IN HINSTANCE hInstance,
    IN LPCSTR lpTemplateName,
    IN HWND hWndParent,
    IN DLGPROC lpDialogFunc,
    IN LPARAM dwInitParam);

#define DialogBoxA(hInstance, lpTemplate, hWndParent, lpDialogFunc) \
DialogBoxParamA(hInstance, lpTemplate, hWndParent, lpDialogFunc, 0L)

#define DialogBox  DialogBoxA
Записан

jur
Помогающий

lt
Offline Offline

« Ответ #28 : 11-03-2009 13:48 » new

ты создаёшь модальный диалог при помощи АПИшной функции

Да, согласен. Полагаю, что это наименее трудный путь. Я ведь ленив до безобразия!... ;-)

Спасибо за помощь!

Записан

MPEG-4 - в массы!
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines