Окна на чистом WinAPI. Или просто о сложном

    Disclaimer

    Казалось бы, что WinAPI уходит в прошлое. Давно уже существует огромное количество кросс-платформенных фреймфорков, Windows не только на десктопах, да и сами Microsoft в свой магазин не жалуют приложения, которые используют этого монстра. Помимо этого статей о том, как создать окошки на WinAPI, не только здесь, но и по всему интернету, исчисляется тысячами по уровню от дошколят и выше. Весь этот процесс разобран уже даже не по атомам, а по субатомным частицам. Что может быть проще и понятнее? А тут я еще…

    Но не все так просто, как кажется.

    Почему о WinAPI сейчас?


    В один прекрасный момент, изучая потроха одной из игр в весьма неплохом эмуляторе NES, я подумал: Вроде неплохой такой эмуль, а в отладчике нет такой простой вещи, как навигация по кнопкам клавиатуры, которая есть в любом нормальном отладчике.

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

    О чем это я? А вот об этом кусочке кода:

    	
    case WM_KEYDOWN:
    	MessageBox(hwndDlg,"Die!","I'm dead!",MB_YESNO|MB_ICONINFORMATION);
    	break;
    

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

    Ответ такой: так делать нельзя!

    И, возвращаясь, к изначальному вопросу о WinAPI: очень много популярных, и не очень, проектов продолжают его использовать и в настоящее время, т.к. лучше, чем на чистом API многие вещи не сделать (тут можно бесконечно приводить аналогии вроде сравнения высокоуровневых языков и ассемблера, но сейчас не об этом). Да и мало ли почему? Просто используют и все тут.

    О проблеме


    Диалоговые окна упрощают работу с GUI, одновременно лишая нас возможности сделать что-то самостоятельно. Например, сообщения WM_KEYDOWN/WM_KEYUP, приходящие в оконную процедуру, «съедаются» в недрах DefDlgProc, беря на себя такие вещи, как: Навигация по Tab, обработка клавиш Esc, Enter, и т.д. Кроме того, диалоги не нужно создавать вручную: проще, ведь, набросать кнопок, списков, в редакторе ресурсов, вызвать в WinMain CreateDialog/DialogBox и все готово.

    Обойти такие мелкие неприятности просто. Есть, как минимум, два вполне легальных способа:

    1. Создать свой собственный класс через RegisterClassEx и в процедуре обработки класса схватывать WM_KEYDOWN, перенаправлять в процедуру обработки самого диалога. Да-да! Можно создавать диалоги со своим собственным классом, и встроенный в VS редактор даже позволяет задавать имя класса для диалога. Вот только кто об этом знает и этим пользуется?
      Минус очевиден: Нужно регистрировать еще один класс, иметь на 1 CALLBACK процедуру больше, суть которой будет всего-навсего в трансляции пары сообщений. Кроме того, мы не будем знать куда их транслировать, и придется городить костыли.
    2. Использовать встроенный механизм акселераторов. И нам даже не придется менять код диалоговой процедуры! Ну, разве что, добавить одну строчку в switch/case, но об этом ниже.

    Tutorials?


    Не побоюсь сказать, что все туториалы по созданию окон через WinAPI начинаются с такого незамысловатого кода, обозначая его, как «цикл обработки сообщений» (опущу детали по подготовке класса окна и прочую обвязку):

    
        while (GetMessage(&msg, nullptr, 0, 0))
        {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
        }

    Здесь действительно все просто:

    1. GetMessage() выхватывает очередное сообщение из очереди, и ключевой момент: блокирует поток, если в очереди пусто.
    2. TranslateMessage() из WM_KEYDOWN/WM_KEYUP формирует сообщения WM_CHAR/WM_SYSCHAR (они нужны, если кто-то хочет сделать свой редактор текста).
    3. DispatchMessage() отправляет сообщение в оконную процедуру (если таковая существует).

    Начнем с того, что этот код использовать опасно, и вот почему. Обратите внимание на сноску:
    Because the return value can be nonzero, zero, or -1, avoid code like this:
    while (GetMessage( lpMsg, hWnd, 0, 0)) ...
    И ниже приводится пример правильного цикла.

    Стоит сказать, что в шаблонах VS для Win32 приложений, написан именно такой неправильный цикл. И это очень печально. Ведь мало кто будет вникать в то, что сделали сами авторы, ведь это априори правильно. И неправильный код множится вместе с багами, которые очень сложно отловить.

    После этого фрагмента кода, как правило, следует рассказ про акселераторы, и добавляется пара новых строчек (учитывая замечание в MSDN, предлагаю сразу писать правильный цикл):

    
        HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
        BOOL bRet = 0;
        while ( bRet = GetMessage(&msg, nullptr, 0, 0) )
        {
               if ( -1 == bRet ) break;
               if ( !TranslateAccelerator(msg.hwnd, hAccel, &msg) )
               {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
                }
        }

    Этот вариант я видел чаще всего. И он (та-дам) снова неправильный!

    Сперва о том, что изменилось (потом о проблемах этого кода):

    В первой строчке из ресурсов загружается таблица клавиш, при нажатии на которые, будет формироваться сообщение WM_COMMAND с соответствующим id команды.

    Собственно TranslateAccelerator этим и занимается: если видит WM_KEYDOWN и код клавиши, которые есть в этом списке, то (опять же ключевой момент) будет формировать сообщение WM_COMMAND (MAKEWPARAM(id, 1)) и отправлять в соответствующую для дескриптора окна, указанного в первом аргументе, процедуру обработки.

    Из последней фразы, думаю, стало понятно, в чем проблема предыдущего кода.
    Поясню: GetMessage выхватывает сообщения для ВСЕХ объектов типа «окно» (в число которых входят и дочерние: кнопки, списки и прочее), а TranslateAccelerator будет отправлять сформированную WM_COMMAND куда? Правильно: обратно в кнопку/список и т.д. Но мы обрабатываем WM_COMMAND в своей процедуре, а значит нам интересно ее получать в ней же.

    Ясно, что TranslateAccelerator надо вызывать для нашего созданного окна:

    
        HWND hMainWnd = CreateWindow(...);
        HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
        BOOL bRet = 0;
        while (bRet = GetMessage(&msg, nullptr, 0, 0))
        {
               if ( -1 == bRet ) break;
               if ( !TranslateAccelerator(hMainWnd, hAccel, &msg) )
               {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
                }
        }

    И вроде все хорошо и замечательно теперь: мы разобрали все детально и все должно работать идеально.

    И снова нет. :-) Это будет работать правильно, пока у нас ровно одно окно — наше. Как только появится немодальное новое окно (диалог), все клавиши, которые будут в нем нажаты оттранслируются в WM_COMMAND и отправляться куда? И опять же правильно: в наше главное окно.

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

    IsDialogMessage


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

    На самом деле, делает она чуть больше, чем следует из названия. А именно:

    • Осуществляет навигацию по дочерним контролам кнопками Tab/Shift+Tab/вверх/вниз/вправо/влево. Плюс еще кое-что, но этого нам достаточно
    • По нажатии на ESC формирует WM_COMMAND( IDCANCEL )
    • По нажатии на Enter формирует WM_COMMAND( IDOK ) или нажатие на текущую кнопку по умолчанию
    • Переключает кнопки по умолчанию (рамочка у таких кнопок чуть ярче остальных)
    • Ну и еще разные штуки, которые облегчают пользователю работу с диалогом

    Что она нам дает? Во-первых, нам не надо думать о навигации внутри окна. Нам и так все сделают. Кстати, навигацию по Tab можно сделать, добавив стиль WS_EX_CONTROLPARENT нашему основному окну, но это топорно и не так функционально.

    Во-вторых, она нам облегчит жизнь по всем остальным пунктам, перечисленным в списке (и даже немного больше).

    Вообще, она используется где-то в недрах Windows для обеспечения работы модальных диалоговых окон, а программистам дана, чтобы вызывать ее для немодальных диалогов. Однако мы ее можем использовать где угодно:
    Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.
    Т.е. теперь, если мы оформим цикл так:

    
        HWND hMainWnd = CreateWindow(...);
        HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
        BOOL bRet = 0;
        while (bRet = GetMessage(&msg, nullptr, 0, 0))
        {
               if ( -1 == bRet ) break;
               if ( !TranslateAccelerator(hMainWnd, hAccel, &msg) )
               {
                    if ( !IsDialogMessage(hMainWnd, &msg) )
                    {
    		           TranslateMessage(&msg);
    		           DispatchMessage(&msg);
                     }
                }
        }

    То наше окошко будет иметь навигацию, как в родном диалоге Windows. Но теперь мы получили два недостатка:

    1. Этот код также будет хорошо работать только с одним (немодальным) окном;
    2. Получив все достоинства диалоговой навигации, мы лишаемся прелестей в виде сообщений WM_KEYDOWN/WM_KEYUP (только для самого окна, а не для дочерних контролов);

    И вот на этом этапе вообще все туториалы заканчиваются и начинаются вопросы: How to handle keyboard events in a winapi standard dialog?
    Это первая ссылка в гугле, но поверьте: тысячи их. Про предлагаемые решений (лучшее из которых — это создать свой класс диалогов, о чем я писал выше, до subclassing и RegisterHotKey. Где-то я даже видел «лучший» из способов: использовать Windows Hooks).

    Пора поговорить о том, чего нет в туториалах и ответах.

    Как правило (как правило! Если кому-то захочется большего, то можно регистрировать свой класс для диалогов и работать так. И, если же, кому-то это интересно, я могу дополнить этим статью) WM_KEYDOWN хотят тогда, когда хотят обработать нажатие на клавишу, которая выполнит функцию в независимости от выбранного контрола в окне — т.е. некая общая функция для всего данного конкретного диалога. А раз так, то почему бы не воспользоваться богатыми возможностями, которые нам сама WinAPI и предлагает: TranslateAccelerator.

    Везде используют ровно одну таблицу акселераторов, и только для главного окна. Ну действительно: цикл GetMessage-loop один, значит и таблица одна. Куда еще их девать?

    На самом деле, циклы GetMessage-loop могут быть вложенными. Давайте еще раз посмотрим описание PostQuitMessage:
    The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future.
    И GetMessage:
    If the function retrieves the WM_QUIT message, the return value is zero.
    Таким образом, выход из GetMessage-loop осуществится, если мы вызовем PostQuitMessage в процедуре окна. Что это значит?

    Мы можем для каждого немодального окна в нашей программе создавать свой собственный подобный цикл. В данном случае DialogBoxParam нам не подходит, т.к. оно крутит свой собственный цикл и повлиять мы на него не можем. Однако если создадим диалог через CreateDialogBoxParam или окно через CreateWindow, то можно закрутить еще один цикл. При этом в каждом таком окне и диалоге мы должны вызывать PostQuitMessage:

    
        HWND hMainWnd = CreateWindow(...);
        HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
        BOOL bRet = 0;
        while (bRet = GetMessage(&msg, nullptr, 0, 0))
        {
               if ( -1 == bRet ) break;
               if ( !TranslateAccelerator(hMainWnd, hAccel, &msg) )
               {
                    if ( !IsDialogMessage(hMainWnd, &msg) )
                    {
    		           TranslateMessage(&msg);
    		           DispatchMessage(&msg);
                     }
                }
        }
    // .... 
    LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
    {
        switch( umsg )
        {
            case WM_MYMESSAGE:
            {
                        HWND hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwnd, MyDialogBoxProc);
                        HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR_FOR_MY_DIALOG));
                        BOOL bRet = 0, fSavedEnabledState = IsWindowEnabled(hwnd);
                        EnableWindow(hwnd, FALSE);  // disable parent window, as dialog window is modal
                        while (bRet = GetMessage(&msg, nullptr, 0, 0))
                        {
                               if ( -1 == bRet ) break;
                               if ( !TranslateAccelerator(hDlg, hAccel, &msg) )
                               {
                                    if ( !IsDialogMessage(hDlg, &msg) )
                                    {
                    		           TranslateMessage(&msg);
                    		           DispatchMessage(&msg);
                                     }
                                }
                        }
                        EnableWindow(hwnd, fSavedEnabledState);  // enable parent window. Dialog was closed
                        break;
            }
        }
    }
    
    INT_PTR CALLBACK MyDlgProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
    {
          switch(umsg)
          {
                case WM_CLOSE:
                 {
                        // EndDialog( hwnd, 0 ); -- DONT DO THAT! 
                        // EndDialog is valid ONLY for Modal Dialogs, created with DialogBox(Param)
                        DestroyWindow( hwnd );
                        break;
                 }
                 case WM_DESTROY:
                 {
                        PostQuitMessage( 0 );
                        break;
                  }
           // ....
          }
         
          return 0;
    }
    

    Обратите внимание: теперь для каждого нового окна в нашей программе мы можем добавить в обработку собственную таблицу акселераторов. WM_QUIT будет выхватывать GetMessage из цикла для диалога, а внешний цикл его даже не увидит. Почему так происходит?

    Дело в том, что внешний цикл «встал» на вызове DispatchMessage, который вызвал нашу процедуру, которая крутит свой собственный внутренний цикл GetMessage с таким же DispatchMessage. Классический вложенный вызов (в данном случае DispatchMessage). Посему внешний цикл не получит WM_QUIT и не завершится на этом этапе. Все будет работать стройно.

    Но и тут есть свои недостатки:
    Каждый такой цикл будет обрабатывать сообщения только для «своего» окна. Про другие-то мы здесь не знаем. А значит, если где-то объявится еще один цикл, то все остальные окна не будут получать нужной обработки своих сообщений парой TranslateAccelerator/IsDialogMessage.

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

    Делаем красиво


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

    Во-первых, было бы логично, что только активное окно принимает сообщения. Т.е. для неактивного окна мы не будем транслировать акселераторы и передавать сообщения в IsDialogMessage.

    Во-вторых, если для окна не задана таблица акселераторов, то транслировать нечего, будем просто отдавать сообщение в IsDialogMessage.

    Создадим простой std::map, который будет мапить дескриптор окна в дескриптор таблицы акселераторов. Вот так:

    std::map<HWND,HACCEL> l_mAccelTable;

    И по мере создания окон будем в него добавлять новые окна с дескриптором на свою любимую таблицу (или нуль, если такая обработка не требуется).

    Вот так:

    
    BOOL AddAccelerators(HWND hWnd, HACCEL hAccel)
    {
    	if ( IsWindow( hWnd ) )
    	{
    		l_mAccelTable[ hWnd ] = hAccel; 
    		return TRUE;
    	}
    	return FALSE;
    }
    
    BOOL AddAccelerators(HWND hWnd, LPCTSTR accel)
    {
    	return AddAccelerators( hWnd, LoadAccelerators( hInstance, accel ) );
    }
    
    BOOL AddAccelerators(HWND hWnd, int accel)
    {
    	return AddAccelerators( hWnd, MAKEINTRESOURCE( accel ) );
    }
    
    BOOL AddAccelerators(HWND hWnd)
    {
    	return AddAccelerators( hWnd, HACCEL( NULL ) );
    }
    
    

    Ну и после закрытия окна удалять. Вот так:

    
    void DelAccel(HWND hWnd)
    {
    	std::map<HWND, HACCEL>::iterator me = l_mAccelTable.find( hWnd );
    
    	if ( me != l_mAccelTable.end() )
    	{
    		if ( me->second )
    		{
    			DestroyAcceleratorTable( me->second );
    		}
    		l_mAccelTable.erase( me );
    	}
    }
    

    Теперь, как создаем новый диалог/окно, вызываем AddAccelerators( hNewDialog, IDR_MY_ACCEL_TABLE ). Как закрываем: DelAccel( hNewDialog ).

    Список окон с нужными дескрипторами у нас есть. Немного модифицируем наш основной цикл обработки сообщений:

    
        // ...
        HWND hMainWnd = CreateWindow(...);
        AddAccelerators(hMainWnd, IDR_ACCELERATOR);
        BOOL bRet = 0;
        while (bRet = GetMessage(&msg, nullptr, 0, 0))
        {
               if ( -1 == bRet ) break;
               if ( !HandleAccelArray( GetActiveWindow(), msg ) )
               {
    	           TranslateMessage(&msg);
    	           DispatchMessage(&msg);
                }
        }
        // ...
    

    Значительно лучше! Что же там в HandleAccelArray и зачем там GetActiveWindow()?

    Немного теории:

    Есть две функции, возвращающих дескриптор активного окна GetForegroundWindow и GetActiveWindow. Отличие первой от второй вполне доходчиво описано в описании второй:
    The return value is the handle to the active window attached to the calling thread's message queue. Otherwise, the return value is NULL.
    Если первая будет возвращать дескриптор любого окна в системе, то последняя только того, которое использует очередь сообщений нашего потока. Т.к. нас интересуют окна только нашего потока (а значит те, которые будут попадать в нашу очередь сообщений), то и возьмем последнюю.

    Так вот HandleAccelArray, руководствуясь переданным ей дескриптором на активное окно, ищет это самое окно в нашей мапе, и если оно там есть, отдает это сообщение на трансляцию в TranslateAccelerator, а затем (если первый не увидел нужного) в IsDialogMessage. Если и последняя не обработала сообщение, то возвращаем FALSE, чтобы пройти по стандартной процедуре TranslateMessage/DispatchMessage.

    Выглядит так:

    
    BOOL HandleAccelWindow(std::map<HWND,HACCEL>::const_iterator mh, MSG & msg)
    {
    	const HWND & hWnd = mh->first;
    	const HACCEL & hAccel = mh->second;
    
    	if ( !TranslateAccelerator( hWnd, hAccel, &msg ) )
    	{
    		// message not for TranslateAccelerator. Try it with IsDialogMessage
    		if ( !IsDialogMessage( hWnd, &msg ) )
    		{
    			// so, do default stuff
    			return FALSE;
    		}
    	}
    
    	// ok, message translated. Say to message-loop, to get next message
    	return TRUE;
    }
    
    BOOL HandleAccelArray( HWND hActive, MSG & msg )
    {
    	if ( !hActive )
    		return FALSE;  // no active window. Nothing to do
    
    	std::map<HWND, HACCEL>::const_iterator mh = l_mAccelTable.find( hActive );
    	if ( mh != l_mAccelTable.end() )
    	{
    		// Got it! Try to translate this message for the active window
    		return HandleAccelWindow( mh, msg );
    	}
    
    	return FALSE;
    }

    Теперь каждое дочернее окно вправе добавить себе любимую таблицу акселераторов и спокойно ловить и обрабатывать WM_COMMAND с нужным кодом.

    А что там еще об одной строчке в коде обработчика WM_COMMAND?


    Описание в TranslateAccelerator гласит:
    To differentiate the message that this function sends from messages sent by menus or controls, the high-order word of the wParam parameter of the WM_COMMAND or WM_SYSCOMMAND message contains the value 1.
    Обычно код обработки WM_COMMAND выглядит так:

    
    switch( HIWORD( wParam ) )
    {
           case BN_CLICKED:     // command from buttons/menus
           {
                  switch( LOWORD( wParam ) )
                  {
                        case IDC_BUTTON1: DoButton1Stuff(); break;
                        case IDC_BUTTON2: DoButton2Stuff(); break; 
    // ...
                  }
                  break;
           }
    }
    

    Теперь можно написать так:

    
    switch( HIWORD( wParam ) )
    {
           case 1: // accelerator
           case BN_CLICKED:     // command from buttons/menus
           {
                  switch( LOWORD( wParam ) )
                  {
                        case IDC_BUTTON1: DoButton1Stuff(); break;
                        case IDC_BUTTON2: DoButton2Stuff(); break; 
    // ...
                  }
                  break;
           }
    }
    

    И теперь, возвращаясь к тому же fceux, добавив всего одну строчку в код обработки команд от кнопок, мы получим желаемое: управлять дебагером с клавиатуры. Достаточно добавить небольшую обертку вокруг главного цикла сообщений и новую таблицу акселераторов с нужными соответствиями VK_KEY => IDC_DEBUGGER_BUTTON.

    P.S.: Мало кто знает, но можно создавать свою собственную таблицу акселераторов, а теперь и применять ее прямо налету.

    P.P.S.: Т.к. DialogBox/DialogBoxParam крутит собственный цикл, то от при вызове диалога через них акселераторы работать не будут и наш цикл (или циклы) будет «простаивать».

    P.P.P.S.: После вызова HandleAccelWindow мап l_mAccelTable может измениться, т.к. TranslateAccelerator или IsDialogMessage вызывают DispatchMessage, а там может встретиться AddAccelerators или DelAccel в наших обработчиках! Поэтому лучше его после этой функции не трогать.

    Пощупать код можно здесь. За основу был взят код, генерируемый из стандартного шаблона MS VS 2017.
    Share post

    Similar posts

    Comments 94

      +2
      А я вот думаю, статью по HiDPI на WINAPI дописать, да всё думаю что никому не надо может.
        +5
        WinAPI — своеобразный низкий уровень для написания приложений под Windows. Все, даже кроссплатформенное, даже Qt в своей основе (именно для Windows) упирается именно в WinAPI. Поэтому, знать его, я считаю, необходимо хоть в какой-то степени. И статьи по нему также полезны и интересны.
          –2
          Хотел сначала Вас раскритиковать, мол негоже в 2018 году опираться на апи не самой лучшей ос, зачем так усложнять и т.п. Потом подумал и решил, что Вы правы, учитывая, да
          даже Qt в своей основе (именно для Windows)

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

          Сам помню, как познакомился с Delphi и офигевал что простая програмулька после сборки веси 300-400 кб. В том же делфи писал с применением WinAPI (из *.dpr, с функцией WinMain определенной в прототипах хе-хе) и получал уже 10-15 кб.

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

          Плюс
            +2
            Даже в 2018 негоже использовать многомегабайтный Qt или аналоги если тебе надо вывести диалог с десятком кнопок. Голый апи имеет свою нишу.
              –8
              Голый апи имеет свою нишу

              имеет очень узкую нишу, ограниченную одной единственной ОС, занимающей мизерную долю в общей массе устройств
                +3
                  +1

                  Среди десктопов — да. Но среди всех устройств — уже нет, т.к. десктопов в сравнении с мобилками не очень много нынче. В итоге с формальной точки зрения придраться к формулировке maisvendoo сложно. Вот так вот и манипулируют рекламщики да политики.

                    0
                    Но среди всех устройств — уже нет, т.к. десктопов в сравнении с мобилками не очень много нынче.
                    Не так давно равенство было. Сейчас ещё разница меньше, чем вдвое. Всё-таки когда говорят о «мизерной доле», то речь не идёт обычно о 40%.
                      +1

                      Опять же… манипулировать статистикой очень просто. В приведенной сслыке идёт про использование web-а, а не про количество устройств. Я вот и сам всё ещё хабр с десктопа по старинке читаю, хотя телефонов и планшетов у меня на порядок больше, чем десктопов.

                        0
                        телефонов и планшетов у меня на порядок больше, чем десктопов.
                        Зачем вам 10 телефонов и планшетов???
                          0

                          Для разных целей, включая тестирование. Я то, конечно, не типичный пользователь, но вполне очевидно, что сейчас телефон есть почти у каждого, включая бабушек и детей, что не скажешь про десктоп. А если брать развивающиеся страны, то там даже программируют на телефонах: https://habrahabr.ru/company/everydaytools/blog/345232/
                          А ещё в современном мире тот же Android пихают везде от принтеров и часов до унитазов и автомобилей. Так что мир "устройств" очень большой.

                            0
                            Всё равно странно. У меня у самого Android'ов для тестирования десяток… но для разработки у меня три ноута и десктоп. Так что разница «на порядок» всё равно не вытанцовывается… Разница в 2-3 раза — да, может быть…
                      0
                      Хоть с формальной, хоть с неформальной. Массовый потребитель теряет интерес к десктопу, и чем дальше, тем больше будет терять. А там, извините, правит бал уже не винда
                        +2
                        Эмм, а что, Андроид АПИ принципиально лучше, чем ВинАПИ?
                          –1
                          Да. Уже хотя бы тем, что там у каждого приложения своя песочница. UWP, конечно, ничем не хуже, только вот беда: все те сотни тысяч (миллионы?) приложений для Windows, из-за которых ей продолжают пользоваться — ни разу не UWP…
                            0
                            Эмм, а что, Андроид АПИ принципиально лучше, чем ВинАПИ?

                            Вы свой вопрос поняли вообще сами?
                    0
                    15 мегабайт — мизерная цена за возможность просто взять и реализовать свою функциональность вместо многочасового ковыряния в библиотечной ерунде или в скелете приложения.
                      +2
                      а потом в результате такого мышления получаем скайп в 300 мб, и мессенджеры по 133 мб (WhatsApp), лишь бы только не ковыряться (ключевое слово никогда)
                        +2
                        Ну так вы С++ с Qt не путайте с другими решениями (Delphi, Java и прочее).
                        Посмотрел, две базовые библиотеки Qt для окошек на моих никсах весят 4.5Мб (QtCore) и 6.5 (QtWidgets). Этого достаточно, чтобы быстро, надежно и без геморроя написать приложение с окошками.

                        Потом, Qt — не шаблонная библиотека, она не раздувает код. А также не тащит с собой виртуальные машины, райтаймы и т.д. Вот прям сейчас под рукой простенькое приложение на 10 окошек с сетью и БД занимает 300кБ в релизе. Размер смешной, чтобы его считать хоть сколько-то значимым.
                          +1
                          две базовые библиотеки Qt...

                          Этого недостаточно. Посмотри (и проверь на чистой системе) размер полного деплоя, особенно для Qt5.

                          А шаблоны несущественно раздувают код.
                          В целом, Кьют сравнимо с дНЕТ и вполне терпимо на сегодняшний день.

                          Кстати, Дельфи/БСБ дают меньший оверхед для голой ВЦЛ
                            0
                            Этого недостаточно. Посмотри (и проверь на чистой системе) размер полного деплоя, особенно для Qt5.

                            Я последний раз что-то на Винде разворачивал еще во времена активной жизни Qt4, и не припомню, чтобы размеры как-то впечатляли. Если открыть официальный мануал, то речь идет о 15-20мб (или +27, если с ICU) для mingw. Если msvc собрать, меньше будет.

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

                            А шаблоны несущественно раздувают код.

                            Еще как раздувают. Какая-нибудь жирная либа на шаблонах типа CGAL сделает прогу уровня хеллоуворлд в несколько мегабайт на старте.

                            В целом, Кьют сравнимо с дНЕТ и вполне терпимо на сегодняшний день.

                            Ну, если мы деплоим на винду, то там, как правило, весь рантайм и либы (для минимального приложения) лежат в системе, поэтому бинарники весят очень мало. Зато на никсы деплоить дотнет — тот еще геморрой.
                              0
                              Вот тут поподробнее дерево разрисовано. С введением QtQuick2 и своего GL-рендерера в Qt5 размер деплоя стал гораздо больше.
                                0
                                Ну если есть какие-то проблемы с деплоем, зачем тащить с собой QtQuick, когда для классических форм вполне достаточно старых ламповых виджетов?
                                0
                                Приходилось работать с одной специфичной программой одного вендора (который и на Хабре что-то пописывает в свой корпоративный блог)… Программа выпускается только под Windows, о том, чтобы портировать её на другие ОС, я не слышал. Задачи, которая должна решать программа:
                                1. Периодически обращаться к некоторому хосту в сети (где развернут веб-сервер) и скачивать оттуда некий XML-файл (до 10 Мб)
                                2. Сконвертировать (обработать) этот XML в специфический формат
                                3. Результат обработки залить на другой хост сети

                                GUI используется только для «моргания иконкой» (сигнализация о процессе работы программы) в SystemTray и для задания настроек (окно с полями, где указываются хост-источник, хост-приемник, периодичность опроса источника, кнопки «OK» и «Cancel»)…
                                Размер каталога установленной программы составляет в районе 40 Мб. Я до сих пор не могу понять: зачем для такой программы нужно было использовать Qt (вендор её сделал полностью на Qt)?
                                Стабильностью работы программа не отличается (о чем иногда здесь пишут хабрапользователи в соответствующих тредах), жалобы/багрепорты отправляются вендором в /dev/null на первую линию обороны техподдержки, которая никогда не называет сроков выпуска исправлений (не говорят даже магическое «Ошибка будет исправлена в ближайшем релизе/сборке»).
                                Все это привело к тому, что клиентам мы теперь ставим свое решение, написанное на WinAPI+COM, вместо этого «чудовища». Размер каталога установленного нашего решения составляет в районе 2 Мб, максимальное потребление памяти в районе 25 Мб (программа вендора может запросто отожрать 70 Мб, да еще и ядра процессора нагрузить до 70% и перестать реагировать на пользователя).
                                  0
                                  Говном можно и золото измазать, ну.
                                  Стабильность, огромный деплой, баги, глюки — это все не от прямых рук.
                          +2
                          Чтобы «просто взять и реализовать» обычно достаточно разок почитать основы и далее при необходимости подсматривать в хелпы. ВынАпи достаточно последователен и работать с чем-то новым там не сложно. Да и для обычных задач быстро делаются шаблоны и дальше используются готовые куски. А для простых задач вообще можно слепить за пару минут диалоговое окошко любой сложности в конструкторе и юзать его. Это уже не говоря о том, что машинах слабее топовых разница в скорости работы того же Qt и голого апи заметна невооруженным глазом. Про чуть более старые машины вообще молчу.

                          А вот если вам нужно запилить мс-ворд с его сотней видов окошек, панелек и т.д. — то да, придётся тащить фреймворк.
                            +1
                            для простых задач вообще можно слепить за пару минут диалоговое окошко любой сложности
                            Ой ли любой? А как там с layout, по-прежнему любые динамические изменения размеров нужно реализовывать руками в WM_SIZE? Думаю, что да. И это, скорее всего, правильно. Но этот уровень программирования слишком низкий для написания приложений. Это уровень написания библиотеки для написания приложений.

                            Ваши мысли полностью аналогичны утверждению: «Программировать нужно на ассемблере.» Нет, я не могу с этим согласиться.
                              +1
                              А как же WM_SIZING?
                              В свое время написал именно такую библиотеку, где кроме своей layout системы много еще разных (нужных мне) вещей, и которую периодически дополняю по мелочам. Сделал свой шаблон с ней для VS и создаю вполне себе приложения на чистом WinAPI за пару минут. Не спрашивайте зачем мне это. Просто почесать свое эго. :-)
                                0
                                Делаете окно, в парент диалогу цепляете его и получаете обычное окно с с фиксированным фрагментом с контролами. Ну и ничего не мешает один раз сделать обработку размера и цеплять его шаблоном если уж так надо. Но первый вариант проще.
                                0
                                В свое время была проблема достать стоящую литературу по разработке на WinAPI, все что не было описано в книги, искали через файлы заголовков самого WinAPI. Вполне все было понятно.
                                  0
                                  Достаточно было найти хелп (1 файл) по api (он был в комплекте с sdk но можно было и отдельно найти). Собственно не считая каких-то совсем хитрых случаев его было обычно достаточно. До сих пор иногда пригождается, хотя .hlp сейчас фиг чем откроешь. Каждый раз приходится искать старый просмотрщик.
                              0
                              Статическая линковка плюс правильные настройки компилятора/линкера — и размер будет на порядок меньше (в исполняемый файл будет включено только то, что реально используется).
                                0
                                Это понятно. Однако не только к размеру претензии. Я чуть ниже написал, в чём основная проблема. Размером в наше время, к сожалению, уже никого не удивишь.
                              +2
                              > В том же делфи писал с применением WinAPI (из *.dpr, с функцией WinMain определенной в прототипах хе-хе) и получал уже 10-15 кб.

                              Для делфи еще была библиотека KOL, которая, сохраняя визуальный конструктор, уменьшала размер файла раз в 5-10.
                                0
                                Почему была? Она и сейчас есть. И даже портирована в FreePascal.
                              0
                              Особняком стоит WPF, который не использует WinAPI для отрисовки.
                                –1
                                Да-да, больше систем независимых и разных. А потом поменял настройки вынды и у тебя в лучшем случае пачка разномастных окошек образовалась. А в худшем вообще треш и угар. Половину программ можно поломать (гуй имеется ввиду) просто изменив масштаб шрифта. Любая нестандартная отрисовка окон рано или поздно ломается. Проверено.
                                  +1
                                  Сейчас уже не всё так плохо. За счёт того, что приложения используют фреймворки, а не голый API, проблем с масштабированием стало значительно меньше.
                                    +2
                                    Проблем сейчас меньше не по этому, а потому, что за прошедшие годы все фреймворки по ним успели уже походить. Ну и речь даже не совсем об этом, а больше о том, что если надо делать не «весёленько» а нормальный рабочий интерфейс, то лучше использовать либо апи, либо фреймворк на родном апи. А то сейчас прямо как назад в 90е, когда в win95 все начали городить кастомные интерфейсы. В итоге нет 2х похожих программ. Причём если настройки родного интерфейса я хотя бы могу поменять, то неродные… Вот сейчас на работе: оракловый манагер — какая-то хрень на яве (дико тормозная, но речь не об этом) — это один. Ворд и прочие офисы со своими плоскими панельками — это второй. За однотонные окна без рамок автора хочется от… ть отдельно. иногда понять где заканчивается одно окно и начинается другое просто невозможно. Порой приходится сначала подвигать окно за заголовок, чтобы понять, где у него границы… И что это был не его заголовок, т.к. цветом он тоже не выделяется. VC — у него тоже своя атмосфера. Ну и родной интерфейс — это четыре. И этот салат реально бесит как по причине внешнего вида, так и по причине невозможности его исправить или необходимости настраивать отдельно в каждой программе (да и то что в них можно настроить кроме выбора темы?)
                                    А, чуть не забыл. Есть ещё и наша софтина с отдельно у… дивительным интерфейсом (тоже какой-то фреймворк). Но это другая история.
                                  +1
                                  Только для отрисовки клиентской области, сами окна на WinAPI.
                                  0
                                  А вы уверены что Qt использует WinAPI для отрисовки кнопок?
                                  Найти HWND кнопок и других элементов в приложениях Qt под Windows не получится.
                                  +1
                                  Пишите, статей на эту тему крайне мало и подробные референсы на русском языке не помешали бы
                                    +1
                                    Надо!!!
                                    Мучительно ковыряю в данный момент эту тему, и понимаю что я многое упустил…
                                    Пишите пожалуйста!
                                      +2
                                      С интересом прочту про Per-Monitor DPI-Aware.
                                      0
                                      Примерно похожий минимальный каркас приложения создается в Dev-Cpp.
                                        +6

                                        Когда то информация по winapi казалась чем-то очень новым, а теперь она подается как что-то очень старое, типа забытые знания древних цивилизаций:)

                                          0
                                          Забытые знания древних цивилизаций — это скорее VMS или GEOS всё-таки. А тут речь идёт о базе, над которой всё надстроено…
                                            0
                                            WinApi сейчас даже многими windows разработчиками воспринимается как программы на Коболе, исполняющиеся на каком-то мэйнфрейме, но имеющими .Net адаптер. Избранные могут поправить что-то в их коде по настоятельной просьбе или послать " так работать не будет" без возможности проверить.
                                          +1
                                          Вот только кто об этом знает и этим пользуется?

                                          В это суровое время уже не модно читать MSDN? O tempora....

                                            +1
                                            Диски!
                                              –1
                                              Дикси?
                                                +2

                                                О, да. Сколько корпоративного трафика было списано на "мы MSDN качали". Ну и стимул читать ее был.А сейчас? Вот она дока, только руку протяни, но "мало кто знает".

                                              –6
                                              А есть ли смысл в наше время в данной статье? Зачем сейчас вообще тратить время на разработку на голом WinAPI? Лет 15 назад уже особо не видел чистого WinAPI, MFC использовали.
                                                –1
                                                На собеседованиях я всегда показывал свою программу на апи. Часто она вызывает интерес и смею предположить поднимает уровень в глазах собеседников.
                                                Это как минимум.
                                                Как максимум — кто знает с чем придется иметь дело завтра.
                                                  0
                                                  Вчегда считал, что важен конечный результат, а не используемый инструмент. Я показывал к примеру свой CGridCtrl для MFC, с попиксельным скроллом, in-place редактированием (и возможностью без проблем добавлять свои редакторы) и объединением ячеек. И на мой взгляд это важнее для среднестатистического работодателя. Изучать же полёт фантазии на собеседовании особого времени нет, да и особо никто не будет даже не в рамках собеседования это делать.
                                                  0
                                                  WinAPI — это как ассемблер. Пользоваться не обязательно, но знать совсем не повредит.
                                                    +4
                                                    MFC ужасен. Тот же winAPI, только с еще большей кучей грязи и не самого лучшего стиля программирования. Уж если упрощать взаимодействие с GUI для разработчиков, то путём полнейшей инкапсуляции всяких HWND и прочей ереси.
                                                      0
                                                      Угу, VCL в Delphi/Builder был прекрасен. Я пытался перейти на MSVC, но от MFC меня просто тошнило. В итоге так и продолжал сидеть на Delphi/Builder, пока не вышел .NET с Windows Forms.
                                                        0
                                                        Интерфейс Скайпа в до-микрософтовскую эпоху был сделан в Delphi, а вот «потроха» уже были написаны на MSVS.
                                                          0
                                                          Почему был? Он и сейчас есть.
                                                          0
                                                          MFC ужасен. Тот же winAPI, только с еще большей кучей грязи и не самого лучшего стиля программирования. Уж если упрощать взаимодействие с GUI для разработчиков, то путём полнейшей инкапсуляции всяких HWND и прочей ереси.
                                                          Так MFC на 90% и есть ВинАПИ с полнейшей инкапсуляцией всяких ХВНД и диспатчеров =)
                                                            +2
                                                            Вы не совсем верно поняли мой комментарий. Про то, что это обёртка winAPI я итак в курсе… MFC был сделан давным давно, с использованием далеко не самых лучших практик. WinAPI там так и лезет на ружу. Хоть этот MFC и призван упростить разработку GUI приложений под Win, но он всё-равно придерживается концепций, принятых в голом winAPI, и эти все передачи всяких кодов, определенных константами целочисленного типа… Помимо этого — это уже морально устаревший код (какой-то СИ с классами, а не С++11/14/17). Да, там есть множество фич, по типу сплиттеров и Docked-окон. Но выполнено это всё в ужасной, по сегодняшним меркам, манере.
                                                            0
                                                            А я и не хвалю его, да, он ужасен. Но он позволял сэкономить очень много времени. WTL мы так и не дождались в те времена.
                                                              0
                                                              MFC действительно не отличаются качественным кодом или показательным стилем программирования, но они существенно упрощают создание программ на вин-апи. На предыдущей работе был большой виндовый проект, написанный с использованием MFC, занимался его разработкой с нуля около 7 лет. Никаких глюков или нестабильной работы за ним замечено не было, наоборот, система работала очень стабильно и при этом быстро. Однажды нас даже спросили «а почему она у вас так быстро работает?». При этом, систему можно запускать на компьютерах с частотой процессора ниже 1 ГГц и не замечать серьезного замедления (конечно, если работать с умеренным для такого компьютера объемом информации).

                                                              Да, создание интерфейса пользователя на MFC всегда требует дополнительной работы, если сравнивать с более современными технологиями. Но на начальном этапе пишутся требуемые классы (например, класс диалога, позволяющий перемещать/растягивать элементы при изменении размера диалога) или даже свои «контролы», и дальнейшая работа уже идет быстрее. При этом ты всегда знаешь, что если какого-то функционала нет, ты можешь напрямую использовать WinAPI. Да и исходники MFC доступны, можно зайти в них отладчиком, если нужно посмотреть, что и как там работает.

                                                              В итоге, при создании диалогов больше всего раздражало даже не то, что надо все писать руками, а их ужасный редактор в студии (под конец была 2012) — например, чтобы поменять порядок контролов (для правильной навигации клавишей Таб) было проще отредактировать файл ресурсов в фаре, чем жать Ctrl-D и по очереди тыкать в них мышкой. Также факт того, что все координаты и размеры в диалогах измеряются в DLU, не равных пикселям на «стандартном» разрешении в 96 DPI тоже сильно огорчает, делая редактирование диалогов в студии далеко не самой приятной задачей.
                                                                0
                                                                А сейчас на чём бы посоветовали писать GUI? Желательно ещё с хорошим и современным стилем программирования конкретно на C++11/14/17. Вот вот, кстати, должен выйти C++/winRT (обычный/стандарт С++17 без всяких модификаций, по типу C++/CLI, C++/CX), но это сугубо под Win 8.1 — 10. Короче, библиотека Windows Runtime во всей своей красе и без лишнего оверхэда, станет доступной в чистом С++17. Планируют включить её в VisualStudio 2017 v15.7.
                                                                  0
                                                                  WinRT это и есть оверхед. См например статью тут
                                                                    –1
                                                                    Ну так там C++/CX, а тут C++17) Можете почитать здесь. Там же можно в статьях найти сравнение производительности с С++/CX и с C#, посмотреть выступление с конференции CPPCON2017, ну и почитать прочие подробности. Проект еще не вышел в свет. Пока доступен только инсайдерам.
                                                                    0
                                                                    А сейчас я занимаюсь более «системным» программированием — пишу сервисы или консольные утилиты, не использующие GUI. Для них использую «чистый» С++ и STL. Коллеги пишут интерфейсы на QT (т.к. надо и под винду, и под линукс), но не в восторге от него, хотя у нас по определенным причинам далеко не самая последняя версия (5.2). Другие коллеги пишут интерфейсные и веб-приложения на C#.

                                                                    Поэтому, если речь идет о С++, я ничего не могу порекомендовать. Если бы лично передо мной встала задача написания GUI приложения, «по-старинке» начал бы с MFC, т.к., например, тенденции UI Windows 8/10 мне вообще не нравятся, когда речь идет о десктопном приложении.
                                                                      0
                                                                      например, тенденции UI Windows 8/10 мне вообще не нравятся, когда речь идет о десктопном приложении.
                                                                      А, кстати, этот феномен кто-нибудь обьективно изучал? Насколько людям вообще современные интерфейсы нравятся?

                                                                      Когда Windows 95 вышла, то народ был в восторге, и только доли процента говорили «всё переделали, ни черта непонятно» (хотя как раз для них в Windows95 есть ProgMan и можно за 5 минут вернуть старый интерфейс). «На спор» я это сам делал (хотя это было уже во времена XP), но практически я не видел никого, кто бы этим пользовался.

                                                                      Против Windows XP протестов было уже больше, а возвращать старый интерфейс стали чаще. И чем дальше в лес, тем хуже: возрат старого интерфейса становился всё сложнее, но статей соответствующих публикуется всё больше.

                                                                      Революция в интерфейсе GNOME была встречена с таким «воодушевлением», что доля Linux'а на десктопе опять упала после перехода на GNOME 3.x (народ начал возвращаться на Windows), был сделан fork, но по настоящему возврат начался только тогда, когда Windows 8 со своими экспериментами сделала невыносимым и существование в Windows тоже.

                                                                      От новых версий iOS и Android'а тоже люди плюются (пока что недостаточно для того, чтобы разыскивать способы возращать старый дизайн целиком, но есть люди, которые ради блобов рутуют устройства).

                                                                      Про всякие Skype и MS Office с Ribbon'ом я уже молчу — да, есть фанаты, но есть и люди, которые из-за Ribbon'а пользуются MS Office 2003!

                                                                      Мир сошёл с ума или я чего-то не понимаю в современных интерфейсах? Зачем менять интерфейс, если это приведёт к тому, что люди будут ваш продукт избегать?

                                                                      P.S. Разработчики новых интерфейсов, разумеется, проводят исследования, но это не совсем то. Как правило императивом там ставится «как нам заменить интерфейс так, чтобы не все пользователи сбежали». Варианта «выкинуть все изыскания к чёртовой матери и оставить всё как есть» обычно в таких исследованиях непредусмотрено (понятно почему: если так поступить, то уж точно ни бонусов, ни повышений не видать), но это, собственно, делает их почти бесполезными для оценки качества интерфейса (потому что ёжику понятно, что старый, привычный, интерфейс — наиболее реальная альтернатива, и если вы его игнорируете, то говорить о какой-либо обьективности нельзя).

                                                                      P.P.S. С другой стороны когда в посторонней дискуссии встречается фразы типа есть еще Gerrit, который там как-то с этим борется, но интерфейс из 90х убивает все желание пользоваться этим, то понимаешь, что у современных новомодных интерфейсов есть не только противники (их-то сразу видно на форумах), но и фанаты, которые обычно просто молчат (они и так уже побеждают, зачем им что-то говорить?), что, собственно, и порождает желание увидеть-таки независимое исследование…
                                                                        0
                                                                        В случае перехода к чему-то новому всегда есть консерватизм, тормозящий этот процесс, поэтому видя новый интерфейс не всегда понятно — действительно ли так плох этот интерфейс или же он просто непривычен.

                                                                        Win 3.11 я особо не застал, т.к. компьютер появился только в 98-м году, а где до этого удавалось поработать с компьютерами, обычно был дос. Win 95 какого-либо дискомфорта при работе не вызывала, её интерфейс был изучен и казался вполне логичным на тот момент. Переход на Win XP был отложен, но совсем не по причине интерфейса — он как раз казался тогда очень даже красивым, а из-за того, что под Win 9х требовала меньше ресурсов и под нее был опыт написания драйверов vxd на асме, а я тогда как раз что-то писал.

                                                                        Переход на 7-ку также был отложен до следующего апгрейда компьютера (когда перешел на 4-х ядерный процессор и купил 16 гб памяти). Её интерфейс не был принят так однозначно, однако после настройки «под себя» он оказался вполне удобным.

                                                                        Переход на семейство 8-10 так и не состоялся по причине действительной убогости нового интерфейса (имхо). МС решили адаптировать десктопную винду для планшетов, чтобы можно было тыкать в нее пальцем — да действительно, если на планшете запустить классический интерфейс, то работать с ним получится с большим трудом, т.к. попасть пальцем в мелкую иконку tree expand действительно трудно. Но зато работать с мышкой в новом интерфейсе просто неудобно. Да и тенденция к сокращению количества цветов мне не по душе — я специально оставил себе офис 2010, т.к. он последний в нормальной цветовой гамме. На работе стоит как раз 2013, каждый раз плююсь при его запуске. Также плавность перемещения курсора в excel на мой взгляд делает работу с ним только хуже (может где можно отключить, не смотрел).

                                                                        Тем не менее, есть люди, которым новый интерфейс 10-ки кажется очень логичным, красивым и удобным.
                                                                +1

                                                                У разных людей разное ощущение времени.
                                                                И время отклика программ важно чувства для комфорта определенной доли IT-персонала.
                                                                По этому программы на WinAPI, которые очень эффективно работают, всегда будут востребованы.
                                                                А следовательно и специалисты.

                                                                +5
                                                                20 лет назад, учась в школе, вызывал функции WinApi на VisualBasic тогда это казалось крутой магией. Сейчас это кажется детством, теплым и безоблачным… Думал, что сейчас этого уже нет, ан нет
                                                                  +2
                                                                  А что здесь сложного? После трудов Чарлза Петзолдта то да ещё после их перевода.
                                                                    +1

                                                                    А можно чуть подробнее, в чем проблема заключается?
                                                                    Закопался в исходники очень многолетней давности, нашел вполне себе ожидаемое


                                                                    LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam){
                                                                       switch (message){
                                                                        ... 
                                                                        case WM_KEYDOWN:        OnKey(hwnd,wParam,lParam);break;
                                                                        ...
                                                                        default: return DefWindowProc(hwnd,message,wParam,lParam);
                                                                        }
                                                                       return 0;
                                                                    }

                                                                    Вроде работало
                                                                    Или речь о каком-то особенном диалоге?

                                                                      0
                                                                      Вся проблема возникает, когда в дело вступает IsDialogMessage. Как я уже писал, она нам дает преимущества в виде навигации в стиле диалогов, но при этом лишает возможности получать WM_KEYDOWN/KEYUP. Добавьте ее в цикл и WM_KEYDOWN у вас больше не придет. Вы можете ее не использовать явно, но если вы используете DialogBox/DialogBoxParam/DialogBoxParamIndirect — она будет вызываться (внутри этих функций).

                                                                      В Вашем конкретном случае приведенный код — обычная процедура обработки для вашего собственного окна (LRESULT вместо INT_PTR, который у DlgProc и DefWindowProc вместо 'return 0'). И цикл обработки сообщений у Вас, скорее всего, IsDialogMessage не использует.
                                                                        0
                                                                        Понял, спасибо, при прочтении видимо пролетел мимо этого момента.
                                                                      +2

                                                                      Каждый разработчик под Win должен написать хотя бы одно приложение на чистом WinApi!


                                                                      И потом больше никогда не пользоваться им :)

                                                                        +1
                                                                        А как же маленькие и самодостаточные исполняемые файлы? ;-)
                                                                          –2
                                                                          То есть перестать писать для винды? Тогда правильней будет написать так: «Каждый разработчик LINUX-приложений должен хоть раз попробовать WinAPI, сделав одно приложение на нём». Только им то это зачем?
                                                                            0

                                                                            Не слишком разумно находясь в Windows не пользоваться ресурсами и вызовами функций ОС.

                                                                              0

                                                                              Программирование на WinApi вызывает боль, потому что даже для си оно написано крайне кандовым. Эти самые ресурсы и вызовы стоит использовать, если за разборки в этом аду будут платить.
                                                                              DWORD BOOL VOID WINAPI CALLBACK LPVOID HIWORD TCHAR LPCTSTR STRADAY


                                                                              PS: WinApi на C# — те же системные вызовы, но подружелюбнее.

                                                                                0
                                                                                Ага, щас. Оно было бы подружелюбнее если бы поддерживалось MS. Но ведь каждую используемую функцию нужно импортировать вручную, и никто автоматически не проверит ее сигнатуру…
                                                                                  0
                                                                                  Вот врать то. Во-первых API — он и в Африке API, от языка он не зависит. Во-вторых он вполне дружелюбен, хоть и не интуитивен. В-третьих на решётке ничего хорошего как раз не напишешь, единственное, для чего имеет смысл с ней мучиться, — это чтоб получить утечку памяти, добиться которой на c/c++ — квест, близкий к не решаемому. Простой пример: динамический массив с не стандартным размещением элементов и функцией-членом, обрабатывающей все элементы в не стандартном порядке. Как без указателя перебрать их? Менять адрес, на который ведёт ссылка? Теряя при этом адрес нулевого элемента. Ах да, настоящая ссылка же привязывается к переменной один раз и перекинуть её на другую уже нельзя. Построив «вручную» «коллекцию»? Здравствуй, статика.
                                                                                    0
                                                                                    А можно подробнее про утечки памяти на C#?
                                                                                      +1
                                                                                      чёт вы тут напутали всё напрочь. Вы знаете о существовании итераторов, контейнеров vector, list, и прочих в С++ и об аналогах в С#. Вы знаете о том, что с массивами в голом виде не работают? Знаете о том, что в шарпе утечку памяти получить сложнее, чем в С++ (не используя умные указатели)? Знаете о существовании в С++ std::array и скором появлении std::dynarray и о том, что подобные штуки люди сами пишут?
                                                                                        +1
                                                                                        я, если что, не придираюсь и т.п. Из вашего комментария не совсем ясно то, что конкретно вы подразумевали.
                                                                                      0
                                                                                      Вот именно.
                                                                                        –1
                                                                                        Вот именно. Точнее это глупо.
                                                                                    +1

                                                                                    В своё время книга Дэна Эплмана Win32 и VB сэкономила много сил и времени при разработке.

                                                                                      –1
                                                                                      Ни разу её не видел.
                                                                                      0
                                                                                      Эх, были времена. Помню изучал, дошел до примеров диалоговых окон на WinAPI. Плюнул и вернулся на MFC.
                                                                                      Но для графических приложений, типа игр — самое то.
                                                                                        +2
                                                                                        Только так и пишу. Никаких MFC или OWL! Может быть код и будет чуточку объёмней (и то не факт), зато конечный результат будет — супер. Приложения получаются компактней и быстрей. А у этих библиотек время от времени параметры в методах меняются. Я в своё время накололся на этом, когда у борланда сначала была библиотека owl1, а потом пошла owl2, и параметры в функциях поменялись. С тех пор зарёкся. И описание в таких библиотеках тоже остаётся желать лучшего. А описание функций WinAPI всегда можно найти. Поэтому автора заметки поддерживаю за здоровый консерватизм.
                                                                                        Кстати, объектный подход для окон хорошо ложится в код. Любую функцию обратного вызова можно переделать, как нужно. В своё время таким образом мне удалось сделать форматный ввод в окнах. Причём отладка много времени не заняла.
                                                                                          +1
                                                                                          Вобщем-то, за все время, пока я ковыряю WinAPI, собралось много разных штук, описание которых тянет еще на несколько статей.
                                                                                          +1
                                                                                          Там не на несколько статей, а на несколько книжек. Вон Фроловы, может помните таких, так те несколько книжек написали по Windows API. Хорошие книжки были с примерами, с разбором.
                                                                                          Лет 10 назад (может больше) я на WinAPI написал приложение для абонентского отдела АТС. Ничего там сложного не было, простой учёт оплаты и начисление. Плюс несколько интегрированных отчётов. СУБД Interbase. Работало всё мгновенно. 500 записей начисления оплаты одной командой SQL выполнялось меньше чем за секунду. Хотя там была по тем временам довольно таки посредственная машина с каким-то селерончиком. Уже не помню. Загрузочный модуль был, не поверите, 170 килобайт.
                                                                                          А сейчас пишут непонятно на чём, работает всё через пень-колоду, никаких процессоров и памяти не настачишь!

                                                                                          Only users with full accounts can post comments. Log in, please.