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

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

ru
Offline Offline

« : 27-07-2004 06:29 » 

Здраствуйте. По прежнему пишу свою программу, она уже давно работает, но есть ряд проблемм.

В роли клиентской части выступает программа, пользовательский интерфейс которой основан на Win32API MDI (без использования MFC). По некоторым причинам, там используется многопоточность и каждое дочернее окно MDI приложение работает в своем потоке. Проблемма в том, что в дочерних окнах не работает переключение раскладок, и для того, чтобы сменить раскладку на русскую, пользователю приходится перенести фокус на главное окно, переключить раскладку и вернуться к дочернему окну.. это не слишком удобно (хотя пользователи и не жалуются - они у меня верх мечтаний Ага  )
Есть предположение, что данный баг вызван неправильной обработкой сообщений в многопоточном приложении. Вот как это примерно происходит (за вычетом несущественных деталей - если чтото покажется неясным, готов дать больше кода):

Код:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  ....
  LC_KERNEL = new CClientKernel(); // Инациализация ядра программы
  ....
  LC_KERNEL->Start(); // Запуск ядра
  delete LC_KERNEL;
  ...
  return ret;
}

  DWORD dRes;
 
CClientKernel::Start() {
  ...
  m_hWnd = ::CreateWindowEx(0, GUI_MAIN_WINDOW_CLASS, "Client",
                        WS_TILEDWINDOW | WS_VISIBLE |  WS_CLIPCHILDREN | WS_MAXIMIZE,
                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                        NULL, NULL, hInstance, (LPVOID)(some_info));
  // some_info - некая структура, используемая окном для, тут она не важна.
  // На самом деле, окно создается не непосредственно в этой функции, а через вызов мембера объекта класса CMainWin,
  // описанного в другом файле. Но создание объекта производится в тут.
  // (я субклассю окна, и в результате имею обект MainWin.)
  // Но если приводить здесь весь код то это будет слишком длинно
  ...
  // Запуск потоков ядра. В одном из этих потоков создаются сеансы
  ...
  MSG msg;
  while (GetMessage(&msg,hWnd,0,0)>0) {
    if ( msg.hwnd == NULL)
      continue;
    ::TranslateMessage( &msg );
    ::DispatchMessage( &msg );
  }
  ...
  return (LRESULT)msg.wParam;
}

Внутри одного из потоков ядра, создается обект сеанса. Сеанс запускает отдельный поток и там создает свое дочернее окно через вызов метода главного окна приложения (приведен код этого метода):
Код:
int CMDIMainFrame::MDI_CreateClientWnd(CMenu * WindowMenu) {
  // Создание дочернего окна
  CLIENTCREATESTRUCT ccs;
  ccs.hWindowMenu = WindowMenu->GetHandle();
  ccs.idFirstChild = IDM_WINDOWCHILD;
 
  // Create the MDI client window.
  m_hWndClient = CreateWindowEx(0, "MDICLIENT", (LPCTSTR) NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
                              0, 0, 0, 0, m_hWnd, (HMENU) 0xCAC, App->GetInstance(), (LPSTR) &ccs);
  ::ShowWindow(m_hWndClient, SW_SHOW);
  return true;
}

После чего, в этом потоке сеанса запускается цикл обработки соообщений:
Код:
 MSG msg;
  while (GetMessage(&msg,hWnd,0,0)>0) {
    if ( msg.hwnd == NULL)
      continue;
    ::TranslateMessage( &msg );
    ::DispatchMessage( &msg );
  }
  return (LRESULT)msg.wParam;

Таким образом, главное окно (FRAME) и дочерние окна (MDI_CHILD) работают каждое в своем потоке и у всех есть свой цикл обработки сообщений.. Никакого упоминания о том, как производить обработку сообщений в многопоточных MDI приложениях, в MSDN я не нашел, однако меня смущает то, что во всех случаях циклы обработки ничем не отличаются.

По поводу переключения раскладок. Spy++ показал следующее (для поля вводе на дочернем окне при попытке сменить раскладку):
Цитата
<00115> 076A05A4 P WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00116> 076A05A4 P WM_KEYDOWN nVirtKey:VK_SHIFT cRepeat:1 ScanCode:2A fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00117> 076A05A4 P WM_INPUTLANGCHANGEREQUEST fSysCharset:True hkl:04190419
<00118> 076A05A4 S ..WM_IME_NOTIFY dwCommand:00000001 dwData:00000000
<00119> 076A05A4 R ..WM_IME_NOTIFY
<00120> 076A05A4 S ..WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
<00121> 076A05A4 R ..WM_IME_NOTIFY
<00122> 076A05A4 S WM_INPUTLANGCHANGE charset:204 hkl:04190419
<00123> 076A05A4 S .WM_IME_NOTIFY dwCommand:0000000A dwData:00000000
<00124> 076A05A4 R .WM_IME_NOTIFY
<00125> 076A05A4 R WM_INPUTLANGCHANGE
<00126> 076A05A4 P WM_KEYUP nVirtKey:VK_SHIFT cRepeat:1 ScanCode:2A fExtended:0 fAltDown:0 fRepeat:1 fUp:1
<00127> 076A05A4 P WM_INPUTLANGCHANGEREQUEST fSysCharset:True hkl:04090409
<00128> 076A05A4 S ..WM_IME_NOTIFY dwCommand:00000001 dwData:00000000
<00129> 076A05A4 R ..WM_IME_NOTIFY
<00130> 076A05A4 S ..WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
<00131> 076A05A4 R ..WM_IME_NOTIFY
<00132> 076A05A4 S WM_INPUTLANGCHANGE charset:0 hkl:04090409
<00133> 076A05A4 S .WM_IME_NOTIFY dwCommand:0000000A dwData:00000000
<00134> 076A05A4 R .WM_IME_NOTIFY
<00135> 076A05A4 R WM_INPUTLANGCHANGE
<00136> 076A05A4 P WM_KEYUP nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:1 fUp:1

А вот - что он же выдает при смене раскладки на главном окне (там есть листбокс):
Цитата
<00178> 03820560 P WM_KEYDOWN nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00179> 03820560 P WM_KEYDOWN nVirtKey:VK_SHIFT cRepeat:1 ScanCode:2A fExtended:0 fAltDown:0 fRepeat:0 fUp:0
<00180> 03820560 P WM_INPUTLANGCHANGEREQUEST fSysCharset:True hkl:04090409
<00181> 03820560 P WM_KEYUP nVirtKey:VK_SHIFT cRepeat:1 ScanCode:2A fExtended:0 fAltDown:0 fRepeat:1 fUp:1
<00182> 03820560 P WM_INPUTLANGCHANGEREQUEST fSysCharset:True hkl:04090409
<00183> 03820560 S ..WM_IME_NOTIFY dwCommand:00000001 dwData:00000000
<00184> 03820560 R ..WM_IME_NOTIFY
<00185> 03820560 S ..WM_IME_NOTIFY dwCommand:00000002 dwData:00000000
<00186> 03820560 R ..WM_IME_NOTIFY
<00187> 03820560 S WM_INPUTLANGCHANGE charset:0 hkl:04090409
<00188> 03820560 R WM_INPUTLANGCHANGE
<00189> 03820560 P WM_KEYUP nVirtKey:VK_CONTROL cRepeat:1 ScanCode:1D fExtended:0 fAltDown:0 fRepeat:1 fUp:1

ПС. Кстати интересная деталь - данный баг наблюдается при запуске клиента в WinXP. Когда программа была запущена под Win2k такой проблеммы не было!  Я шокирован!
ППС. Проблемма существует уже давно, однако решить ее никак не удается. Я уже два раза открывал топик на эту тему, но это ничего не дало   Так больше нельзя... . Буду признателен за любые мысли
« Последнее редактирование: 30-11-2007 21:32 от Алексей1153++ » Записан
titov_alex
Участник

ru
Offline Offline

« Ответ #1 : 27-07-2004 10:06 » 

Внимательно посмотрев на сообщения я решил повторно провести тестирование
Была найдена интересная закономерность Улыбаюсь

1. Нажимаем Crtl+Shift, отпускаем - раскладка клавиатуры остается англиской
2. Нажимаем Crtl и удерживая его _дважды_ нажимаем Shift  - раскладка клавиатуры меняется.  Быть такого не может

Такое ощущение, что программа дважды меняет раскладку клавиатуры при одинарном нажатии Ctrl+Shift, а во втором случае - раскладка, похоже, меняется аж 3 раза.

С чем это может быть связано?  :?

ПС. оба варианта нажатия производились в EDIT-е дочернего окна
Записан
akc
Гость
« Ответ #2 : 18-10-2007 06:46 » 

titov_alex, хотелось бы узнать чем закончилась эпопея с MDI?
Встал на теже грабли.
ЗЫ. Виновата в этом не ХР, а её "Advanced Text Service" при активности которого сообщение WM_INPUTLANGCHANGEREQUEST (запрос на смену языка) посылается ДВАЖДЫ!
Причем первое "сурогатное" и никем не обрабатывается, за исключением активного MDI-окна работающего в отдельном потоке (наша бага) - из-за этого смена происходит дважды (и это видно через spy). Причем если из этого отдельного потока открыть диалог то тоже всё работает правильно => виноват не сам поток, а что-то ещё.
ЗЗЫ. Сервис можно отключить в региональных настройках, но пропадет иконка языка Жаль - у меня такая маза не прошла.
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #3 : 10-03-2008 20:17 » 

to titov_alex
Если почитаешь MSDN то там жирным шрифтом написано, что все объекты UI должны жить в одном потоке, посему комментариев с решением вашей проблемы вы не дождетесь, строго говоря вышей программой пользоваться нельзя, потому как ее работа может привести к любому результату, например формату винта (не шучу).
Записан

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

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


« Ответ #4 : 11-03-2008 04:07 » 

ну про формат это ты загнул, конечно )
Записан

titov_alex
Участник

ru
Offline Offline

« Ответ #5 : 11-03-2008 08:28 » 

to titov_alex
Если почитаешь MSDN то там жирным шрифтом написано, что все объекты UI должны жить в одном потоке, посему комментариев с решением вашей проблемы вы не дождетесь, строго говоря вышей программой пользоваться нельзя, потому как ее работа может привести к любому результату, например формату винта (не шучу).
Цитату?

хотелось бы узнать чем закончилась эпопея с MDI?
Победой Улыбаюсь

Была ошибка в коде, если не ошибаюсь - вызывал DefWndProc вместо фреймового варианта для главного окна приложения, или что то в том духе.. в итоге смена раскладки производилась дважды.
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #6 : 11-03-2008 18:02 » 

titov_alex,
Ну сейчас... хотя лучше, вот так, как в программе создается окно (где?, имеется ввиду поток и так сказать обстоятельства создания окна) и как выс ним работаете, например выводится инвормация (в каком потоке и какие при этом происходят синхронизации)
Записан

С уважением Lapulya
titov_alex
Участник

ru
Offline Offline

« Ответ #7 : 12-03-2008 08:32 » 

Вся работа окна идёт в выделенном потоке - от создания до высвобождения ресурсов. Не помню никаких, связанных именно с GUI синхронизаций - только синхронизация доступа к общим для нескольких потоков данных.

Выделенный поток для работы окна - обычный для меня приём. Стараюсь чтобы немного "тяжёловатые" GUI-API не влияли на общую архитектуру программы. Потому я и спросил про цитату из МСДН. Давно не читал там ничего про UI, т.к. мои задачи редко вынуждают создавать сложные пользовательские интерфейсы..
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #8 : 14-03-2008 19:28 » 

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

С уважением Lapulya
theambient
Гость
« Ответ #9 : 26-01-2010 15:00 » 

Спасибо вам, наконец-то разобрался в чем дело, правда поставил костыль - если второе сообщение приходит слишком быстро - не обрабатываем его, зато работает ))
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines