Диалоги Windows API без использования файлов ресурсов

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

Как я могу это сделать?

Простой пример подойдет. Ничего сложного пока не делаю.

почему вы против наличия файла rc?

Brian R. Bondy 15.09.2008 00:48

Я хочу иметь возможность изменять его во время выполнения.

conmulligan 15.09.2008 00:51

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

ahmedsafan86 11.04.2013 11:44

@ BrianR.Bondy Ну, раз уж вы спросили, мне, честно говоря, было просто любопытно посмотреть, есть ли способ создавать графические интерфейсы в winapi, используя только язык C. Ради интереса я исследую, как .NET построен поверх winapi для создания Windows Forms, и я просто решил начать как можно более низкоуровнево и вручную; концептуально понять, что такое окно на самом деле.

jrh 24.12.2017 07:17
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
23
4
19 523
5

Ответы 5

Взгляните на этот инструментарий, который описывает, как создавать диалоги без файлов ресурсов.

Это в WTL. Однако я уверен, что вы можете разделить внутреннее устройство, чтобы добиться того же, используя Win32 API напрямую.

Попробуйте поискать в MSDN "шаблоны диалогов в памяти".

См. Это, например: Диалоговые окна

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

jrh 24.12.2017 07:24

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

Руководство по Windows API (только C Win32 API, без MFC):

Руководство по Windows API

Пока что это единственный ответ, который, кажется, действительно предоставляет код для использования CreateWindow вместо файла rc.

jrh 24.12.2017 07:27

... однако, как я узнал в своем ответе, диалог, созданный с помощью CreateWindow, не ведет себя точно так же, как диалог, созданный с помощью редактора диалогов VS. Штопать.

jrh 04.01.2018 16:10

Если все, что вы хотите сделать, это показать окно с элементами управления, можно создать окно без использования файлов / сценариев ресурсов (.rc).

Это не то же самое, что диалог, но это может быть проще, чем создание диалога программным способом.

Сначала несколько примечаний о том, как это делается:

  • Вместо разработки диалогового окна в файле rc вы можете вручную использовать CreateWindow (или CreateWindowEx) для создания дочерних окон главного окна. (для программистов .NET Windows Forms эти окна похожи на Controls).

  • Этот процесс вообще не будет графическим (вам нужно будет вручную ввести расположение и размер каждого окна), но я думаю, что это может быть отличным способом понять, как диалоги создаются под капотом.

  • У неиспользования реального диалога есть некоторые недостатки, а именно эта вкладка не будет работать при переключении между элементами управления.


О примере:

  • В этом примере показано диалоговое окно с двумя кнопками, поле редактирования (программисты .NET Windows Forms могли бы подумать о нем как о TextBox) и флажок.

Screenshot of window

Он был протестирован в следующих условиях:

  • сборка x86
  • x64 сборка
  • Сборка Unicode (определены UNICODE и _UNICODE)
  • Сборка без Unicode (UNICODE и _UNICODE не определены)
  • Создан с помощью компилятора C Visual Studio
  • Создан с помощью компилятора Visual Studio C++
  • ОС: Windows 10 64 бит

Теперь код:

Обратите внимание, что было добавлено большое количество комментариев, чтобы попытаться задокументировать функции Windows, я рекомендую скопировать / вставить их в текстовый редактор для достижения наилучших результатов.

// This sample will work either with or without UNICODE, it looks like
// it's recommended now to use UNICODE for all new code, but I left
// the ANSI option in there just to get the absolute maximum amount
// of compatibility.
//
// Note that UNICODE and _UNICODE go together, unfortunately part
// of the Windows API uses _UNICODE, and part of it uses UNICODE.
//
// tchar.h, for example, makes heavy use of _UNICODE, and windows.h
// makes heavy use of UNICODE.
#define UNICODE
#define _UNICODE
//#undef UNICODE
//#undef _UNICODE

#include <windows.h>
#include <tchar.h>

// I made this struct to more conveniently store the
// positions / size of each window in the dialog
typedef struct SizeAndPos_s
{
    int x, y, width, height;
} SizeAndPos_t;

// Typically these would be #defines, but there
// is no reason to not make them constants
const WORD ID_btnHELLO = 1;
const WORD ID_btnQUIT = 2;
const WORD ID_CheckBox = 3;
const WORD ID_txtEdit = 4;
const WORD ID_btnShow = 5;

//                                    x,      y,      width,  height
const SizeAndPos_t mainWindow   =   { 150,    150,    300,    300 };

const SizeAndPos_t btnHello     =   { 20,     50,     80,     25 };
const SizeAndPos_t btnQuit      =   { 120,    50,     80,     25 };
const SizeAndPos_t chkCheck     =   { 20,     90,     185,    35 };

const SizeAndPos_t txtEdit      =   { 20,     150,    150,    20 };
const SizeAndPos_t btnShow      =   { 180,    150,    80,     25 };

HWND txtEditHandle = NULL;

// hwnd:    All window processes are passed the handle of the window
//         that they belong to in hwnd.
// msg:     Current message (e.g., WM_*) from the OS.
// wParam:  First message parameter, note that these are more or less
//          integers, but they are really just "data chunks" that
//          you are expected to memcpy as raw data to float, etc.
// lParam:  Second message parameter, same deal as above.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

    switch (msg)
    {

    case WM_CREATE:
        // Create the buttons
        //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

        // Note that the "parent window" is the dialog itself. Since we are
        // in the dialog's WndProc, the dialog's handle is passed into hwnd.
        //
        //CreateWindow( lpClassName,        lpWindowName,       dwStyle,                                x,          y,          nWidth,         nHeight,            hWndParent,     hMenu,              hInstance,      lpParam
        //CreateWindow( windowClassName,    initial text,       style (flags),                          xPos,       yPos,       width,          height,             parentHandle,   menuHandle,         instanceHandle, param);
        CreateWindow(   TEXT("Button"),     TEXT("Hello"),      WS_VISIBLE | WS_CHILD,                  btnHello.x, btnHello.y, btnHello.width, btnHello.height,    hwnd,           (HMENU)ID_btnHELLO, NULL,           NULL);

        CreateWindow(   TEXT("Button"),     TEXT("Quit"),       WS_VISIBLE | WS_CHILD,                  btnQuit.x,  btnQuit.y,  btnQuit.width,  btnQuit.height,     hwnd,           (HMENU)ID_btnQUIT,  NULL,           NULL);

        // Create a checkbox
        //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        CreateWindow(  TEXT("button"),      TEXT("CheckBox"),   WS_VISIBLE | WS_CHILD | BS_CHECKBOX,    chkCheck.x, chkCheck.y, chkCheck.width, chkCheck.height,    hwnd,           (HMENU)ID_CheckBox, NULL,           NULL);

        // Create an edit box (single line text editing), and a button to show the text
        //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        //Handle        = CreateWindow(windowClassName,    windowName,           style,                              xPos,       yPos,       width,          height,             parentHandle,   menuHandle,         instanceHandle, param);
        txtEditHandle   = CreateWindow(TEXT("Edit"),       TEXT("Initial Text"), WS_CHILD | WS_VISIBLE | WS_BORDER,  txtEdit.x,  txtEdit.y,  txtEdit.width,  txtEdit.height,     hwnd,           (HMENU)ID_txtEdit,  NULL,           NULL);

        //CreateWindow( windowClassName,    windowName,         style,                                  xPos,      yPos,      width,          height,           parentHandle,   menuHandle,         instanceHandle, param);
        CreateWindow(   TEXT("Button"),     TEXT("Show"),       WS_VISIBLE | WS_CHILD,                  btnShow.x, btnShow.y, btnShow.width, btnShow.height,    hwnd,           (HMENU)ID_btnShow,  NULL,           NULL);

        // Create an Updown control. Note that this control will allow you to type in non-number characters, but it will not affect the state of the control

        break;

    // For more information about WM_COMMAND, see
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647591(v=vs.85).aspx
    case WM_COMMAND:

        // The LOWORD of wParam identifies which control sent
        // the WM_COMMAND message. The WM_COMMAND message is
        // sent when the button has been clicked.
        if (LOWORD(wParam) == ID_btnHELLO)
        {
            MessageBox(hwnd, TEXT("Hello!"), TEXT("Hello"), MB_OK);
        }
        else if (LOWORD(wParam) == ID_btnQUIT)
        {
            PostQuitMessage(0);
        }
        else if (LOWORD(wParam) == ID_CheckBox)
        {
            UINT checked = IsDlgButtonChecked(hwnd, ID_CheckBox);

            if (checked)
            {
                CheckDlgButton(hwnd, ID_CheckBox, BST_UNCHECKED);
                MessageBox(hwnd, TEXT("The checkbox has been unchecked."), TEXT("CheckBox Event"), MB_OK);
            }
            else
            {
                CheckDlgButton(hwnd, ID_CheckBox, BST_CHECKED);
                MessageBox(hwnd, TEXT("The checkbox has been checked."), TEXT("CheckBox Event"), MB_OK);
            }
        }
        else if (LOWORD(wParam) == ID_btnShow)
        {
               int textLength_WithNUL = GetWindowTextLength(txtEditHandle) + 1;
               // WARNING: If you are compiling this for C, please remember to remove the (TCHAR*) cast.
               TCHAR* textBoxText = (TCHAR*) malloc(sizeof(TCHAR) * textLength_WithNUL);

               GetWindowText(txtEditHandle, textBoxText, textLength_WithNUL);

               MessageBox(hwnd, textBoxText, TEXT("Here's what you typed"), MB_OK);

               free(textBoxText);
        }
        break;

    case WM_DESTROY:

        PostQuitMessage(0);
        break;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}


// hInstance: This handle refers to the running executable
// hPrevInstance: Not used. See https://blogs.msdn.microsoft.com/oldnewthing/20040615-00/?p=38873
// lpCmdLine: Command line arguments.
// nCmdShow: a flag that says whether the main application window
//           will be minimized, maximized, or shown normally.
//
// Note that it's necessary to use _tWinMain to make it
// so that command line arguments will work, both
// with and without UNICODE / _UNICODE defined.
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    MSG  msg;
    WNDCLASS mainWindowClass = { 0 };

    // You can set the main window name to anything, but
    // typically you should prefix custom window classes
    // with something that makes it unique.
    mainWindowClass.lpszClassName = TEXT("JRH.MainWindow");

    mainWindowClass.hInstance = hInstance;
    mainWindowClass.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    mainWindowClass.lpfnWndProc = WndProc;
    mainWindowClass.hCursor = LoadCursor(0, IDC_ARROW);

    RegisterClass(&mainWindowClass);

    // Notes:
    // - The classname identifies the TYPE of the window. Not a C type.
    //   This is a (TCHAR*) ID that Windows uses internally.
    // - The window name is really just the window text, this is
    //   commonly used for captions, including the title
    //   bar of the window itself.
    // - parentHandle is considered the "owner" of this
    //   window. MessageBoxes can use HWND_MESSAGE to
    //   free them of any window.
    // - menuHandle: hMenu specifies the child-window identifier,
    //               an integer value used by a dialog box
    //               control to notify its parent about events.
    //               The application determines the child-window
    //               identifier; it must be unique for all
    //               child windows with the same parent window.

    //CreateWindow( windowClassName,                windowName,             style,                            xPos,         yPos,       width,              height,            parentHandle,   menuHandle,  instanceHandle, param);
    CreateWindow(   mainWindowClass.lpszClassName,  TEXT("Main Window"),    WS_OVERLAPPEDWINDOW | WS_VISIBLE, mainWindow.x, mainWindow.y, mainWindow.width, mainWindow.height, NULL,           0,           hInstance,      NULL);

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

    return (int)msg.wParam;
}

// This code is based roughly on tutorial code present at  http://zetcode.com/gui/winapi/

дальнейшее чтение

Встроенный набор оконных классов довольно ограничен, поэтому вам может быть любопытно, как вы можете определить свои собственные классы окон («Controls») с помощью Windows API, см. Статьи ниже:

NOTE: I originally intended this post to cover the creation of dialogs programmatically. Due to a mistake on my part I didn't realize that you can't just "show" a window as a dialog. Unfortunately I wasn't able to get the setup mentioned by Raymond Chen working.

Грамматические придирки: я не знаю, правильно ли говорить «В winapi» или «In winapi». Также это, вероятно, само собой разумеется, но будьте осторожны с программированием на winapi, что бы вы ни делали, не смешивайте версии функций unicode и non-unicode. Вы можете просто использовать все функции W-формы и забыть о (винтажной) совместимости с ANSI. Я был слишком большим задротом, чтобы не упомянуть совместимость с ANSI. Также функции winapi, как правило, не очень терпимы к неправильным типам дескрипторов, передаваемых в них, например, не передают hInstance во что-то, ожидающее HWND.

jrh 28.12.2017 18:14
Из-за недопонимания с моей стороны, this answer creates a окно, not a диалог; it's not as easy to just "show" a window as I thought. I'm working on an edit to fix this.
jrh 28.12.2017 20:06

Обновление: возможно отобразить диалоговое окно в виде окна (например, пример HEXCALC Чарльза Петцольда в Программе Windows, 5-е издание, глава 11), но это не то же самое, что отображение диалогового окна, и, опять же, оно, к сожалению, имеет некоторые серьезные ограничения.

jrh 31.12.2017 23:32

Другие вопросы по теме