Windows api c библиотека

Writing a Windows GUI Application with Windows API was avoided by many programmers due to its complexity, but is it really something to be afraid of?

In the WinAPI you essentially need the WinMain(…), CALLBACK(…) and WndProc(…) functions to create and present a GUI. You will also need a Window Class (WNDCLASS) and the CreateWindow(…) function. Content is provided in the form of Text, Cotrol Elements, Menu and MessageBoxes.

All in all it’s not that bad, mostly some typing if you know what you need. In order for you to gain this knowledge, I have summarized the essential elements along with examples in this article.

WinAPI Controls

Our Example WindowsAPI Application

Creating a WinAPI Window in Modern C

The following article shows the creation of a basic WinAPI application with a GUI that resembles the most common applications used in the MS Windows environment.

Basics

In another article I showed how to create a very basic WinAPI GUI Application and we will reuse most of this code here. It is a lot of code to make very little happen, but it is the basis of our application and shows a MS Windows window with a text.

We will need two functions, WinMain(…) as a replacement for main() and WndProc(…) as a function that processes the MS Windows event loop. In order to use the types and functions of the WinAPI we include windows.h.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int cmdShow)
{ 
   ... /* code here */
}

Creating and Initializing the Window with the WinAPI

Again, the details are shown in another Article about GUI in C, so we will cover it briefly here. Instead of our beloved main(…) function we now have to use WinMain(…) together with some parameters that are necessary for Windows.

Next we create a windowclass and register it. Then we create the window itself (with a title) and show it to the screen. An EventLoop that collects operating system messages is also started.

...
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int cmdShow)
{    
    static TCHAR appName[] = TEXT("Hello WindowsAPI");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = appName;

    if(!RegisterClass(&wndclass)) {
        MessageBox(NULL, TEXT("Could not register Window Class!"), appName, MB_ICONERROR);
    }

    hwnd = CreateWindow(appName, TEXT("The Hello Windows Program"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    
    ShowWindow(hwnd, cmdShow);
    UpdateWindow(hwnd);

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

    return msg.wParam;
}
...

Processing MS Windows Events with the WinAPI

The CALLBACK(…) function is called when the event loop fires events from the operating system. For each event you can react with whatever is necessary and/or appropriate.

The WM_DESTROY event announces the upcoming shutdown of the application, WM_PAINT is fired whenever a repaint of the window is necessary (e.g. because you moved it). WM_CREATE is fired on creation of the window and we will do something here later.

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch (message)
    {
    case WM_CREATE:
        /* Add Initialization Code */
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &rect);
        DrawText(hdc, TEXT("This is your first Windows Window!"),-1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }

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

Our Hello World Window

If you compile what we have coded so far, you should get a window with a title, a white background and a black text in the center of the screen.

Compiler Notice: You have to link gdi32 to link the Windows functionality into your code, otherwise you will get a linker error. In gcc you can do it with the -lgdi32 option (e.g. gcc main.c -o winapi -lgdi32)

WinAPI Hello World

Our first Windows API Window – Hello World!

Most windows have a menu bar at the top where the user can find some actions that he is able to perform with the application. Let’s create a menu bar for our example application.

On the top we add the declaration for an add_menu(…) function which takes the window reference as a parameter.

In the function itself we create four menu items, two of them are main menu items and two are sub menu items. Last we add the created menu to the main window with help of the hWnd Reference.

The & character underlines the following letter and enables access via the ALT Key+Letter (e.g. ALT+F for &File). Make sure that there are no duplicates (like &File and &Format) in the same menu level.

#include <windows.h>
...
void add_menu_bar(HWND);

int WINAPI WinMain(HINSTANCE hInstance...
...
void add_menu_bar(HWND hwnd)
{
    HMENU hFileMenu = CreateMenu();
    HMENU hHelpMenu = CreateMenu();
    HMENU hQuitItem = CreateMenu();
    HMENU hAboutItem = CreateMenu();

    AppendMenu(hQuitItem, MF_STRING, ITEM_QUIT, "&Quit");
    AppendMenuW(hFileMenu, MF_POPUP, (UINT_PTR)hQuitItem, L"&File");
    
    AppendMenu(hAboutItem, MF_STRING, ITEM_ABOUT, "&About");
    AppendMenuW(hFileMenu, MF_POPUP, (UINT_PTR)hAboutItem, L"&Help");

    SetMenu(hwnd, hFileMenu);
}

Now we have to call the add_menu() function in WndProc(…), namely in the WM_CREATE case so the menu is created when the GUI is created.

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch (message)
    {
    case WM_CREATE:
        add_menu_bar(hwnd);
        return 0;
    case WM_PAINT:
      ...

    ...
}
...

After compiling and running the application we now have a menu bar with two items which itself contain one item each.

WinAPI Menu

Now we have a nice Menu Bar

Closing the Application on Menu Item Click

Right now our Menu doesn’t do anything. We want to make the Quit item close the application. When such an item is clicked, WndProc will receive a WM_COMMAND event which we can handle appropriatly.

We extend he switch(message) block and check if ITEM_QUIT was clicked. If yes we will send the PostQuitMessage() in order to close the application.

Now the application should close if you click on File->Quit.

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    ...

    switch (message)
    {
    case WM_CREATE:
    ...
    case WM_PAINT:
    ...
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case ITEM_QUIT:
            PostQuitMessage(0);
            break;
        default:
            /* not handled*/
            return 0;
        }
        break;
    case WM_DESTROY:
    ...
    default:
    ...
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}
...
WinAPI Quit

The Menu Item File->Quit will close the Application

Control Elements

A blank window with some text is mostly not what we want so lets add some control elements. First we will add an EditBox where the user can type some text. We will also add a Buttons that the user can click but will do nothing for now.

The call to the creation again happens in the WndProc(..) function where we already created the menu. We declare add_controls(…) and call it in the WM_CREATE case. We also need two new IDs for our control elements.

We also declare the EditBox Element globally so that we can use it later when we implement Message Boxes.

#include <windows.h>

#define ITEM_QUIT 1
#define ITEM_ABOUT 2
#define EDIT_BOX_ID 3
#define SEND_BUTTON_ID 4

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void add_menu_bar(HWND);
void add_controls(HWND);

HWND hEditBox;

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  ....
    switch (message)
    {
    case WM_CREATE:
        add_menu_bar(hwnd);
        add_controls(hwnd);
        return 0;
    case WM_PAINT:
...

The function itself is rather simple; we create an EditBox and a Button with the help of the CreateWindowL(…) function where the first parameter indicates the type of control that we want. The Button also gets a Text which serves as caption. The four numbers are x and y position as well as width and height of the control.

WS_CHILD makes the Element a child of the main window and WS_VISIBLE makes it visible. We also need an ID for each control in order to identify it later (e.g. for button click events, etc.).

void add_controls(HWND hwnd)
{
    hEditBox = CreateWindowW(L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 30, 50, 150, 25, hwnd, (HMENU)EDIT_BOX_ID, NULL, NULL);
    HWND hButtonSend = CreateWindowW(L"Button", L"Send Text", WS_CHILD | WS_VISIBLE, 30, 80, 80, 30, hwnd, (HMENU)SEND_BUTTON_ID, NULL, NULL);
}

When we compile and run the application now we get the following result:

WinAPI Controls

A textbox and a button show up in our window

Message Boxes

A MessageBox is also a common control element in a GUI. It is an easy way to show important information to the user or to get an answer for a request.

We will implement a simple MessageBox that will show up when we click on our About menu item in the Help section of the Menu Bar. We created this a few paragraphs ago.

In the WM_COMMAND section of the WndProc(…) function we will add another case for the About item. Instead of closing the application we now call a Windows function called MessageBox(…) which will show us a MessageBox with a Title, Text and Button.

I will only show you the part of WndProc(..) that has changed

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
  case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case ITEM_QUIT:
            PostQuitMessage(0);
            break;
        case ITEM_ABOUT:
            MessageBox(NULL, TEXT("Thank you for reading this far."), TEXT("A Message Box!"), 0);
            break;
        default:
            /* not handled*/
            return 0;
        }
        break;
...
}

If we now navigate to the menu item Help->About and click the MessageBox will appear.

Remember the Button we added in the Control Elements paragraph? We now give it something to do. When the user clicks the button we will show the text that he entered in the EditBox inside a MessageBox.

We have to visit the WndProc(…) function one last time. In the WM_COMMAND section we check if the event comes from our button (SEND_BUTTON_ID) and then check if the event was a button click (BN_CLICKED). Then we create a buffer that will hold our text and copy the contents of the EditBox into this buffer. Finally we show a MessageBox with a title and our buffer as text.

Now you see why the EditBox was declared globally – we need to access it in two different functions.

...
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    ...

    switch (message)
    {
    case WM_CREATE:
        ....
    case WM_PAINT:
        ...
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case ITEM_QUIT:
            PostQuitMessage(0);
            break;
        case ITEM_ABOUT:
            MessageBox(NULL, TEXT("Thank you for reading this far."), TEXT("A Message Box!"), 0);
            break;
        case SEND_BUTTON_ID:
            if(HIWORD(wParam) == BN_CLICKED)
            {
                TCHAR buffer[1024];
                GetWindowText(hEditBox, buffer, 1024);
                MessageBox(NULL, buffer, TEXT("Sending Text:"), 0);
            }
            break;
        default:
            /* not handled*/
            return 0;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }

    ...
}

Finally when we click on the button the entered text will appear inside a MessageBox.

WinAPI MessageBox

The MessageBox appears and contains the text that we typed into the textedit control

Summary

This was a brief introduction to GUI Programming with the WinAPI in C. From here you can write your own programs and begin to add more control elements and functionality.

If you want to try out an alternative GUI that you can program in C you can e.g. read about SDL (Simple DirectMedia Layer) in this article. In contrast to WinAPI, SDL is portable.

WinMain entry point

Here is the prototype of WinMain().

int CALLBACK WinMain(
  _In_  HINSTANCE hInstance,
  _In_  HINSTANCE hPrevInstance,
  _In_  LPSTR lpCmdLine,
  _In_  int nCmdShow
);

Familiar?
WinMain() is the conventional name used for a graphical Windows-based application entry point.

The parameter definition from msdn:

  • hInstance [in]
    Type: HINSTANCE
    A handle to the current instance of the application.
  • hPrevInstance [in]
    Type: HINSTANCE
    A handle to the previous instance of the application. This parameter is always NULL. If you need to detect whether another instance already exists, create a uniquely named mutex using the CreateMutex function. CreateMutex will succeed even if the mutex already exists, but the function will return ERROR_ALREADY_EXISTS. This indicates that another instance of your application exists, because it created the mutex first. However, a malicious user can create this mutex before you do and prevent your application from starting. To prevent this situation, create a randomly named mutex and store the name so that it can only be obtained by an authorized user. Alternatively, you can use a file for this purpose. To limit your application to one instance per user, create a locked file in the user’s profile directory.
  • lpCmdLine [in]
    Type: LPSTR
    The command line for the application, excluding the program name. To retrieve the entire command line, use the GetCommandLine function.
  • nCmdShow [in]
    Type: int
    Controls how the window is to be shown.

Windows Data Type

The data types supported by Windows are used to define function return values, function and message parameters, and structure members. They define the size and meaning of these elements.

The following table contains the following types: character, integer, Boolean, pointer, and handle. The character, integer, and Boolean types are common to most C compilers. Most of the pointer-type names begin with a prefix of P or LP. Handles refer to a resource that has been loaded into memory.

More complete data types are MSDN — Windows Data Types.

Data Type Description
BYTE A byte (8 bits).
This type is declared in WinDef.h as follows:
typedef unsigned char BYTE;
DWORD A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
This type is declared in IntSafe.h as follows:
typedef unsigned long DWORD;
HINSTANCE A handle to an instance. This is the base address of the module in memory.
HMODULE and HINSTANCE are the same.
This type is declared in WinDef.h as follows:
typedef HANDLE HINSTANCE;
LPSTR A pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
This type is declared in WinNT.h as follows:
typedef __nullterminated CONST CHAR *LPCSTR;
LPVOID A pointer to any type.
This type is declared in WinDef.h as follows:
typedef void *LPVOID;
TCAHR A WCHAR if UNICODE is defined, a CHAR otherwise.
This type is declared in WinNT.h as follows:

#ifdef UNICODE
typedef WCHAR TCHAR;
#else
typedef char TCHAR;
#endif
WCHAR A 16-bit Unicode character.
This type is declared in WinNT.h as follows:
typedef wchar_t WCHAR;
WINAPI The calling convention for system functions.
This type is declared in WinDef.h as follows:
#define WINAPI __stdcall
CALLBACK, WINAPI, and APIENTRY are all used to define functions with the __stdcall calling convention. Most functions in the Windows API are declared using WINAPI. We may wish to use CALLBACK for the callback functions that we implement to help identify the function as a callback function.
WORD A 16-bit unsigned integer. The range is 0 through 65535 decimal.
This type is declared in WinDef.h as follows:
typedef unsigned short WORD;

C / Win32 API

Developing software on Windows involved using the C in conjunction with the Windows API. While there have been numerous successful applications using the raw API has been really painful. First of all, the C language is not object oriented but structural. Also, programmers need to be very careful for manual memory management, and they have to face the tricky pointer arithmetic.

When we combine the numerous global functions and data types defined by the Win32 API to an already formidable language, it’s not a surprise that there are many applications still struggling with so many bugs.

Title Description
MFC Desktop Applications Microsoft Foundation Classes provide a thin object-oriented wrapper over Win32 to enable rapid development of GUI applications in C++.
ATL COM Desktop Components ATL provides class templates and other use constructs to simplify creation of COM objects in C++.
ATL/MFC Shared Classes References for CStringT Class and other classes that are shared by MFC and ATL.
Visual C++ Parent topic for all C++ content in the MSDN library.

C++/Win32 MFC

Using C++ itself is a huge improvement over raw C/API programming. In essence, C++ is a C with a class. C++ can be considered as an object-oriented layer on top of C. However, several C features that bother programmers are still there.

MFC (Microsoft Foundation Class) provides the developer with a set of C++ classes that facilitates the creation of Win32 applications. Actually, MFC provides a thin object-oriented wrapper over the raw Win32 API to enable rapid development of GUI applications in C++. It provides a set of classes, magic macros, and numerous code-generation tools.

But we should admit that even with the help from the MFC framework, still programming in C++ remains difficult and error-prone due to the legacy from C.

COM (Component Object Model)

The COM was Microsoft’s previous application framework. COM’s idea was: «If we build our classes in accordance with the rules of COM, we end up with a block of reusable binary code

The essence of a binary COM server is that it can be accessed in a language-independent manner so that C++ programmer can build COM classes that can be used by VB6. But it turns out, the language independence is limited. For example, there is no way to derive a COM class using an existing COM class since COM does not support inheritance. So, we should take cumbersome route to reuse COM class types.

Another benefit of COM is that it is location-transparent. In other words, using constructs such as application identifiers (AppID), stubs, proxies, and the COM runtime environment, programmers can avoid the need to work with raw sockets, RPC calls, and other low-level details.

To some people, even though COM may be considered as a very successful object model, it is really complex under the hood. The ATL (Active Template Library) came out to help simplify creation of COM objects by providing another set of C++ classes, templates, and macros.

However, language support alone is not enough to hide the complexity of COM. For example, even when we choose a relatively simple COM-aware language such as VB6, we’re still forced to contend with fragile registration entries and numerous deployment-related issues regarding dlls.

  • Стартовая функция WinMain
  • Программа на Си для Windows, как и для любой другой платформы, должна
    обязательно содержать некоторую «стартовую» функцию, которой передается
    управление при запуске программы. Вообще говоря, имя такой «стартовой»
    функции может различаться в различных компиляторах, но исторически
    сложилось так (а, кроме того, имеются еще и стандарты ANSI и ISO,
    к которым, правда, производители коммерческих компиляторов типа Microsoft
    и Borland/Inprise относятся без особого трепета), что такой функцией является:

    int main()

    У этой функции может быть до трех параметров:

    int main(int argc, char *argv[], char *env[])
    • argc — количество параметров в командной строке (включая имя программы),
    • argv — массив строк-параметров (argv[0] — имя программы),
    • env — массив строк-переменных окружения.

    Многие компиляторы для Windows «понимают» такую стартовую функцию.
    Однако при этом они создают хотя и 32-битное, но консольное приложение.
    Пример 1 (example1.cpp):

    #include <stdio.h>
    int main() {
     printf("Hello, world!");
     getc(stdin);
     return 0;
    }

    Компилируем:

    bcc32 example1.cpp

    Запускаем:

    При использовании стандартных библиотек (stdio, stdlib и т. п.)
    вам не потребуется никаких лишних телодвижений по сравнению с обычными
    методами написания программ на Си. Если же ваша цель — 32-битное приложение
    с графическим интерфейсом, то черное консольное окошко будет вас раздражать.
    Пример (example2.cpp):

    #include <windows.h>
    int main() {
     MessageBox(NULL,"Hello, World!","Test",MB_OK);
     return 0;
    }

    В общем, чтобы получить нормальное приложение без каких-либо «довесков»
    типа консольного окошка, используется другая стартовая функция:

    int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hpi, LPSTR cmdline, int ss)
    • hInst — дескриптор для данного экземпляра программы,
    • hpi — в Win32 не используется (всегда NULL),
    • cmdline — командная строка,
    • ss — код состояния главного окна.

    Пример (example3.cpp):

    #include <windows.h>
    int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) {
     MessageBox(NULL,"Hello, World!","Test",MB_OK);
     return 0;
    }

    Кроме того, компилятору и компоновщику нужно сообщить о том, что
    вы делаете графическое приложение. Для bcc32 это опция -tW:

    bcc32 -tW example3.cpp

    Если же вы соскучитесь по черному консольному окошку, его можно
    в любой момент создать при помощи вызова Win32 API

    BOOL AllocConsole(void)

  • О типах, о функциях
  • Как известно, в Си есть лишь три базовых типа
    (char, int, float/double)
    и еще несколько их вариаций с модификаторами signed/unsigned, short/long.
    Однако фирме Microsoft зачем-то понадобилось описывать функции Win32 API
    с помощью переопределенных типов:

    typedef unsigned char BYTE;
    typedef unsigned short WORD;
    typedef unsigned int UINT;
    typedef int INT;
    typedef long BOOL;
    #define FALSE 0
    #define TRUE 1
    typedef long LONG;
    typedef unsigned long DWORD;
    typedef void *LPVOID;
    typedef char CHAR;
    typedef CHAR *LPSTR;
    typedef const CHAR *LPCSTR;

    Кроме перечисленных простых типов, практически ни один вызов Win32 API
    не обходится без «штучек» с «ручками» — переменных типа handle («ручка»),
    которые идентифицируют некоторый объект («штучку»). Такие «ручки» принято
    называть дескрипторами. Реально такая переменная представляет собой всего
    лишь указатель на некоторую системную структуру или
    индекс в некоторой системной таблице.

    typedef void *HANDLE;    /* абстрактный дескриптор (например, файла) */
    typedef void *HMODULE;   /* дескриптор модуля */
    typedef void *HINSTANCE; /* дескриптор экземпляра программы */
    typedef void *HKEY;      /* дескриптор ключа в реестре */
    typedef void *HGDIOBJ;   /* дескриптор графического примитива (перо, шрифт, кисть, палитра,...) */
    typedef void *HWND;      /* дескриптор окна */
    typedef void *HMENU;     /* дескриптор меню */
    typedef void *HICON;     /* дескриптор иконки */
    typedef void *HBITMAP;   /* дескриптор картинки */
    typedef void *HFONT;     /* дескриптор шрифта */

    При заполнении различных структур часто требуется указать такую «ручку»
    от какой-нибудь «штучки». Очень часто вместо конкретного дескриптора допустимо
    передавать значение NULL, означающее, что вы еще не обзавелись
    такой «штучкой» или собираетесь использовать «штучку» по умолчанию.

    В стандартных версиях Си для функций используются два варианта соглашения
    о передаче параметров: соглашение языка Си (параметры функции помещаются в стек в порядке
    обратном их описанию, очистку стека производит вызывающая процедура) и
    соглашение языка Паскаль (параметры функции помещаются в стек в (прямом) порядке их
    описания, очистку стека производит вызываемая процедура). Для этих соглашений
    использовались, соответственно, модификаторы cdecl и pascal.
    При описании функций Win32 API используется модификатор WINAPI, а для
    описания пользовательских функций обратного вызова — модификатор CALLBACK.
    Оба этих модификатора являются переопределением специального модификатора _stdcall,
    соответствующего соглашению о передаче параметров, использующегося исключительно
    в Win32 API, — Standard Calling Convention (параметры функции помещаются в стек
    в порядке обратном их описанию, очистку стека производит вызываемая процедура).

  • Окна
  • Окно — это прямоугольная область экрана, в котором приложение отображает информацию и
    получает реакцию от пользователя. Одновременно на экране может отображаться несколько
    окон, в том числе, окон других приложений, однако лишь одно из них может получать
    реакцию от пользователя — активное окно. Пользователь использует клавиатуру, мышь
    и прочие устройства ввода для взаимодействия с приложением, которому принадлежит активное окно.
    Каждое 32-битное приложение создает, по крайней мере, одно окно, называемое главным окном,
    которое обеспечивает пользователя основным интерфейсом взаимодействия с приложением.

    Окно приложения может содержать строку заголовка title bar (1),
    строку меню menu bar (2), системное меню system menu (3),
    кнопку сворачивания окна minimize box (4), кнопку разворачивания окна maximize box (5),
    рамку изменения размеров sizing border (6), клиентскую область client area (7),
    горизонтальную и вертикальную полосы прокрутки scroll bars (8):

    Меню, строка заголовка с системными кнопками, системное меню, рамка изменения размеров
    и полосы прокрутки относятся к области окна, называемой неклиентской областью (non-client area).
    С неклиентской областью Windows справляется сама, а вот за содержимое и обслуживание
    клиентской области отвечает приложение.

    Кроме главного окна, приложение может использовать еще и другие типы окон:
    управляющие элементы (controls), диалоговые окна (dialog boxes),
    окна-сообщения (message boxes). Управляющий элемент — окно, непосредственно
    обеспечивающее тот или иной способ ввода информации пользователем. К управляющим
    элементам относятся: кнопки, поля ввода, списки, полосы прокрутки и т.п.
    Управляющие элементы обычно не болтаются сами по себе, а проживают в каком-либо
    диалоговом окне.
    Диалоговое окно — это временное окно, напичканное управляющими элементами,
    обычно использующееся для получения дополнительной информации от пользователя.
    Диалоговые окна бывают модальные (modal) и немодальные (modeless).
    Модальное диалоговое окно требует, чтобы пользователь обязательно ввел обозначенную
    в окне информацию и закрыл окно прежде, чем приложение продолжит работу.
    Немодальное диалоговое окно позволяет пользователю, не закрывая диалогового окна,
    переключаться на другие окна этого приложения.
    Окно-сообщение — это диалоговое окно предопределенного системой формата,
    предназначенное для вывода небольшого текстового сообщения с одной или несколькими
    кнопками. Пример такого окна показан в Примере 3.

    В отличие от традиционного программирования на основе линейных алгоритмов,
    программы для Windows строятся по принципам событийно-управляемого программирования
    (event-driven programming) — стиля программирования, при котором поведение
    компонента системы определяется набором возможных внешних событий и ответных реакций
    компонента на них. Такими компонентами в Windows являются окна. С каждым окном
    в Windows связана определенная функция обработки событий. События для окон
    называются сообщениями. Сообщение относится к тому или иному типу,
    идентифицируемому определенным кодом (32-битным целым числом), и сопровождается
    парой 32-битных параметров (WPARAM и LPARAM),
    интерпретация которых зависит от типа сообщения. В заголовочном файле windows.h
    для кодов сообщений определены константы с интуитивно понятными именами:

    #define WM_CREATE   0x0001  /* сообщение о создании окна */
    #define WM_DESTROY  0x0002  /* сообщение об уничтожении окна */
    #define WM_SIZE     0x0005  /* сообщение об изменении размеров окна */
    #define WM_COMMAND  0x0111  /* сообщение от команды меню или управляющего элемента */

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

    Для стандартных управляющих элементов (библиотека Common Controls Library — COMCTL32.DLL)
    в Windows имеются предопределенные обработчики событий, которые при наступлении
    интересных событий сообщают всяческую полезную информацию окну, содержащему этот
    управляющий элемент. Стандартная библиотека Common Dialog Box Library
    (COMDLG32.DLL) содержит несколько готовых весьма полезных диалоговых окон с
    обработчиками: диалоги выбора файла, настроек печати, выбора шрифта, выбора цвета и др.
    Кроме того, любая среда разработки (VisualBasic, Delphi, VisualC++ и т.п.) навязывает
    разработчику дополнительный набор готовых управляющих элементов и диалогов —
    иногда достаточно удобных, иногда не очень.

  • Структура программы
  • Программа для Win32 обычно состоит из следующих блоков:

    #include <windows.h>
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR cmdline,int ss) {
    

    /* Блок инициализации: создание класса главного окна, создание главного окна, загрузка ресурсов и т.п. */

    /* Цикл обработки событий: */ MSG msg; while (GetMessage(&msg,(HWND)NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); }

    return msg.wParam; } LRESULT CALLBACK MainWinProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {

    /* Обработка сообщений главного окна */ switch (msg) { case WM_CREATE: /* ... */ return 0; case WM_COMMAND: /* ... */ return 0; case WM_DESTROY: /* ... */ PostQuitMessage(0); return 0; /* ... */ }

    return DefWindowProc(hw,msg,wp,lp); }

    Каждое окно принадлежит определенному классу окон. Окна одного класса
    имеют схожий вид, обслуживаются общей процедурой обработки событий,
    имеют одинаковые иконки и меню. Обычно каждое приложение создает
    для главного окна программы свой класс. Если приложению требуются
    дополнительные нестандартные окна, оно регистрирует другие классы.
    Стандартные диалоги и управляющие элементы принадлежат к предопределенным классам окон,
    для них не надо регистрировать новые классы. Чтобы определить новый класс окон,
    надо заполнить структуру WNDCLASS, содержащую следующие поля:

    • UINT style — стиль (поведение) класса окон,
    • WNDPROC lpfnWndProc — процедура обработки событий окна,
    • int cbClsExtra — размер дополнительной памяти в системной структуре класса для данных пользователя,
    • int cbWndExtra — размер дополнительной памяти в системной структуре окна для данных пользователя,
    • HINSTANCE hInstance — дескриптор модуля (экземпляра программы), в котором реализована процедура обработки,
    • HICON hIcon — дескриптор иконки окна,
    • HCURSOR hCursor — дескриптор курсора мыши для окна,
    • HBRUSH hbrBackground — дескриптор «кисточки» для закрашивания фона окна,
    • LPCSTR lpszMenuName — имя ресурса, содержащего меню окна,
    • LPCSTR lpszClassName — имя класса.

    Класс регистрируется при помощи функции:

    WORD WINAPI RegisterClass(const WNDCLASS *lpwc)

    При успешном завершении функция возвращает целочисленный код,
    соответствующий строке-имени класса в общесистемной таблице строк
    (такой код называется атомом). При ошибке возвращается 0.

    Для создания окна вызывается функция:

    HWND WINAPI CreateWindow(
      LPCSTR lpClassName,  /* имя класса */
      LPCSTR lpWindowName, /* имя окна (заголовок) */
      DWORD dwStyle,       /* стиль (поведение) окна */
      int x,               /* горизонтальная позиция окна на экране */
      int y,               /* вертикальная позиция окна на экране */
      int nWidth,          /* ширина окна */
      int nHeight,         /* высота окна */
      HWND hWndParent,     /* дескриптор родительского окна */
      HMENU hMenu,         /* дескриптор меню */
      HANDLE hInstance,    /* дескриптор экземпляра программы */
      LPVOID lpParam       /* указатель на какую-нибудь ерунду */
    )

    Вместо параметров x, y, nWindth, nHeight допустимо передавать
    константу CW_USEDEFAULT, позволяющую операционной системе задать эти числа
    по ее усмотрению.

    Интерпретация кода стиля определяется классом окна.
    Стиль определяет не только оформление окна, но и его поведение.
    Общие для всех классов константы стилей
    (при необходимости объединяются операцией побитовое ИЛИ):

    • WS_DISABLED — при создании окно заблокировано (не может получать реакцию от пользователя);
    • WS_VISIBLE — при создании окно сразу же отображается (не надо вызывать ShowWindow);
    • WS_CAPTION — у окна есть строка заголовка;
    • WS_SYSMENU — у окна есть системное меню;
    • WS_MAXIMIZEBOX — у окна есть кнопка разворачивания;
    • WS_MINIMIZEBOX — у окна есть кнопка сворачивания;
    • WS_SIZEBOX или WS_THICKFRAME — у окна есть рамка изменения размеров;
    • WS_BORDER — у окна есть рамка (не подразумевает изменение размеров);
    • WS_HSCROLL или WS_VSCROLL — у окна есть горизонтальная или вертикальная прокрутка;
    • WS_OVERLAPPED или WS_TILED — «перекрываемое» окно — обычное окно с рамкой и строкой заголовка;
    • WS_POPUP — «всплывающее» окно;
    • WS_OVERLAPPEDWINDOW — «перекрываемое» окно с системным меню, кнопками сворачивания/разворачивания,
      рамкой изменения размеров, короче, типичный стиль для главного окна приложения.

    Во время выполнения функции CreateWindow процедуре обработки событий
    окна посылается сообщение WM_CREATE. При успешном выполнении функции
    возвращается дескриптор созданного окна, при неудаче — NULL.

    После создания окна неплохо бы сделать его видимым (отобразить), если только
    оно не создано со стилем WS_VISIBLE:

    BOOL WINAPI ShowWindow(HWND hw, int ss)

    Второй параметр этой функции — код состояния отображения окна. В качестве этого кода
    можно взять значение четвертого параметра, с которым была запущена функция WinMain.
    Другие возможные значения этого параметра:

    • SW_SHOW — отобразить и активировать окно;
    • SW_HIDE — скрыть окно;
    • SW_MAXIMIZE — развернуть окно на весь экран;
    • SW_RESTORE — активировать окно и отобразить его в размерах по умолчанию;
    • SW_MINIMIZE — свернуть окно.

    Если перед вызовом этой функции окно было видимым, функция возвращает TRUE,
    если же окно было скрыто — FALSE.

    Если клиентская область главного окна приложения содержит объекты, прорисовываемые
    по сообщению WM_PAINT, имеет смысл прорисовать эти объекты сразу после
    отображения главного окна на экране. Функция UpdateWindow непосредственно
    вызывает процедуру обработки событий указанного окна с сообщением WM_PAINT
    (минуя очередь сообщений приложения):

    BOOL WINAPI UpdateWindow(HWND hw)

    Windows использует два способа доставки сообщений процедуре обработки событий окна:

    • непосредственный вызов процедуры обработки событий (внеочередные или
      неоткладываемые сообщенияnonqueued messages);
    • помещение сообщения в связанный с данным приложением буфер типа FIFO,
      называемый очередью сообщенийmessage queue
      (откладываемые сообщенияqueued messages).

    К внеочередным сообщениям относятся те сообщения, которые непосредственно
    влияют на окно, например, сообщение активации окна WM_ACTIVATE и т.п.
    Кроме того, вне очереди сообщений обрабатываются сообщения, сгенерированные
    различными вызовами Win32 API, такими как SetWindowPos,
    UpdateWindow, SendMessage,
    SendDlgItemMessage

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

    BOOL WINAPI GetMessage(
      MSG *lpmsg,          /* сюда попадает сообщение со всякими параметрами */
      HWND hw,             /* извлекать только сообщения для указанного окна (NULL - все) */
      UINT wMsgFilterMin,  /* фильтр сообщений (нам не надо - ставим 0) */
      UINT wMsgFilterMax   /* фильтр сообщений (нам не надо - ставим 0) */
    )

    Эта функция возвращает FALSE, если получено сообщение WM_QUIT,
    и TRUE в противном случае. Очевидно, что условием продолжения
    цикла обработки событий является результат этой функции. Если приложение хочет
    завершить свою работу, оно посылает само себе сообщение WM_QUIT
    при помощи функции

    void WINAPI PostQuitMessage(int nExitCode)

    Ее параметр — статус выхода приложения. Обычно эта функция вызывается в ответ на
    сообщение об уничтожении окна WM_DESTROY.

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

    BOOL WINAPI TranslateMessage(const MSG *lpmsg)
    LONG WINAPI DispatchMessage(const MSG *lpmsg)

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

    Процедура обработки сообщений окна должна быть объявлена по следующему прототипу:

    LRESULT CALLBACK WindowProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp)

    Значения параметров: hw — дескриптор окна, которому предназначено сообщение,
    msg — код сообщения, wp и lp — 32-битные параметры
    сообщения, интерпретация которых зависит от кода сообщения. Зачастую старший/младший
    байт или старшее/младшее слово параметров сообщения несут независимый смысл, тогда
    удобно использовать определенные в windows.h макросы:

    #define LOBYTE(w)   ((BYTE) (w))
    #define HIBYTE(w)   ((BYTE) (((WORD) (w) >> 8) & 0xFF))
    #define LOWORD(l)   ((WORD) (l))
    #define HIWORD(l)   ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))

    Например, сообщение WM_COMMAND посылается окну в трех случаях:

    1. пользователь выбрал какую-либо команду меню;
    2. пользователь нажал «горячую» клавишу (accelerator);
    3. в дочернем окне произошло определенное событие.

    При этом параметры сообщения интерпретируются следующим образом.
    Старшее слово параметра WPARAM содержит: 0 в первом случае, 1 во втором случае
    и код события в третьем случае. Младшее слово WPARAM содержит
    целочисленный идентификатор пункта меню, «горячей» клавиши или дочернего управляющего
    элемента. Параметр LPARAM в первых двух случаях содержит NULL,
    а в третьем случае — дескриптор окна управляющего элемента.

    Процедура обработки событий должна вернуть определенное 32-битное значение,
    интерпретация которого также зависит от типа сообщения. В большинстве случаев,
    если сообщение успешно обработано, процедура возвращает значение 0.

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

    LRESULT WINAPI DefWindowProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)

    Все описанное в данном параграфе суммируется в примере 4 (example4.cpp):

    #include <windows.h>
    
    LRESULT CALLBACK MainWinProc(HWND,UINT,WPARAM,LPARAM);
    #define ID_MYBUTTON 1    /* идентификатор для кнопочки внутри главного окна */
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int ss) {
     /* создаем и регистрируем класс главного окна */
     WNDCLASS wc;
     wc.style=0;
     wc.lpfnWndProc=MainWinProc;
     wc.cbClsExtra=wc.cbWndExtra=0;
     wc.hInstance=hInst;
     wc.hIcon=NULL;
     wc.hCursor=NULL;
     wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
     wc.lpszMenuName=NULL;
     wc.lpszClassName="Example 4 MainWnd Class";
     if (!RegisterClass(&wc)) return FALSE;
    
     /* создаем главное окно и отображаем его */
     HWND hMainWnd=CreateWindow("Example 4 MainWnd Class","EXAMPLE 4",WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL);
     if (!hMainWnd) return FALSE;
     ShowWindow(hMainWnd,ss);
     UpdateWindow(hMainWnd);
    
     MSG msg; /* цикл обработки событий */
     while (GetMessage(&msg,NULL,0,0)) {
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
     return msg.wParam; 
    }
    
    /* процедура обработки сообщений для главного окна */
    LRESULT CALLBACK MainWinProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
     switch (msg) {
      case WM_CREATE:
       /* при создании окна внедряем в него кнопочку */
       CreateWindow("button","My button",WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE,
        5,5,100,20,hw,(HMENU)ID_MYBUTTON,NULL,NULL);
       /* стиль WS_CHILD означает, что это дочернее окно и для него
        вместо дескриптора меню будет передан целочисленный идентификатор,
        который будет использоваться дочерним окном для оповещения 
        родительского окна через WM_COMMAND */
       return 0;
      case WM_COMMAND:
       /* нажата наша кнопочка? */
       if ((HIWORD(wp)==0) && (LOWORD(wp)==ID_MYBUTTON)) 
        MessageBox(hw,"You pressed my button","MessageBox",MB_OK|MB_ICONWARNING);
       return 0;
      case WM_DESTROY:
       /* пользователь закрыл окно, программа может завершаться */
       PostQuitMessage(0);
       return 0;
     }
     return DefWindowProc(hw,msg,wp,lp);
    }

    Приведенный пример создает окно с кнопкой «My button», при нажатии
    на которую вылезает окно-сообщение:

  • Ресурсы
  • Ресурсы — это бинарные данные, добавляемые в исполняемый файл
    при компоновке программы. К стандартным ресурсам относятся: иконки,
    курсоры, меню, диалоги, растровые изображения (BMP), векторные изображения (EMF),
    шрифты, таблицы горячих клавиш, таблицы строк, информация о версии программы или модуля.
    В процессе разработки программы ресурсы описывают в отдельном текстовом файле —
    файле описания ресурсов (расширение .rc), — а затем при помощи компилятора
    ресурсов переводят в бинарный вид и добавляют в исполняемый файл на этапе компоновки
    исполняемого файла. Использование ресурсов значительно облегчает работу программиста
    по визуализации графических примитивов интерфейса программы.

    Файл описания ресурсов состоит из операторов, объединяемых в блоки.
    Один оператор занимает одну строку файла. Допускается использовать
    комментарии, определяемые так же, как в программе на языке Си.
    Файл описания ресурсов перед компиляцией так же обрабатывается препроцессором,
    поэтому в нем можно использовать директивы препроцессора (#include,
    #define, …) и макроопределения. В сложных «блочных»
    описаниях ресурсов вместо ключевых слов BEGIN и END
    можно использовать { и }, соответственно.

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

    nameID RESOURCETYPE [load-option] [mem-option] filename

    Здесь nameID — численный или строковой идентификатор;
    RESOURCETYPE — ключевое слово, обозначающее тип ресурса:
    ICON, BITMAP или
    CURSOR;
    load-option и mem-option — всякие неинтересные
    в данный момент опции, которые можно спокойно пропустить;
    filename — имя файла, содержащее соответствующий ресурс.

    Примеры:

    disk1   BITMAP "disk.bmp"
    12      ICON   "myicon.ico"

    Эти ресурсы можно внедрить в виде шестнадцатеричных кодов
    непосредственно в файл ресурсов:

    nameID RESOURCETYPE
    BEGIN
     hex data
    END

    Пример:

    FltBmp BITMAP
    {
     '42 4D A2 00 00 00 00 00 00 00 3E 00 00 00 28 00'
     '00 00 19 00 00 00 19 00 00 00 01 00 01 00 00 00'
     '00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00'
     '00 00 00 00 00 00 00 00 00 00 FF FF FF 00 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 FF FF FF 80 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 C0 FF 81 80 FE FF'
     'BF 80 FE FF BF 80 FE FF BF 80 FE FF BF 80 FE FF'
     'BF 80 FE FF BF 80 FE FF BF 80 FE FF BF 80 FE FF'
     'BF 80 FE FF BF 80 FE 00 3F 80 FF FF FF 80 FF FF'
     'FF 80 FF FF FF 80 FF FF FF 80 FF FF FF 80 FF FF'
     'FF 80'
    }

    Следует отметить, что первая иконка в ресурсах
    будет использоваться «Проводником» как иконка
    исполняемого файла.

    Меню описывается следующим образом:

    nameID MENU [load-option] [mem-option]
    BEGIN 
        item-definitions
        ...
    END

    Здесь item-definitions — один из трех операторов:

    MENUITEM text, result [, optionlist]
            /* обычный пункт меню */
    MENUITEM SEPARATOR
            /* строка-сепаратор */
    POPUP text [, optionlist]
    BEGIN   /* подменю */
        item-definitions
        ...
    END

    Параметры операторов имеют следующий смысл:
    text — текст пункта меню или подменю
    (может содержать комбинации \t — табуляция,
    \a — выравнивание по правому краю, &
    следующий символ подчеркивается, обозначает «горячую» клавишу для
    указанного пункта меню);
    result — целочисленный идентификатор пункта меню,
    посылаемый окну-владельцу через сообщение WM_COMMAND
    при выборе этого пункта меню;
    optionlist — необязательный список опций, разделенных
    запятой или пробелом:

    • CHECKED — рядом с пунктом меню отображается галочка,
    • GRAYED — пункт меню неактивен (не может быть выбран)
      и отображается серым цветом и др.

    Доступ к ресурсам, скомпонованным с исполняемым файлом, можно получить
    при помощи следующих функций:

    HICON WINAPI LoadIcon(HINSTANCE hInst, LPCSTR lpIconName)
    HBITMAP WINAPI LoadBitmap(HINSTANCE hInst, LPCSTR lpBitmapName)
    HCURSOR WINAPI LoadCursor(HINSTANCE hInst, LPCSTR lpCursorName)
    HMENU WINAPI LoadMenu(HINSTANCE hInst, LPCSTR lpMenuName)

    Первый параметр этих функций — дескриптор экземпляра программы,
    второй — идентификатор соответствующего ресурса. Если ресурс идентифицируется
    не именем, а числом, то следует использовать макрос, объявленный в windows.h:

    #define MAKEINTRESOURCE(i)  (LPSTR) ((DWORD) ((WORD) (i)))

    Например:

    HMENU hMainMenu=LoadMenu(hInst,MAKEINTRESOURCE(10));

    Для закрепления полученных сведений, давайте добавим к примеру 4
    какую-нибудь иконку и такое меню:

    Для этого создаем файл ресурсов (example4a.rc):

    Ex4_Icon ICON "myicon.ico"
    
    Ex4_Menu MENU
    {
     POPUP "&File"
     {
      MENUITEM "&Open...\tCtrl-O", 2
      MENUITEM "&Save", 3
      MENUITEM "Save &As...", 4
      MENUITEM SEPARATOR
      MENUITEM "&Hex view", 5, CHECKED GRAYED
      MENUITEM "&Exit\tAlt-F4", 6
     }
     POPUP "&Edit"
     {
      MENUITEM "&Copy", 7
      MENUITEM "&Paste", 8
      POPUP "Popup"
      {
       MENUITEM "1", 9
       MENUITEM "2", 10
       MENUITEM "3", 11
      }
      MENUITEM SEPARATOR
      MENUITEM "Search", 12
     }
     POPUP "&Help"
     {
      MENUITEM "&About...\tF1", 13
     }
    }

    Для перевода файла описания ресурсов в бинарный вид используется
    компилятор ресурсов Borland Resource Compiler:

    brcc32 example4a.rc

    В результате получается файл example4a.res, который потребуется в процессе
    компоновки.

    В примере 4 надо изменить строки

     wc.hIcon=NULL;
     wc.lpszMenuName=NULL;

    на

     wc.hIcon=LoadIcon(hInst,"Ex4_Icon");
     wc.lpszMenuName="Ex4_Menu";

    Чтобы программа не была такой скучной, изменим обработчик сообщения WM_COMMAND:

      case WM_COMMAND:
       if (HIWORD(wp)==0) {
        char buf[256];
        switch (LOWORD(wp)) {
         case 6:  /* команда меню Exit */
          PostQuitMessage(0);
         default: /* все остальные команды */
          wsprintf(buf,"Command code: %d",LOWORD(wp));
          MessageBox(hw,buf,"MessageBox",MB_OK|MB_ICONINFORMATION);
        }
       }
       return 0;

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

    Обратите внимание: среди команд меню не используется код 1,
    который отведен кнопке «My button». Это типичная практика назначать
    всем дочерним элементам окна и командам меню разные численные идентификаторы,
    что облегчает обработку сообщения WM_COMMAND.

  • Компиляция и компоновка сложных проектов
  • Теперь компоновка программы будет более сложной, поэтому bcc32
    с этой задачей не справится. В этом примере компилятор будет
    использоваться только для компилирования (запускаем с ключом ):

    bcc32 -c -tW example4a.cpp

    В результате получаем объектный файл example4a.obj.

    Чтобы собрать все части программы вместе, придется запускать
    компоновщик вручную (ilink32 или tlink32).
    В командной строке компоновщика указываются следующие параметры:

    ilink32 [options] objfiles,exefile,mapfile,libfiles,deffile,resfiles
    • options — о допустимых опциях можно узнать, запустив компоновщик без параметров.
      Нам потребуются:

      • -aa — тип приложения «графическое для Win32»
        (другие варианты: -ap — консольное приложение, -ad — драйвер);
      • -Tpe — формат выходного файла «.EXE» (другой вариант: -Tpd — «.DLL»);
      • -L путь — путь для поиска библиотек и объектных файлов
        (обычно: -Lc:\bcc55\lib).
    • objfiles — список объектных файлов, из которых составляется программа,
      разделенных пробелом или знаком «+». Этот список должен начинаться с борландовского
      инициализационного объектного файла: c0w32.obj — для графического приложения под
      Win32 или c0x32.obj — для консольного приложения.
    • exefile — имя исполняемого файла, который получится в результате компоновки.
    • mapfile — имя файла, который после компиляции будет содержать
      карту сегментов вашей программы (оно вам надо? если нет, здесь делаем «пусто»,
      а в опциях указываем -x, чтобы компоновщик не замусоривал рабочий каталог этой фигней).
    • libfiles — список библиотек, в которых надо искать не
      определенные в программе функции, разделенных пробелом или знаком «+».
      Как минимум, надо указать import32.lib, которая содержит код подключения
      к стандартным библиотекам Win32 API: kernel32.dll, user32.dll, gdi32.dll,
      advapi32.dll и др. Если вы используете какие-либо функции стандартных
      библиотек языка Си (stdlib, stdio, …), надо указать еще cw32.lib.
    • deffile — файл параметров модуля (module definition file).
      Это текстовый файл, в котором определяются различные настройки компилируемой
      программы типа: размеры сегментов стека, кода, данных, «заглушка»
      (что будет происходить при попытке запуска программы в DOS) и проч.
      Если не указывать этот файл, компоновщик выберет вполне приличные
      для большинства случаев параметры по умолчанию.
    • resfiles — файлы ресурсов (разделяются пробелом или знаком «+»).

    Компоновщик ilink32 умеет работать в пошаговом (инкрементирующем)
    режиме incremental linking, при этом он создает несколько файлов состояний
    (*.IL?). При последующих попытках компиляции он использует их, так что
    процесс компиляции занимает меньше времени. Чтобы отключить пошаговую
    компиляцию и не замусоривать рабочий каталог этими файлами, следует
    указать опцию -Gn. Например, если при отключенной пошаговой компиляции
    программа компилируется 8 секунд, то первая компиляция в пошаговом режиме
    займет 25 секунд, а все последующие — не более 2 секунд.

    Итак, компонуем модифицированный пример 4:

    ilink32 -aa -Tpe -Lc:\bcc55\lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res

    Если все сделано правильно, первое, что становится сразу заметным —
    у исполняемого файла появилась иконка:

    Эта же иконка отображается в строке заголовка главного окна программы. Под
    строкой заголовка отображается созданное нами меню. При выборе любой команды
    меню появляется окно-сообщение с кодом команды. При выборе команды «Exit»
    программа завершается.

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

    правило: зависимости
    	команды для выполнения

    Сценарий компиляции должен содержать как минимум одно правило.
    Строка с командами обязательно должна начинаться с отступа табуляцией.
    В качестве имени правила обычно выступает имя файла, который получится
    в результате выполнения команд в теле правила.
    Зависимости — необязательный список имен файлов, разделенных пробелами,
    от которых зависит данное правило. Если при вызове make окажется,
    что хотя бы один файл из этого списка новее, чем файл-результат правила,
    то выполняются все команды из этого правила. В качестве зависимостей
    могут указываться имена файлов-названия других правил. Тогда make
    будет выполнять рекурсивную проверку зависимостей. Make не выполняет
    команды из правила, если все файлы-зависимости старее файла-результата.

    Пример:

    example4a.exe: example4a.rc example4a.cpp myicon.ico
    	brcc32 example4a.rc
    	bcc32 -c -tW example4a.cpp
    	ilink32 -Gn -x -aa -Tpe -Lc:\bcc55\lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res

    Если не указывать в качестве файлов-зависимостей example4a.rc и example4a.cpp,
    то make не станет ничего делать, когда файл example4a.exe уже существует.
    Тем не менее, приведенный пример — не совсем удачный сценарий компиляции.
    Если мы изменим только файл ресурсов, make все равно будет перекомпилировать
    исходный текст. Если мы изменим только исходный текст, make будет
    перекомпилировать еще и ресурсы. С учетом этого замечания, более удачным
    будет следующий сценарий:

    example4a.exe: example4a.obj example4a.res
    	ilink32 -Gn -x -aa -Tpe -Lc:\bcc55\lib c0w32.obj+example4a.obj,example4a.exe,,import32.lib+cw32.lib,,example4a.res
    
    example4a.obj: example4a.cpp
    	bcc32 -c -tW example4a.cpp
    
    example4a.res: example4a.rc myicon.ico
    	brcc32 example4a.rc

    Если в командной строке make не указано иное, то make пытается выполнить
    первое правило из сценария. Именно поэтому первым правилом стоит example4a.exe
    — результат, который мы хотим получить после компиляции всего проекта.

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

    make

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

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

    Для создания модального диалога используется функция DialogBox,
    а для создания немодального диалога — CreateDialog:

    int WINAPI DialogBox(HANDLE hInst, LPCSTR template, HWND parent, DLGPROC DlgFunc)
    HWND WINAPI CreateDialog(HANDLE hInst, LPCSTR template, HWND parent, DLGPROC DlgFunc)

    Параметры: hInst — дескриптор экземпляра программы
    (модуля, в котором находится шаблон); template — имя ресурса,
    описывающего диалог; parent — дескриптор родительского окна;
    DlgFunc — диалоговая функция следующего формата:

    BOOL CALLBACK DlgFunc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) 

    Параметры диалоговой функции такие же, как у обычной функции обработки
    событий. Отличие этой функции — она вызывается из предопределенной
    функции обработки событий для диалоговых окон. Она должна вернуть
    значение TRUE, если обработала переданное ей сообщение,
    или FALSE в противном случае. Она ни в коем случае
    не должна сама вызывать DefWindowProc.

    При создании диалогового окна диалоговая процедура получает
    сообщение WM_INITDIALOG. Если в ответ на это сообщение
    процедура возвращает FALSE, диалог не будет создан:
    функция DialogBox вернет значение -1,
    а CreateDialogNULL.

    Модальное диалоговое окно блокирует указанное в качестве родительского
    окно и появляется поверх него (вне зависимости от стиля WS_VISIBLE).
    Приложение закрывает модальное диалоговое окно при помощи функции

    BOOL WINAPI EndDialog(HWND hw, int result)

    Приложение должно вызвать эту функцию из диалоговой процедуры в ответ
    на сообщение от кнопок «OK», «Cancel» или команды «Close» из системного
    меню диалога. Параметр result передается программе как
    результат возврата из функции DialogBox.

    Немодальное диалоговое окно появляется поверх указанного в качестве
    родительского окна, но не блокирует его. Диалоговое окно остается
    поверх родительского окна, даже если оно неактивно. Программа сама
    отвечает за отображение/сокрытие окна (с помощью стиля WS_VISIBLE
    и функции ShowWindow). Сообщения для немодального
    диалогового окна оказываются в основной очереди сообщений программы.
    Чтобы эти сообщения были корректно обработаны, следует включить в цикл
    обработки сообщений вызов функции:

    BOOL WINAPI IsDialogMessage(HWND hwDlg, MSG *lpMsg)

    Если эта функция вернула TRUE, то сообщение обработано
    и его не следует передавать функциям TranslateMessage
    и DispatchMessage.

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

    BOOL WINAPI DestroyWindow(HWND hw)

    Шаблон диалогового окна в ресурсах задается следующим образом:

    nameID DIALOG [load-option] [mem-option] x, y, width, height
        [property-statements]
    BEGIN 
        control-statements
        ...
    END

    В начале блока описания диалога задается: nameID
    целочисленный или строковой идентификатор ресурса, x, y
    координаты диалога на экране (или относительно родительского окна),
    width, height — размер диалога.
    Координаты и размеры диалога и всех элементов внутри
    него задаются в диалоговых единицах (dialog units).
    Одна горизонтальная диалоговая единица соответствует 1/4 средней ширины
    символа в системном шрифте. Одна вертикальная диалоговая единица
    соответствует 1/8 средней высоты символа в системном шрифте.

    После заголовка блока идет ряд необязательных операторов-параметров диалога
    (property-statements) в любом порядке:

    STYLE style               /* стиль диалога */
    CAPTION captiontext       /* заголовок диалога */
    FONT pointsize, typeface  /* шрифт диалога (размер, название) */
    MENU menuname             /* меню диалога */

    В качестве стиля диалога можно применять все перечисленные для обычных окон
    стили. Обычно выбирают: WS_POPUP, WS_SYSMENU, WS_CAPTION,
    а также WS_BORDER для немодального диалога и DS_MODALFRAME
    для модального. Кроме того, можно использовать DS_SETFOREGROUND,
    чтобы при отображении диалога перевести его на передний план, даже если
    его родительское окно неактивно.

    В теле шаблона (control-statements) перечисляются
    составляющие его управляющие элементы. Один из возможных вариантов оператора:

    CONTROL text, id, class, style, x, y, width, height

    Здесь text — текст управляющего элемента (заголовок),
    id — целочисленный идентификатор элемента 0…65535
    (внутри одного диалога идентификаторы всех элементов должны различаться),
    class — имя класса, к которому принадлежит управляющий элемент,
    style — стиль управляющего элемента,
    x, y, width, height
    положение и размер диалогового элемента относительно клиентской
    области диалога в диалоговых единицах.

    Вот пример диалога, содержащего простое статическое текстовое поле и кнопку «OK»:

    Ex4_Dlg DIALOG 50,50,90,40
     STYLE WS_POPUP|WS_CAPTION|DS_MODALFRAME
     CAPTION "MyDlg"
     FONT 10, "Arial"
    {
     CONTROL "", 1, "STATIC", SS_LEFT, 5, 5, 80, 10
     CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 20, 80, 12
    }

    Добавим этот диалог к ресурсам примера 4.
    В текст программы добавим две глобальных переменных:

    char buf[256]=""; /* строка для текстового поля в диалоге */
    HINSTANCE h;      /* дескриптор экземпляра программы */

    Присвоим переменной h значение дескриптора экземпляра программы в самом начале
    функции WinMain. Это значение нам потребуется для вызова функции
    DialogBox.

    Изменим обработчик сообщения WM_COMMAND следующим образом:

      case WM_COMMAND:
        switch (LOWORD(wp)) {
         case 6:  /* команда меню Exit */
          PostQuitMessage(0);
         default: /* все остальные команды */
          wsprintf(buf,"Command code: %d",LOWORD(wp));
          DialogBox(h,"Ex4_Dlg",hw,DlgProc);
        }
        return 0;

    Теперь в текст программы необходимо добавить диалоговую процедуру:

    BOOL CALLBACK DlgProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
     switch (msg) {
      case WM_INITDIALOG: /* сообщение о создании диалога */
       SetDlgItemText(hw,1,buf);
       return TRUE;
      case WM_COMMAND:    /* сообщение от управляющих элементов */
       if (LOWORD(wp)==2) EndDialog(hw,0);
     }
     return FALSE;
    }

    При создании диалога вызывается процедура SetDlgItemText,
    меняющая содержание текстового поля в диалоге (элемент с id=1).
    Для уничтожения диалога используется кнопка «OK», генерирующая
    сообщение WM_COMMAND с id=2.

    Функция DlgProc должна быть определена или описана
    до ссылки на нее в вызове DialogBox.

  • Управляющие элементы
  • Управляющие элементы, как и другие окна, принадлежат тому или
    иному классу окон. Windows предоставляет несколько предопределенных
    классов управляющих элементов. Программа может создавать управляющие
    элементы поштучно при помощи функции CreateWindow
    или оптом, загружая их вместе с шаблоном диалога из своих ресурсов.
    Управляющие элементы — это всегда дочерние окна. Управляющие элементы
    при возникновении некоторых событий, связанных с реакцией пользователя,
    посылают своему родительскому окну сообщения-оповещения
    (notification messages) WM_COMMAND или WM_NOTIFY.

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

    BOOL WINAPI EnableWindow(HWND hw,BOOL bEnable)

    В качестве второго параметра передается флаг TRUE (разблокировать)
    или FALSE (блокировать). Функция возвращает значение TRUE,
    если перед ее вызовом окно было заблокировано. Узнать текущий статус
    блокирования окна можно при помощи функции:

    BOOL WINAPI IsWindowEnabled(HWND hw),

    которая возвращает значение TRUE, если окно разблокировано.

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

    LRESULT WINAPI SendMessage(HWND hw, UINT msg, WPARAM wp, LPARAM lp)

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

    HWND WINAPI GetDlgItem(HWND hDlg, int idDlgItem)

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

    LRESULT WINAPI SendDlgItemMessage(
      HWND hwndDlg,	 /* дескриптор родительского диалога */
      int idControl, /* идентификатор управляющего элемента */
      UINT msg,      /* код сообщения */
      WPARAM wp,     /* параметр сообщения */
      LPARAM lp      /* параметр сообщения */
    )

    Для управляющих элементов внутри диалогов специальный смысл имеют
    стили WS_TABSTOP и WS_GROUP. Если в диалоге
    имеются управляющие элементы со стилем WS_TABSTOP, то при нажатии
    пользователем на клавишу [Tab] (или [Shift]+[Tab]), текущий активный элемент
    диалога будет терять фокус и передавать его следующему за ним (или предыдущему)
    ближайшему элементу со стилем WS_TABSTOP. С помощью стиля
    WS_GROUP элементы диалога можно объединять в группы. Группа
    элементов начинается с элемента со стилем WS_GROUP и заканчивается
    элементом, после которого идет элемент со стилем WS_GROUP, или
    последним элементом в диалоге. Внутри группы только первый элемент
    должен иметь стиль WS_GROUP. Windows допускает перемещение
    внутри группы при помощи клавиш-стрелок.

    Классы предопределенных управляющих элементов:

    «STATIC»
    Статический управляющий элемент представляет собой
    текстовую метку или прямоугольник. Не предоставляет пользователю
    ни средств ввода, ни вывода. Примеры:

    Соответствующее описание ресурсов:

     CONTROL "",-1, "STATIC", SS_BLACKFRAME,  5, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_GRAYFRAME,  30, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_WHITEFRAME, 55, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_BLACKRECT,  80, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_GRAYRECT,  105, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_WHITERECT, 130, 40, 20, 10
     CONTROL "",-1, "STATIC", SS_ETCHEDFRAME,155, 40, 20, 10
    
     /* Для статиков-иконок или картинок текстовое поле определяет имя ресурса */
     CONTROL "Ex4_Bmp",-1, "STATIC", SS_BITMAP, 5, 55, -1, -1
     CONTROL "Ex4_Icon",-1, "STATIC", SS_ICON, 65, 55, -1, -1
    
     CONTROL "text",-1, "STATIC", SS_LEFT,    105, 55, 20, 10
     CONTROL "text",-1, "STATIC", SS_CENTER,  130, 55, 20, 10
     CONTROL "text",-1, "STATIC", SS_RIGHT,   155, 55, 20, 10
    
     /* По умолчанию SS_LEFT, SS_RIGHT, SS_CENTER делают перенос по словам */
     CONTROL "This is long text example",-1, "STATIC", SS_SIMPLE|SS_SUNKEN, 65, 70, 55, 15
     CONTROL "This is long text example",-1, "STATIC", SS_LEFT|SS_SUNKEN,  125, 70, 50, 15

    Для текстовых статиков со стилями SS_LEFT, SS_RIGHT
    или SS_CENTER существуют более простые операторы объявления ресурсов:

     LTEXT "text",-1, 105, 55, 20, 10
     CTEXT "text",-1, 130, 55, 20, 10
     RTEXT "text",-1, 155, 55, 20, 10
    
     LTEXT "This is long text example",-1, 65, 70, 55, 15, SS_LEFTNOWORDWRAP|SS_SUNKEN
     LTEXT "This is long text example",-1,125, 70, 50, 15, SS_LEFT|SS_SUNKEN

    Чтобы поменять текст статика ему можно послать сообщение WM_SETTEXT
    (wp=0; lp=(LPARAM)(LPCSTR)lpsz — адрес строки) или
    использовать функции:

    BOOL WINAPI SetWindowText(HWND hw, LPCSTR lpsz)
    BOOL WINAPI SetDlgItemText(HWND hDlg, int idControl, LPCTSTR lpsz)

    Чтобы сменить иконку или картинку нетекстового статика, надо послать
    ему сообщение STM_SETIMAGE (wp=(WPARAM)fImageType
    тип изображения: IMAGE_BITMAP или IMAGE_ICON;
    lp=(LPARAM)(HANDLE)hImage — дескриптор иконки или картинки).

    «BUTTON»
    Кнопка — это небольшое прямоугольное дочернее окно, обычно
    имеющее два состояния: нажато/отпущено или включено/выключено.
    Пользователь меняет состояние этого элемента щелчком мыши.
    К этому классу относятся: кнопки-«давилки» (push buttons),
    кнопки-«галочки» (check boxes), «радио»-кнопки (radio buttons)
    и специальный тип групповых рамочек (group boxes). Примеры:

    Соответствующее описание ресурсов:

     /* DEFPUSHBUTTON - кнопка по умолчанию (нажимается по [Enter]) */
     CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 20, 50, 12
     CONTROL "text", 3, "BUTTON", BS_PUSHBUTTON, 60, 20, 50, 12
    
     CONTROL "GroupBox1", -1, "BUTTON", BS_GROUPBOX, 5, 35, 50, 50
    
     CONTROL "text", 4, "BUTTON", BS_CHECKBOX,     10, 45, 30, 10
     CONTROL "text", 5, "BUTTON", BS_AUTOCHECKBOX, 10, 57, 30, 10
     CONTROL "text", 6, "BUTTON", BS_AUTO3STATE,   10, 69, 30, 10
    
     CONTROL "GroupBox2", -1, "BUTTON", BS_GROUPBOX, 60, 35, 50, 50
    
     CONTROL "text", 7, "BUTTON", BS_AUTORADIOBUTTON|WS_GROUP, 65, 45, 30, 10
     CONTROL "text", 8, "BUTTON", BS_AUTORADIOBUTTON, 65, 57, 30, 10
     CONTROL "text", 9, "BUTTON", BS_AUTORADIOBUTTON, 65, 69, 30, 10

    Для кнопок существуют более простые операторы объявления ресурсов:

     DEFPUSHBUTTON text, id, x, y, width, height [, style]
     PUSHBUTTON text, id, x, y, width, height [, style]
     GROUPBOX text, id, x, y, width, height [, style]
     CHECKBOX text, id, x, y, width, height [, style]
     RADIOBUTTON text, id, x, y, width, height [, style]

    Разница между стилями BS_xxx и BS_AUTOxxx
    заключается в том, что при щелчке по AUTO-кнопкам Windows
    сама автоматически переключает их состояние. Для не AUTO-кнопок
    это надо делать вручную в диалоговой процедуре, послав сообщение
    BM_SETCHECK (wp=(WPARAM)fCheck — флаг:
    BST_UNCHECKED, BST_CHECKED или BST_INDETERMINATE
    (для BS_3STATE-кнопок); lp=0) или при помощи функций:

    BOOL WINAPI CheckDlgButton(HWND hDlg, int idButton, UINT fCheck)
    BOOL WINAPI CheckRadioButton(
        HWND hDlg,         /* дескриптор родительского диалога */
        int idFirstButton, /* id первой радио-кнопки в группе */
        int idLastButton,  /* id последней радио-кнопки в группе */
        int idCheckButton  /* id отмечаемой радио-кнопки */
    )

    Автоматические радио-кнопки должны быть объединены в группу
    при помощи стиля WS_GROUP, чтобы Windows корректно их
    обрабатывала.
    Проверить состояние кнопки можно, послав ей сообщение BM_GETCHECK
    (wp=0; lp=0) или вызовом функции:

    UINT WINAPI IsDlgButtonChecked(HWND hDlg, int idButton)

    При щелчке мыши по кнопке она присылает родительскому диалогу сообщение-оповещение
    WM_COMMAND (HIWORD(wp)=BN_CLICKED; LOWORD(wp)=(int)idButton;
    lp=(HWND)hwndButton
    ).

    «EDIT»
    Поле редактирования предназначено для ввода пользователем текста
    с клавиатуры. Щелчком мыши внутри элемента пользователь передает
    этому элементу фокус ввода (input focus).
    При этом внутри элемента появляется текстовый курсор — мигающая
    каретка. Пользователь может использовать мышь для перемещения
    каретки по полю редактирования и выделению текста в этом поле. Примеры:

    Соответствующее описание ресурсов:

     /* По умолчанию эти элементы создаются вообще без рамки, поэтому добавлено WS_BORDER */
     CONTROL "" 4, "EDIT", ES_MULTILINE|ES_WANTRETURN|WS_BORDER 5, 45, 60, 35
    
     CONTROL "text", 5, "EDIT", ES_LEFT|WS_BORDER, 70, 45, 30, 10
     CONTROL "text", 6, "EDIT", ES_CENTER|ES_PASSWORD|WS_BORDER, 70, 57, 30, 10
     CONTROL "text", 7, "EDIT", ES_RIGHT|ES_READONLY|WS_BORDER, 70, 69, 30, 10

    Стиль ES_WANTRETURN означает, что кнопка [Enter] будет
    обрабатываться самим элементом, а не передаваться диалогу. Благодаря
    этому стилю оказался возможен переход на новую строчку для предложения
    «Она съела кусок…» (на картинке).
    По умолчанию текстовые поля позволяют вводить столько текста,
    сколько может отобразиться в рамках поля. Чтобы предоставить
    пользователю возможность ввести больше текста, надо использовать
    стиль ES_AUTOHSCROLLES_AUTOVSCROLL
    для многострочных полей).
    Для текстовых полей существует более простой оператор объявления ресурсов:

     EDITTEXT id, x, y, width, height [, style]

    Чтобы поменять содержимое текстового поля, программа вызывает
    функцию SetDlgItemText. Чтобы получить текущее
    содержимое текстового поля, используется функция:

    UINT WINAPI GetDlgItemText(
        HWND hDlg,       /* дескриптор родительского диалога */
        int idControl,   /* идентификатор поля */
        LPSTR lpString,  /* буфер под текст */
        int nMaxCount    /* размер буфера */
    )

    Чтобы узнать размер строки в текстовом поле, надо послать элементу
    сообщение WM_GETTEXTLENGTH (wp=0; lp=0).
    Текстовое поле посылает родительскому диалогу следующие сообщения-оповещения
    WM_COMMAND (LOWORD(wp)=(int)idContol; lp=(HWND)hwndEditCtrl):

    • HIWORD(wp)=EN_KILLFOCUS — текстовое поле потеряло фокус
      (фокус передан другому элементу диалога);
    • HIWORD(wp)=EN_SETFOCUS — текстовое поле получило фокус;
    • HIWORD(wp)=EN_CHANGE — пользователь изменил текст в поле;
    • HIWORD(wp)=EN_ERRSPACE — закончилось место, отведенное под
      текстовый буфер управляющего элемента.
    «LISTBOX»
    Окно-список используется для отображения списка имен
    (например, имен файлов). Пользователь может, просматривая список,
    выделить один или несколько элементов щелчком мыши. При выделении
    того или иного элемента списка, он подсвечивается, а родительскому
    окну посылается сообщение-оповещение. Для очень больших списков
    могут использоваться полосы прокрутки. Примеры:

    Соответствующее описание ресурсов:

     CONTROL "" 3, "LISTBOX", LBS_MULTIPLESEL|WS_BORDER|WS_VSCROLL, 5, 45, 60, 35
     CONTROL "" 4, "LISTBOX", LBS_MULTICOLUMN|WS_BORDER|WS_HSCROLL, 70, 45, 60, 35
     CONTROL "" 5, "LISTBOX", LBS_SORT|LBS_NOSEL|WS_BORDER, 135, 45, 60, 35

    или в короткой форме:

     LISTBOX 3, 5, 45, 60, 35, LBS_MULTIPLESEL|WS_BORDER|WS_VSCROLL
     LISTBOX 4, 70, 45, 60, 35, LBS_MULTICOLUMN|WS_BORDER|WS_HSCROLL
     LISTBOX 5, 135, 45, 60, 35, LBS_SORT|LBS_NOSEL|WS_BORDER

    Для добавления элемента к списку следует послать ему сообщение
    LB_ADDSTRING (wp=0; lp=(LPARAM)(LPCSTR)lpsz
    строка для добавления). Для того, чтобы заполнить один из списков,
    показанных на рисунке, в обработчик сообщения WM_INITDIALOG
    в диалоговую процедуру был вставлен такой фрагмент:

       char *a[12]={
        "jan","feb","mar","apr","may","jun",
        "jul","aug","sep","oct","nov","dec"};
       for (int i=0; i<12; i++) {
        SendDlgItemMessage(hw,3,LB_ADDSTRING,0,(LPARAM)a[i]);
       }

    Кроме этого, список «понимает» следующие сообщения:

    • LB_DELETESTRING (wp=(WPARAM)index; lp=0) —
      удалить элемент с указанным номером;
    • LB_INSERTSTRING (wp=(WPARAM)index; lp=(LPARAM)(LPCSTR)lpsz) —
      вставить указанную строку в список как элемент с индексом index;
    • LB_FINDSTRING (wp=(WPARAM)indexStart; lp=(LPARAM)(LPTSTR)lpszFind) —
      найти элемент, содержащий указанную строку (поиск ведется, начиная
      с элемента indexStart), результат сообщения — номер элемента,
      удовлетворяющего критерию, или LB_ERR;
    • LB_GETCOUNT (wp=0; lp=0) — количество элементов в списке;
    • LB_GETCURSEL (wp=0; lp=0) — выделенный элемент в списке;
    • LB_RESETCONTENT (wp=0; lp=0) — удалить все элементы из списка.

    Окно-список посылает родительскому диалогу следующие сообщения-оповещения
    WM_COMMAND (LOWORD(wp)=(int)idContol; lp=(HWND)hwndListBox):

    • HIWORD(wp)=LBN_DBLCLK — пользователь дважды щелкнул мышью по списку;
    • HIWORD(wp)=LBN_SELCHANGE — пользователь выделил другой элемент
      в списке (или отменил выделение).
    «COMBOBOX»
    Комбобокс — это помесь поля редактирования с окном-списком.
    Этот элемент содержит поле редактирование и список, который может
    отображаться все время либо «выпадать» при нажатии на кнопку рядом
    с полем редактирования. Есть три основных типа комбобоксов:

    • «выпадающий» комбобокс (CBS_DROPDOWN) содержит поле
      редактирования и «выпадающий» список;
    • «выпадающий» список (CBS_DROPDOWNLIST) не содержит
      поля для изменения текста;
    • простой комбобокс (CBS_SIMPLE) содержит поле
      редактирования и обычный список.

    Обратите внимание, что в ресурсах значение высоты элемента определяет
    размер поля редактирования вместе с «выпавшим» списком по вертикали.

     CONTROL "" 3, "COMBOBOX", CBS_DROPDOWN|CBS_AUTOHSCROLL|WS_VSCROLL, 5, 45, 60, 70
     CONTROL "" 4, "COMBOBOX", CBS_DROPDOWNLIST|WS_VSCROLL, 70, 45, 60, 70
     CONTROL "" 5, "COMBOBOX", CBS_SIMPLE|CBS_SORT, 135, 45, 60, 70

    Короткий вариант этого же объявления ресурсов:

     COMBOBOX 3, 5, 45, 60, 70, CBS_DROPDOWN|CBS_AUTOHSCROLL|WS_VSCROLL
     COMBOBOX 4, 70, 45, 60, 70, CBS_DROPDOWNLIST|WS_VSCROLL
     COMBOBOX 5, 135, 45, 60, 70, CBS_SIMPLE|CBS_SORT

    Для работы с комбобоксами существуют сообщения, аналогичные списковым:
    CB_ADDSTRING, CB_DELETESTRING, CB_INSERTSTRING,
    CB_FINDSTRING, CB_GETCOUNT, CB_GETCURSEL,
    CB_RESETCONTENT.
    Комбобокс посылает родительскому диалогу сообщение оповещение WM_COMMAND
    со следующими кодами оповещения:

    • CBN_SELCHANGE, когда пользователь выделяет другую строку
      в комбобоксе (бывает полезным для простых комбобоксов);
    • CBN_SELENDOK, когда пользователь выбрал элемент в выпадающем
      списке и щелкнул мышкой по выделению (подтвердил выделение), для
      простых комбобоксов посылается перед каждым CBN_SELCHANGE;
    • CBN_SELENDCANCEL, когда пользователь закрыл выпадающий список,
      так и не выбрав никакой элемент;
    • CBN_DROPDOWN, когда открывается выпадающий список;
    • CBN_CLOSEUP, когда выпадающий список был закрыт по той или
      иной причине.

    Кроме предопределенных управляющих элементов, Windows предоставляет еще
    набор стандартных управляющих элементов посредством библиотеки
    Common Controls Library (COMCTL32.DLL). Чтобы воспользоваться ей,
    в тест программы надо включить заголовочный файл commctrl.h
    и добавить в блок инициализации программы вызов функции:

    void WINAPI InitCommonControls(void)

    Управляющие элементы из этой библиотеки, как правило, посылают
    сообщения-оповещения родительскому диалогу через сообщение WM_NOTIFY
    (wp=(int)idControl; lp=(LPARAM)(NMHDR*)pmnh — указатель на структуру
    со специльными параметрами сообщения-оповещения).

    Классы управляющих элементов из Common Controls Library:

    List View Controls (WC_LISTVIEW)
    Элемент просмотра списков — это окно отображающее совокупность
    элементов. Каждый элемент может быть представлен текстовой меткой и
    (необязательно) иконкой. Типичный пример использования этого элемента —
    программа «Проводник». Содержимое того или иного каталога представляется
    в виде элемента просмотра списков. Есть четыре основных стиля для этого
    элемента:

    • крупные иконки — стиль LVS_ICON;
    • мелкие иконки — стиль LVS_SMALLICON;
    • список — стиль LVS_LIST;
    • таблица — стиль LVS_REPORT.

     CONTROL "",3,WC_LISTVIEW,LVS_REPORT|WS_BORDER, 5, 45, 60, 70
     CONTROL "",4,WC_LISTVIEW,LVS_LIST|WS_BORDER, 70, 45, 120, 70
     CONTROL "",5,WC_LISTVIEW,LVS_ICON|WS_BORDER, 200, 45, 120, 70

    Приведенные в примере списки заполнялись в диалоговой
    процедуре при инициализации диалога (WM_INITDIALOG):

       /* Создание колонок для 1го списка */
       LV_COLUMN lc; lc.mask=LVCF_FMT|LVCF_TEXT|LVCF_SUBITEM|LVCF_WIDTH;
       lc.fmt=LVCFMT_LEFT;
       lc.pszText="Col1"; lc.iSubItem=0; lc.cx=40;
       SendDlgItemMessage(hw,3,LVM_INSERTCOLUMN,0,(LPARAM)&lc);
       lc.pszText="Col2"; lc.iSubItem=1; lc.cx=40;
       SendDlgItemMessage(hw,3,LVM_INSERTCOLUMN,1,(LPARAM)&lc);
    
       /* Создание списка иконок для 2го и 3го списков */
       HIMAGELIST himl1,himl2;
       himl1=ImageList_Create(16,16,ILC_MASK,1,0);   /* список маленьких иконок */
       ImageList_AddIcon(himl1,LoadIcon(h,"Ex4_Icon"));
       himl2=ImageList_Create(32,32,ILC_MASK,1,0);   /* список больших иконок */
       ImageList_AddIcon(himl2,LoadIcon(h,"Ex4_Icon"));
       SendDlgItemMessage(hw,4,LVM_SETIMAGELIST,LVSIL_SMALL,(LPARAM)himl1);
       SendDlgItemMessage(hw,5,LVM_SETIMAGELIST,LVSIL_NORMAL,(LPARAM)himl2);
    
       /* Заполнение списков */
       LV_ITEM li; li.mask=LVIF_TEXT|LVIF_IMAGE; 
       li.iImage=0; /* номер иконки в списке */
       for (int i=0; i<12; i++) {
        li.iItem=i;
        li.iSubItem=0; li.pszText=a[i];
        SendDlgItemMessage(hw,3,LVM_INSERTITEM,0,(LPARAM)&li);
        SendDlgItemMessage(hw,4,LVM_INSERTITEM,0,(LPARAM)&li);
        SendDlgItemMessage(hw,5,LVM_INSERTITEM,0,(LPARAM)&li);
        wsprintf(str,"%d",i); /* вторая колонка для 1го списка */
        li.iSubItem=1; li.pszText=str;
        SendDlgItemMessage(hw,3,LVM_SETITEM,0,(LPARAM)&li);
       }
    
    Status Windows (STATUSCLASSNAME)
    Поле статуса — это горизонтальное окно в нижней части
    родительского окна, которое программа обычно использует для отображения
    каких-либо характеристик, параметров или небольших текстовых сообщений.

    В примере 4б можно заменить статическое текстовое поле
    на поле статуса:

     Ex4_Dlg DIALOG 50,50,70,40
      STYLE WS_POPUP|WS_CAPTION|WS_BORDER
      CAPTION "MyDlg"
      FONT 10, "Arial"
     {
      CONTROL "OK", 2, "BUTTON", BS_DEFPUSHBUTTON, 5, 10, 60, 12
      CONTROL "Status text",1,STATUSCLASSNAME, 0, 0, 0, 0, 0
     }

    При создании поля статуса не требуется указывать ни размер, ни позицию
    окна, Windows сама выберет эти параметры подходящим образом.
    Для создания поля статуса можно использовать специальную функцию:

      HWND WINAPI CreateStatusWindow(
        LONG style,       /* стиль: обязательно указываем WS_CHILD|WS_VISIBLE */
        LPCTSTR lpszText, /* текст поля статуса */
        HWND hwndParent,  /* родительское окно */
        UINT wID          /* числовой id поля статуса */
      )

    С помощью сообщения SB_SETPARTS поле статуса можно разбить
    на части (wp=(int)nParts — количество частей;
    lp=(LPARAM)(int*)widths — указатель на массив размеров частей).
    В таком случае текст для каждой части поля статуса задается сообщением
    SB_SETTEXT (wp=(int)iPart — номер части;
    lp=(LPARAM)(LPSTR)lpszText — текстовая строка). Пример:

     CreateStatusWindow(WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP,"text",hw,100);
     int widths[5]={100,150,200,-1};
     SendDlgItemMessage(hw,100,SB_SETPARTS,4,(LPARAM)widths);
     SendDlgItemMessage(hw,100,SB_SETTEXT,2,(LPARAM)"part 2");
     SendDlgItemMessage(hw,100,SB_SETTEXT,3,(LPARAM)"last part");
    Up-Down Controls (UPDOWN_CLASS)
    Управляющий элемент «up-down» представляет собой пару небольших
    кнопок-стрелок, нажимая которые, пользователь увеличивает или уменьшает
    значение. Этот элемент, как правило, связывается с элементом-компаньоном
    (buddy window), обычно реализованным в виде поля редактирования.
    Для пользователя элемент «up-down» и его компаньон представляются единым
    управляющим элементом.

     CONTROL "0", 5, "EDIT", ES_LEFT|WS_BORDER, 5, 30, 30, 10
     CONTROL "", 6, UPDOWN_CLASS, UDS_AUTOBUDDY|UDS_SETBUDDYINT, 35, 30, 10, 10

    Если при создании элемента «up-down» указать стиль UDS_AUTOBUDDY,
    то компаньоном будет назначен предыдущий управляющий элемент диалога.
    Программа может также передать дескриптор окна-компаньона при помощи
    сообщения UDM_SETBUDDY (wp=(WPARAM)(HWND)hwndBuddy
    дескриптор окна-компаньона; lp=0). Если элементу «up-down»
    назначить стиль UDS_SETBUDDYINT, то он будет автоматически
    менять текст окна-компаньона, представляющий числовое значение.
    Другой способ создать элемент «up-down» — использовать функцию

    HWND WINAPI CreateUpDownControl(
      DWORD dwStyle,   /* стиль элемента */
      int x, int y,    /* позиция */	
      int cx, int cy,  /* размеры */
      HWND hParent,    /* дескриптор родительского окна */
      int ID,          /* id элемента */
      HINSTANCE hInst, /* дескриптор экземпляра программы */
      HWND hBuddy,     /* дескриптор окна-компаньона */
      int nUpper,      /* максимальное значение */
      int nLower,      /* минимальное значение */
      int nPos         /* текущее значение */
    )
    Progress Bars (PROGRESS_CLASS)
    Полоса прогресса — это окно, которое программа может использовать
    для индикации состояния выполнения какой-либо длительной операции.
    Окно представляет собой прямоугольник, заполняемый системным цветом
    слева направо.

       CONTROL "",3,PROGRESS_CLASS,WS_BORDER, 5, 45, 100, 10

    Каждый раз, когда приложение посылает этому окну сообщение
    PBM_STEPIT (wp=0; lp=0), заполнение полосы прогресса
    продвигается дальше вправо на некоторое значение.

    Tooltip Controls (TOOLTIPS_CLASS)
    Окно-подсказка — всплывающее окно, содержащее строку описательной
    информации о том или ином элементе интерфейса программы. Таким элементом
    интерфейса может быть конкретное окно (управляющий элемент) или прямоугольный
    участок клиентской области какого-либо окна. Большую часть времени
    подсказка скрыта. Она появляется, когда пользователь задерживат курсор
    мыши над тем или иным элементом интерфейса программы более, чем на полсекунды.
    Подсказка скрывается, когда пользователь кликает мышью или уводит курсор
    с этого элемента. Одно окно-подсказка может обслуживать любое количество
    элементов интерфейса. Чтобы назначить тому или иному элементу интерфейса
    программы подсказку, надо окну-подсказке послать сообщение TTM_ADDTOOL
    (wp=0; lp=(LPARAM)(TOOLINFO*)lpti — указатель на структуру,
    содержащую информацию об элементе). Пример:

      TOOLINFO ti;
      HWND hwTooltip=CreateWindow(TOOLTIPS_CLASS,"",TTS_ALWAYSTIP,
       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,h,NULL);
      ti.cbSize=sizeof(TOOLINFO);
      ti.uFlags=TTF_SUBCLASS|TTF_IDISHWND;
      ti.hwnd=hMainWnd;
      ti.uId=(UINT)GetDlgItem(hMainWnd,ID_MYBUTTON);
      ti.hinst=h;
      ti.lpszText="Tooltip for my button";
      SendMessage(hwTooltip,TTM_ADDTOOL,0,(LPARAM)&ti);
    Property Sheets & Tab Controls
    Элементы вкладки свойств и переключатели вкладок
    обычно используются совместно. Пример использования вкладок:

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

     /* Шаблон вкладки "Общие" */
     ODTab_General DIALOG 0,0,180,150
      CAPTION "Общие"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      GROUPBOX "Интерфейс",-1,5,5,170,40
       PUSHBUTTON "Шрифт температуры",IDC_FONT,20,15,140,12
       AUTOCHECKBOX "На полный экран",IDC_FULLSCR,20,30,140,10,WS_DISABLED
      GROUPBOX "Алгоритм",-1,5,50,170,80
       LTEXT "Приоритет:",-1,15,65,50,10
       CTEXT "priority",IDC_PRIOTEXT,70,56,85,8
       CONTROL "",IDC_PRIOTRACK,TRACKBAR_CLASS,TBS_TOP|TBS_AUTOTICKS,70,64,85,17
       LTEXT "Период опроса датчика:",-1,15,85,110,10
       EDITTEXT IDC_TRATE,125,85,25,10,ES_RIGHT
       LTEXT "мс",-1,152,85,10,10
       LTEXT "Период опроса формирователя:",-1,15,100,110,10
       EDITTEXT IDC_FRATE,125,100,25,10,ES_RIGHT
       LTEXT "мс",-1,152,100,10,10
       LTEXT "Тайм-аут:",-1,15,115,110,10
       EDITTEXT IDC_TIMEOUT,125,115,25,10,ES_RIGHT
       LTEXT "мс",-1,152,115,10,10
     }
     /* Шаблон вкладки "Датчик" */
     ODTab_Sensor DIALOG 0,0,180,150
      CAPTION "Датчик"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      /* ... */
     }
     /* Шаблон вкладки "Порты" */
     ODTab_Ports DIALOG 0,0,200,150
      CAPTION "Порты"
      STYLE WS_CHILD|WS_VISIBLE
      FONT 8,"Arial"
     {
      /* ... */
     }

    Для создания диалога с вкладками используется функция PropertySheet,
    перед вызовом которой надо заполнить соответствующие системные структуры:

     char *TabTemplts[NumTabs]={"ODTab_General","ODTab_Sensor","ODTab_Ports"};
     PROPSHEETPAGE psp[NumTabs]; /* заполняется для каждой вкладки */
     for (i=0; i<NumTabs; i++) {
      psp[i].dwSize=sizeof(PROPSHEETPAGE);
      psp[i].dwFlags=PSP_DEFAULT;
      psp[i].hInstance=hThisInstance;
      psp[i].pszTemplate=TabTemplts[i];
      psp[i].pfnDlgProc=(DLGPROC)GlobDlgProc;
      psp[i].pfnCallback=NULL;
     }
     PROPSHEETHEADER psh;        /* описывает весь диалог */
     psh.dwSize=sizeof(PROPSHEETHEADER);
     psh.dwFlags=PSH_NOAPPLYNOW|PSH_PROPSHEETPAGE;
     psh.hwndParent=hWnd;
     psh.hInstance=hThisInstance;
     psh.pszCaption="Настройки";
     psh.nPages=NumTabs;
     psh.nStartPage=0;
     psh.ppsp=(LPCPROPSHEETPAGE)&psp;
     psh.pfnCallback=NULL;
     PropertySheet(&psh);

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

    Trackbars (TRACKBAR_CLASS)
    Ползунок (бегунок) используется, если от пользователя
    требуется получить дискретное значение из определенного диапазона.
    Маркер ползунка пермещается на заданное программой значение.
    Пример ползунка показан на первой вкладке предыдущего примера.
    Ползунки бывают горизонтальные (TBS_HORZ) или вертикальные
    (TBS_VERT). Диапазон значений ползунка задается сообщением
    TBM_SETRANGE (wp=(BOOL)fRedraw — перерисовать маркер
    после изменения диапазона; lp=MAKELONG(lMinimum,lMaximum)
    диапазон значений: младшее слово — минимальное значение,
    старшее слово — максимальное значение). Переместить ползунок можно
    при помощи сообщения TBM_SETPOS (wp=TRUE;
    lp=(LONG)position — новая позиция ползунка). Чтобы получить
    текущее значение ползунка, следует послать ему сообщение TBM_GETPOS
    (wp=0; lp=0). Ползунок оповещает родительское окно о событиях
    через сообщение WM_HSCROLL (LOWORD(wp)=ScrollCode
    код события; HIWORD(wp)=posistion — позиция маркера ползунка;
    lp=(HWND)hwndTrackBar — дескриптор элемента).

    Toolbars (TOOLBARCLASSNAME)
    Панель инструментов — это окно, содержащее набор кнопок,
    посылающих командное сообщение родительскому окну, когда пользователь
    щелкает по ним. Как правило, кнопки на панели инструментов соответствуют
    часто используемым командам меню приложения. Панель инструментов
    располагается ниже строки меню.

    Информация о кнопках передается панели инструментов через структуру
    TBBUTTON, а для создания окна панели инструментов удобно
    использовать функцию CreateToolbarEx.

       TBBUTTON tbb[nButtons];
       /* 1я кнопка */
       tbb[0].iBitmap=STD_PROPERTIES;   /*id иконки*/
       tbb[0].idCommand=ID_OPTION;      /*id команды*/
       tbb[0].fsState=TBSTATE_ENABLED;  /*состояние*/
       tbb[0].fsStyle=TBSTYLE_BUTTON;   /*стиль*/
       tbb[0].iString=0;                /*подпись под кнопкой*/
       /* 2я кнопка */
       tbb[1].iBitmap=STD_REDOW;
       tbb[1].idCommand=ID_CALCOUT;
       tbb[1].fsState=TBSTATE_ENABLED;
       tbb[1].fsStyle=TBSTYLE_BUTTON; 
       tbb[1].iString=0;
       /* 3я кнопка */
       tbb[2].iBitmap=STD_UNDO;
       tbb[2].idCommand=ID_CALCIN;
       tbb[2].fsState=TBSTATE_ENABLED;
       tbb[2].fsStyle=TBSTYLE_BUTTON;
       tbb[2].iString=0;
       /* разделитель */
       tbb[3].fsStyle=TBSTYLE_SEP;
       /* и т.д. */
       HWND htb=CreateToolbarEx(
        (HWND) hWndMain,                /*дескриптор родительского окна*/
        (DWORD) WS_CHILD|WS_VISIBLE|TBSTYLE_TOOLTIPS, /*стиль*/
        (UINT) ID_TOOLBAR,              /*id окна*/
        (int) 15,                       /*количество иконок в указанном ресурсе*/
        (HINSTANCE) HINST_COMMCTRL,     /*дескриптор модуля, из которого берется ресурс с иконками*/
        (UINT) IDB_STD_SMALL_COLOR,     /*id ресурса с иконками*/
        (TBBUTTON*) tbb,                /*указатель на массив с информацией о кнопках*/
        (int) nButtons,                 /*количество кнопок на панели*/
        16,16,                          /*размеры кнопок*/
        15,15,                          /*размеры иконок для кнопок*/
        sizeof(TBBUTTON)                /*размер структуры TBBUTTON*/
       );
    Rich Edit Controls (RICHEDIT_CLASS)
    Продвинутое поле редактирования является развитием
    класса «EDIT» стандартных управляющих элементов. Элементы управления
    этого класса поддерживают форматирование текста (по отдельным символам и
    по отдельным абзацам) и позволяют внедрять OLE-объекты.

    Tree View Controls (WC_TREEVIEW)
    Элемент просмотра дерева позволяет представлять информацию
    об иерархии некоторых объектов (содержание документа, дерево каталогов
    файловой системы и т.п.) Каждый объект может быть представлен текстовой
    меткой и иконкой. Объект может иметь иерархию дочерних объектов,
    которая раскрывается по щелчку на этом элементе.

  • Стандартные диалоги
  • Windows предоставляет набор готовых стандартных диалогов посредством
    библиотеки Common Dialog Boxes Library (COMDLG32.DLL): диалог открытия
    и сохранения файла, диалог печати документа, диалог выбора цвета, шрифта и т.п.
    Чтобы создать один из перечисленных диалогов, надо заполнить определенную
    структуру и вызвать соответствующую функцию из этой библиотеки:

    • BOOL WINAPI ChooseColor(CHOOSECOLOR* lpcc)
      — создает диалог, отображающий палитру цветов и позволяющий
      пользователю выбрать тот или иной цвет или создать свой.
    • BOOL WINAPI ChooseFont(CHOOSEFONT* lpcf)
      — создает диалог, отображающий имена установленных в системе шрифтов,
      их кегль, стиль начертания и т.п.
    • BOOL WINAPI GetOpenFileName(OPENFILENAME* lpofn)
      и BOOL WINAPI GetSaveFileNAme(OPENFILENAME* lpofn)
      — создают диалог, отображающий содержимое того или иного каталога,
      и позвояющий пользователю выбрать уникальное имя файла для открытия или
      сохранения.
    • BOOL WINAPI PrintDlg(PRINTDLG* lppd)
      — создает диалог, позволяющий пользователю установить различные
      опции печати, например, диапазон страниц, количество копий и др.
    • BOOL WINAPI PageStupDlg(PAGESETUPDLG* lppsd)
      — создает диалог, позволяющий пользователю выбрать различные
      параметры страницы: ориентацию, поля, размер бумаги и т.п.
    • HWND WINAPI FindText(FINDREPLACE* lpfr)
      — создает диалог, позволяющий пользователю ввести строку для поиска
      и такие опции, как направление поиска.
    • HWND WINAPI ReplaceText(FINDREPLACE* lpfr)
      — создает диалог, позволяющий пользователю ввести строку для поиска,
      строку для замены и опции замены (направление поиска, область поиска).

    Все перечисленные диалоги, кроме последних двух, — модальные,
    т.е. указанная функция не вернет значение, пока пользователь тем или
    иным способом не закроет диалог. Значение TRUE означает,
    что пользователь закрыл диалог, нажав на «ОК», а соответствующая структура
    заполнена новыми значениями. Значение FALSE означает,
    что пользователь нажал на [Esc], выбрал команду системного меню «Закрыть»
    или нажал кнопку «Отмена», а соответствующая структура осталась неизменной.
    Пример использования диалога открытия файла:

      char filename[MAX_PATH]="";             /*буфер под имя файла*/
      OPENFILENAME of;
      of.lStructSize=OPENFILENAME_SIZE_VERSION_400A; /*размер структуры OPENFILENAME*/
      of.hwndOwner=hw;                        /*дескриптор родительского окна*/
      of.hInstance=h;                         /*дескриптор экземпляра программы*/
      of.lpstrFilter="All files (*.*)\0*.*\0";/*фильтр файлов (тип)*/
      of.lpstrCustomFilter=NULL;              /*еще один фильтр: нам не надо*/
      of.nMaxCustFilter=0;                    /*нам не надо*/
      of.nFilterIndex=1;                      /*количество заданных нами фильтров*/
      of.lpstrFile=filename;                  /*адрес буфера под имя файла*/
      of.nMaxFile=MAX_PATH;                   /*размер буфера под имя файла*/
      of.lpstrFileTitle=NULL;                 /*буфер под рекомендуемый заголовок: нам не надо*/
      of.nMaxFileTitle=0;                     /*нам не надо*/
      of.lpstrInitialDir=NULL;                /*стартовый каталог: текущий*/
      of.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; /*разные флаги*/
      if (GetOpenFileName(&of)) {
       /* действия в случае успешного выбора файла */
      }
    

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

    Пример 5 демонстрирует использование немодального диалога
    в приложении типа «блокнот».

    Файл example5.h содержит константы-идентификаторы команд меню
    и элементов диалога.

    #define IDC_OPEN	10
    #define IDC_SAVE	11
    #define IDC_SAVEAS	12
    #define IDC_EXIT	13
    #define IDC_ABOUT	14
    
    #define ID_EDIT		20
    #define ID_STATUS	21

    Файл example5.rc описывает ресурсы программы: иконку, меню и шаблон диалога.

    #include "example5.h"
    
    Ex5_Icon ICON "myicon.ico"
    
    Ex5_Menu MENU
    {
     POPUP "&File"
     {
      MENUITEM "&Open...", IDC_OPEN
      MENUITEM "&Save", IDC_SAVE
      MENUITEM "Save &As...", IDC_SAVEAS
      MENUITEM SEPARATOR
      MENUITEM "&Exit\tAlt-F4", IDC_EXIT
     }
     POPUP "&Help"
     {
      MENUITEM "&About...", IDC_ABOUT
     }
    }
    
    Ex5_Dlg DIALOG 50,50,300,200
     STYLE WS_OVERLAPPED|WS_CAPTION|WS_BORDER|WS_SYSMENU|WS_VISIBLE
     MENU "Ex5_Menu"
     CAPTION "Example 5"
     FONT 10, "Arial"
    {
     EDITTEXT ID_EDIT, 5, 5, 290, 180, ES_MULTILINE|ES_WANTRETURN|ES_AUTOHSCROLL|ES_AUTOVSCROLL
     CONTROL "", ID_STATUS, STATUSCLASSNAME, 0, 0, 0, 0, 0
    }

    Файл example5.cpp — текст программы.

    #include <windows.h>
    #include <commctrl.h>
    #include <stdlib.h>
    #include "example5.h"
    
    BOOL CALLBACK MainProc(HWND,UINT,WPARAM,LPARAM);
    
    HINSTANCE hThisInstance;
    char filename[MAX_PATH]=""; /*буфер имени файла*/ 
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int) {
     hThisInstance=hInst;
     InitCommonControls();
     HWND hMainWnd=CreateDialog(hInst,"Ex5_Dlg",NULL,(DLGPROC)MainProc);
     if (!hMainWnd) return FALSE;
     MSG msg;                   /*цикл обработки событий*/
     while (GetMessage(&msg,NULL,0,0)) {
      if (!IsDialogMessage(hMainWnd,&msg)) {
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      }
     }
     return msg.wParam; 
    }
    
    /* диалоговая процедура */
    BOOL CALLBACK MainProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
     static DWORD OldIcon=0;    /* id старой иконки диалога */
     static OPENFILENAME of;
     char* buf;
     HANDLE hf; 
     DWORD len,len1;
     switch (msg) {
      case WM_INITDIALOG:       /* меняем иконку диалога */
       OldIcon=SetClassLong(hw,GCL_HICON,(long)LoadIcon(hThisInstance,"Ex5_Icon"));
       return TRUE;
      case WM_COMMAND:
       switch (LOWORD(wp)) {
        case IDCANCEL:          /* посылается при закрытии диалога по [Esc]*/
        case IDC_EXIT:          /* команда меню "Exit" */
         DestroyWindow(hw);
         break;
        case IDC_OPEN:          /* команда меню "Open" */
         of.lStructSize=OPENFILENAME_SIZE_VERSION_400A;
         of.hwndOwner=hw;
         of.lpstrFilter="All files (*.*)\0*.*\0";
         of.lpstrCustomFilter=NULL; of.nMaxCustFilter=0;
         of.nFilterIndex=1;
         of.lpstrFile=filename; of.nMaxFile=MAX_PATH;
         of.lpstrFileTitle=NULL; of.nMaxFileTitle=0;
         of.lpstrInitialDir=NULL;
         of.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
         if (!GetOpenFileName(&of)) break;
         SetDlgItemText(hw,ID_STATUS,filename);
         /* открываем файл */
         hf=CreateFile(filename,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
         if (hf==INVALID_HANDLE_VALUE) {
          MessageBox(hw,"Open failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         len=GetFileSize(hf,NULL);
         buf=(char*)malloc(len+1); /* доп. байт под символ-терминатор (0) */
         if (!buf) {
          MessageBox(hw,"Mem alloc failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         ReadFile(hf,buf,len,&len1,NULL);
         buf[len1]=0;
         CloseHandle(hf);
         SetDlgItemText(hw,ID_EDIT,buf);
         free(buf);
         break;
        case IDC_SAVEAS:        /* команда меню "Save As" */
         of.lStructSize=OPENFILENAME_SIZE_VERSION_400A;
         of.hwndOwner=hw;
         of.lpstrFilter="All files (*.*)\0*.*\0";
         of.lpstrCustomFilter=NULL; of.nMaxCustFilter=0;
         of.nFilterIndex=1;
         of.lpstrFile=filename; of.nMaxFile=MAX_PATH;
         of.lpstrFileTitle=NULL; of.nMaxFileTitle=0;
         of.lpstrInitialDir=NULL;
         of.Flags=OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
         if (!GetSaveFileName(&of)) break;
        case IDC_SAVE:          /* команда меню "Save" */
         if (lstrlen(filename)==0) {
          /* для нового файла - вызываем диалог "Save As" */
          PostMessage(hw,WM_COMMAND,IDC_SAVEAS,lp);
          break;
         }
         SetDlgItemText(hw,ID_STATUS,filename);
         /* сохраняем файл */
         hf=CreateFile(filename,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
         if (hf==INVALID_HANDLE_VALUE) {
          MessageBox(hw,"Save failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         len=SendDlgItemMessage(hw,ID_EDIT,WM_GETTEXTLENGTH,0,0);
         buf=(char*)malloc(len+1); /* доп. байт под символ-терминатор (0) */
         GetDlgItemText(hw,ID_EDIT,buf,len+1);
         if (!buf) {
          MessageBox(hw,"Mem alloc failed","Error",MB_ICONHAND|MB_OK);
          break;
         }
         WriteFile(hf,buf,len,&len1,NULL);
         CloseHandle(hf);
         free(buf);
         break;
        case IDC_ABOUT:         /* команда меню "About" */
         MessageBox(hw,"Example N5 from http://dims.karelia.ru/win32","About",MB_OK|MB_ICONINFORMATION);
         break;
       }
       return TRUE;
      case WM_DESTROY:          /* при закрытии окна восстанавливаем старую иконку */
       SetClassLong(hw,GCL_HICON,(long)OldIcon);
       PostQuitMessage(0);
       return TRUE;
     }
     return FALSE;
    }

    The article explains relationships and dependencies between the Windows API, the CRT, and the Standard C++ Library.

    • 1. The Purpose
    • 2. Basics
      • 2.2 Hardware
      • 2.3 Operating System
      • 2.4 Application Programming Interface
        • 2.4.1 Platform Software Development Kit
      • 2.5 C Runtime Library
        • 2.5.1 Standards and Extensions
      • 2.6 Unicode Awareness
        • 2.6.1 Platform SDK is Already Unicode Aware
        • 2.6.2 PSDK Solution: TCHARs
        • 2.6.3 CRT Solution: _TCHARs
      • 2.7 C++ Standard Library
        • 2.7.1 Contents of the C++ Standard Library
      • 2.8 Cross-platform Development
    • 3. Code Reuse
      • 3.1 Linking Statically
        • 3.1.1 CRT as a Black Box
      • 3.2 Linking Dynamically
    • 4. Summary

    1. The Purpose

    The purpose of this article is to clear the essential points about the Windows API, the C Runtime Library (CRT), and the Standard C++ Library (STL). It is not uncommon that even experienced developers have confusion and hold onto misconceptions about the relationship between these parts. If you ever wondered what is implemented on top of what and never had a time to figure it out, then keep reading.

    2. Basics

    The following diagram represents the relationship between WinAPI, CRT, and STL.

    Diagram #1: The relationship between Windows API, CRT, and the C++ Standard Library

    Adjacent blocks can communicate with each other. What does it mean? Let’s go from the bottom to the top.

    2.2. Hardware

    Each hardware part exposes its own set of commands that enables the Operating System to control and communicate with it. The amount and complexity of the commands varies from part to part. Often, different vendors of the same part may provide additional commands beyond the requirements of a common standard. Communication with countless hardware devices with endless variety of commands would be enormous toil for software writers if they had to access it directly. Here, the Operating System comes to the rescue.

    2.3 Operating System

    The purpose of the OS is to encapsulate all the intricacies of the underlying hardware and provide a unified access interface to the computer’s parts. No application can access the hardware directly. Only the OS can access the hardware. The part of the OS that accesses the hardware is said to run in kernel mode.

    Older OSs like MS-DOS, for example, allowed programs to access hardware resources directly. Though it enabled software writers to make certain performance gains, in the long run, this technique often made the software very brittle, and incompatible with newer hardware parts.

    2.4 Application Programming Interface

    The OS exposes the underlying machine resources by means of an Application Programming Interface (API). An API is a uniform set of functions that enables software developers to abstract from hardware peculiarities and focus on their own goals. An application cannot bypass the OS and access hardware resources directly. It is commonly said that applications run in user mode. MS Windows provides an API as a set of C functions. The C language is chosen as the lowest common denominator for software development under the Windows platform.

    2.4.1 Platform Software Development Kit

    MS distributes a free Platform Software Development Kit (Platform SDK or PSDK), which enables software developers to write Windows programs. The PSDK contains:

    1. Header files with API function declarations
    2. Import lib files to link with (where calls to API functions are redirected to the relevant DLLs)
    3. Documentation
    4. Various binary helper tools

    For example, to open or create a file, we call the CreateFile function, which is declared in the «WinBase.h» header file and requires the «Kernel32.lib» library to link with.

    The names of Windows API functions follow the Camel case naming convention and usually are easily distinguished by this. Names of macros and constants are conventionally in uppercase. Each function always has a «Requirements» section on its documentation page where the necessary headers, import libraries, and supported OS versions are specified.

    A Windows application can call any API function, provided the application follows the function’s signature and links with the appropriate import library (or gets the function’s address directly from the implementing DLL with the GetProcAddress call).

    2.5 C Runtime Library

    On top of the OS API functions, software vendors implement the C Runtime Library (CRT). CRT is a standardized set of header files and C functions which implement common tasks such as string operations, some math functions, basic input/output etc. Usually, the same vendor that makes the C compiler also provides the CRT implementation. The International Organization for Standardization [^] is responsible for the C language standard and its runtime library.

    2.5.1 Standards and Extensions

    Theoretically, by using only standard C functions, the developer can ensure that the same code may be used to build and run a program under any platform where a decent C compiler and CRT implementation exists. However, in practice, software vendors include many useful extensions to standard library functions, which make developers’ life easier but at a price of portability.

    The names of CRT functions are in lower case. The names of macros and constants are in uppercase. The names of extensions begin with the underscore character; for example, the _mkdir function. Each function always has a «Requirements» section on its documentation page where its header is specified.

    2.6 Unicode Awareness

    2.6.1 Platform SDK is Already Unicode Aware

    Actually, the above mentioned Win32 API names are not real names. These names are mere macros that are defined in the PSDK header files. So, when the PSDK documentation mentions a function, for example CreateFile, a developer should be aware that CreateFile is a macro. The true names of the CreateFile function are CreateFileA and CreateFileW. Yes, there are two, rather than one, versions for many Win32 API functions. The version that ends with ‘A’ accepts ANSI character strings, i.e., strings of regular chars. Another version ends with ‘W’ (the so called «wide» version) and accepts Unicode character strings, i.e., strings of wchar_ts. Both versions are implemented within the kernel32.dll module. The CreateFile macro will expand into the CreateFileW name if the UNICODE symbol is defined for a project, and into the CreateFileA name otherwise.

    There are three families of Windows OS: MS-DOS/9x-based, Windows CE, and Windows NT.

    1. The MS-DOS/9x-based family, which includes Windows 1.0-3.11, 95, 98, and Windows ME, is based on the MS-DOS OS. Earlier versions of Windows: 1.0-2.0 are true 16-bit OSs. Newer versions: 3.0, 95, 98, and ME are the so called hybrid 16/32-bit OSs. They are 16-bit at low level, but capable of running 32-bit programs with certain limitations. One of these limitations is that only the ANSI version of the Win32 API functions exist on this platform. Currently, the MS-DOS/9x-based family is extinct and unsupported by Microsoft.
    2. The Windows NT family started from Window NT 3.1 in early 90’s and includes Windows NT 4, Windows 2000, Windows XP, Window Vista, and Server flavors of these OSs. The Windows NT family is true 32-bit. It supports both ANSI and Unicode versions of the Win32 API. The Windows NT family operates with Unicode strings internally. The ANSI version of a Win32 API function is a mere wrapper around the real worker – the Unicode version of a function.
    3. The Windows CE family is intended for mobile and embedded devices. It is true 32-bit. Windows CE supports only the Unicode version of the Win32 API.

    2.6.2 PSDK Solution: TCHARs

    In order to avoid multiple PSDKs for different Windows families, Microsoft implemented generic text characters or TCHARs. TCHAR and other relevant macros are defined in the WinNT.h header file. The main idea is that the developer never uses the char or wchar_t types explicitly, but uses the TCHAR macro instead. The TCHAR macro will expand into the appropriate character type depending on whether the UNICODE symbol is defined for a build. In the same manner, instead of calling the ‘A’ or ‘W’ version of a Win32 API function, the developer calls a generic macro version, which will accommodate the actual character type at compile time.

    // Generic code
    //
    LPCTSTR psz = TEXT("Hello World!");
    TCHAR szDir[MAX_PATH] = { 0 };
    GetCurrentDirectory(MAX_PATH, szDir);
    
    // What actually happens if UNICODE symbol is NOT defined for a build
    //
    const char* psz = "Hello World!";
    char szDir[MAX_PATH] = { 0 };
    GetCurrentDirectoryA(MAX_PATH, szDir);
    
    // GetCurrentDirectoryA is a wrapper. It does the following:
    // 1. Allocates temporary wchar_t buffer of given size.
    // 2. Calls real worker: GetCurrentDirectoryW.
    // 3. Calls WideCharToMultiByte in order to convert wchar_t string into
    //    char string according to the active code page for a calling thread.
    //    If some character cannot be converted, then it will be replaced with the '?' symbol.
    
    // What actually happens if UNICODE symbol is defined for a build
    //
    const wchar_t* psz = L"Hello World!";
    wchar_t szDir[MAX_PATH] = { 0 };
    GetCurrentDirectoryW(MAX_PATH, szDir);
    // direct call to real worker; no wrappers in the middle

    Using TCHARs allows a developer to maintain a single code line both for ANSI and Unicode builds. Nowadays, if you do not intend to target old Windows 9x/Me platforms, you can safely forget about TCHARs and use Unicode strings everywhere and make Unicode only builds. As an added bonus, Unicode applications can forget about code pages hustle and use the same logic for all strings.

    The easy way to remember PSDK string declarations is to say them loud:

                L P C T STR = const TCHAR* 
                ^ ^ ^ ^ ^ 
                | | | | | 
    Long -------+ | | | | 
    Pointer to ---+ | | | 
    Constant -------+ | | 
    TCHAR ------------+ | 
    STRing -------------+

    Sometimes L — «Long» is omitted, since long and short pointers are obsolete for the Win32 platform. So, typedef can look like PTSTR = «pointer to TCHAR string», which is just TCHAR*.

    Here are two screenshots of the same program. The first screenshot is taken when the program is built as ANSI. The second screenshot demonstrates the Unicode build of the program.

    Naive ANSI program from the 20th century. All non-English characters are converted into illegible ‘?’ symbols.

    A modern Unicode program is aware of other languages.

    2.6.3 CRT Solution: _TCHARs

    Following the Platform SDK logic, Microsoft introduced generic text mapping into its C runtime library. CRT uses an additional header file to define generic character macros: «tchar.h«. In order to be compliant with the requirements of the C language standard, all non-standard names start from the underscore symbol. Also, CRT uses the shorter _T() macro for literal strings instead of the longer TEXT() macro, which is defined in «WinNT.h«. CRT authors decided to advance the generic text notion even further, and as a result of this decision, now CRT distinguishes three modes for text characters:

    • SBCS — The Single Byte Character Set. The classic char is used for strings. One ASCII character fits within one char element. No symbol has to be defined for a project. This is the traditional C language approach that survived from the 1970’s to our days. English characters are represented with values 0x00 — 0x7F; non-English characters are represented with values 0x80 — 0xFF. The actual meaning of non-English characters is interpreted according to the currently active code page.
    • _MBCS — The Multi-Byte Character Set. The classic char is used for strings. One multi-byte symbol may require one or two char elements. The _MBCS symbol has to be defined for a project. _MBCS is backward compatible with the SBCS mode, and was the default choice for new projects in MS Visual C++ until version 8.0 (2005). _MBCS was commonly used for Eastern Asian languages like Japanese, Korean, and Chinese. Now, _MBCS is being mostly ousted by Unicode characters. Using _MBCS was the only feasible option to handle Eastern Asian languages on Windows 9x/Me platforms.
    • _UNICODE — The Unicode Character Set. The wchar_t type is used for strings. One Unicode symbol occupies one wchar_t element, which is 16-bit on the Windows platform, and can represent up to 65535 different values. This is the default mode for the new projects starting from version 8.0 (2005) of MS Visual C++.

    CRT uses the _MBCS and _UNICODE symbols definition in order to distinguish between multi-byte and Unicode builds.

    Diagram #2: The Generic Text Mapping in CRT

    Generic-text data type or name SBCS (_UNICODE, _MBCS not defined) _MBCS defined _UNICODE defined
    _TCHAR char char wchar_t
    _T("Hello, World!") "Hello, World!" "Hello, World!" L"Hello, World!"
    Function name prefix and example:
    _tcs
    _tcscat, _tcsicmp
    str, _str
    strcat, _stricmp
    _mbs
    _mbscat, _mbsicmp
    wcs, _wcs
    wcscat, _wcsicmp
    // Generic code; names are not standard, hence the leading underscore.
    //
    _TCHAR message[128] = _T("The time is: ");
    _TCHAR* now = _tasctime(&tm);
    _tcscat(message, now);
    _putts(message);
    
    // What happens if no symbol is defined at all (SBCS).
    //
    char message[128] = "The time is: ";
    char* now = asctime(&tm);
    strcat(message, now);
    puts(message);
    
    // What happens if _MBCS symbol is defined (Multi-byte Character Set);
    // non-standard names are with the leading underscore.
    //
    char message[128] = "The time is: ";
    char* now = asctime(&tm);
    _mbscat(message, now);
    puts(message);
    
    // What happens if _UNICODE symbol is defined (Unicode Character Set);
    // non-standard names are with the leading underscore.
    //
    wchar_t message[128] = L"The time is: ";
    wchar_t* now = _wasctime(&tm);
    wcscat(message, now);
    _putws(message);

    2.7 C++ Standard Library

    The C++ programming language defines its own standard library. The C++ Standard Library specifies a set of classes and functions that facilitate common programming tasks.

    Often, the C++ Standard Library is referred to as STL. This abbreviation belongs to pre-standard times, and stands for Standard Template Library. The latest revision of the C++ standard STL became a subset of the C++ Standard Library. However, the term STL is still ubiquitous and used as a synonym for the C++ Standard Library.

    The International Organization for Standardization [^] is responsible for the C++ language standard and its library.

    2.7.1 Contents of the C++ Standard Library

    The C++ Standard Library may be divided into the following major parts:

    1. Containers, where common data structures are defined, such as vector, set, list, map etc.
    2. Iterators, which provide a uniform way to operate over standard containers.
    3. Algorithms, which implement common useful algorithms. Algorithms use iterators instead of working directly with containers. That’s why the same implementation of an algorithm can be used with different standard containers.
    4. Allocators, which handle memory storage allocation/deallocation for elements in containers.
    5. Function Objects and Utilities, which are helpers to algorithms and containers.
    6. Streams, which provide a uniform object oriented way of input/output.
    7. C Runtime Library. Due to the backward compatibility of C++ with the C language, CRT is incorporated into the Standard C++ Library.

    2.8 Cross-platform Development

    Sometimes there is a requirement that a software program will run on several computer platforms. The developer may choose to develop as many separate code bases of software as there are target platforms. However, this approach is tedious and error prone. It is also wasteful and ineffective considering development resources since the same functionality must be implemented and maintained over and over again.

    The common approach is to develop a single code base for all platforms and restrict the usage of platform-dependent API functions and vendor-specific standard library extensions. It makes development harder; however, in the long run, all platforms benefit from new features and bug fixes.

    3. Code Reuse

    There are two ways to incorporate the CRT and/or the C++ Library code into a program: static linking and dynamic linking. In the following discussion, I will use solely the CRT term to save typing; however, these concepts are relevant both to CRT and the C++ Standard Library.

    3.1 Linking Statically

    When the CRT/C++ Library is linked statically, then all its code is embedded into the resulting executable image. This technique has both advantages and disadvantages.

    Advantages:

    1. Simple deployment. It is enough to copy a program to the destination computer to make it run. No need to worry about complicated scenarios of CRT/C++ Library deployment.
    2. No additional files. It can be very convenient for small utility applications to comprise just of one executable file. Such self-contained applications can be easily downloaded and redistributed without the risk of breaking its integrity.

    Disadvantages:

    1. Not serviceable. New versions of a library and fixes of old versions are invisible for statically linked programs.
    2. Domino Effect of static linking. In the modern world, rarely can a program pull it out all by itself. Nowadays, software programs are complex, and heavily rely on third party components and libraries. Also, a software program itself is often divided into several loosely coupled modules. Using static linking to CRT in one of them greatly reduces interoperability between modules and forces developers to fall back on the lowest common denominator, i.e., the C interface with explicit methods for the acquisition and release of resources. The following section discusses the issue in more details.

    3.1.1 CRT as a Black Box

    The problem is that internal CRT objects cannot be shared with other CRT instances. The memory allocated in one instance of CRT must be freed in the same instance, the file opened on one instance of CRT must be operated and closed by functions from the same instance, etc. It happens because the CRT tracks the acquired resources internally. Any attempt to free a memory chunk or read from a file via FILE* that came from another CRT instance will lead to corruption of the internal CRT state and most likely to crash.

    That’s why linking CRT statically obligates a developer of a module to provide additional functions to release allocated resources and a user of a module to remember to call these functions in order to prevent resource leaks. No STL containers or C++ objects that use allocations internally can be shared across modules that link to the CRT statically. The following diagram illustrates the usage of a memory buffer allocated via a call to malloc.

    Diagram #2: Using memory allocated by malloc from different modules

    Diagram #2: Using memory allocated by `malloc' from different modules

    In the above diagram, Module 1 is linked to the CRT statically, while Modules 2 and 3 are linked to the CRT dynamically. Modules 2 and 3 can pass CRT owned objects between them freely. For example, a memory chunk allocated with malloc in Module 3 can be freed in Module 2 with free. It is because both malloc and free calls will end up in the same instance of CRT.

    On the other hand, Module 1 cannot let other modules to free its resources. Everything allocated in Module 1 must be freed by Module 1. It is because only Module 1 has access to the statically linked instance of the CRT. In the above sample, Module 2 must remember to call a function from Module 1 in order to properly release the acquired memory.

    3.2 Linking Dynamically

    When the CRT/C++ Library is linked dynamically, only small import libraries are linked with the resulting executable image. Import libraries contain instructions for where to find the actual implementation of the CRT/C++ Library functions. On a program’s start, the system loader reads these instructions and loads the appropriate DLLs into the process’ address space.

    Advantages:

    1. Improved Modularity. As described in previous sections, the overall modularity of a program can benefit from dynamic linking. A program can be divided into several modules while being able to pass relatively high-level objects between them.
    2. Faster start. CRT DLLs are preloaded by the system on start. Then, when a program needs to load a CRT module, no actual load occurs. It enables the system to save physical memory and reduce page swapping.

    Disadvantages:

    1. Complicated deployment. CRT libraries must be redistributed and properly installed in order for a program to work. It requires writing an additional setup program and thinking out a deployment strategy.

    4. Summary

    The article described relationships and dependencies between the Windows API, the C Runtime Library, and the Standard C++ Library. The Windows API is the lowest operational level for user mode programs. On top of the Windows API, there is the C Runtime Library, which encapsulates and hides the Operating System differences. The Standard C++ Library provides much more functionality and also includes the CRT as an integral part. Using only standardized functions and classes allows to write cross-platform applications. Such applications require rebuild only in order to run on a new platform. No code change is required.

    Both the C Runtime Library and the Standard C++ Library can be linked to statically or dynamically, depending on the application’s needs. Each method has its own advantages and drawbacks.

    The windows.h header file is a fundamental header file for the Microsoft Windows operating system. It includes declarations for a wide range of functions and data types used in Windows programming. Here’s a basic example demonstrating the usage of windows.h for creating a simple Windows GUI application:

    Contents

    • 1 C Library – windows.h File
    • 2 Predefined functions of C Library – windows.h
      • 2.1 C Library – windows.h predefined functions List
      • 2.2 Note
    • 3 sleep() in C Library – windows.h
    • 4 Sleep() Example-1
      • 4.1 Code
      • 4.2 Explanation
    • 5 Sleep() Example-2
    • 6 Slep() Example-3
    • 7 Next
      • 7.1 Explore more
    #include <windows.h>
    
    // Function prototype for the window procedure
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int main() {
        // Get the handle to the instance of this application
        HINSTANCE hInstance = GetModuleHandle(NULL);
    
        // Create the main window
        HWND hwnd = CreateWindowEx(
            0,                              // Optional window styles
            L"WindowClass",                 // Window class
            L"My First Window",             // Window title
            WS_OVERLAPPEDWINDOW,            // Window style
    
            // Size and position
            CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
    
            NULL,       // Parent window    
            NULL,       // Menu
            hInstance,  // Instance handle
            NULL        // Additional application data
        );
    
        // Display the window
        ShowWindow(hwnd, SW_SHOWNORMAL);
    
        // Enter the message loop
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    
    // The window procedure
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        switch (uMsg) {
            case WM_DESTROY:
                PostQuitMessage(0);
                return 0;
            default:
                return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        return 0;
    }
    

    This above example creates a simple window using the Windows API. The WindowProc function is the window procedure, which handles messages for the main window. The CreateWindowEx function creates the main window, and the ShowWindow function displays it. The program then enters a message loop (while (GetMessage...)) where it waits for and processes messages until the user closes the window.

    Remember that this is just a basic example, and real-world Windows applications will involve more complexities and considerations. Additionally, GUI programming in Windows often involves using additional libraries, such as MFC (Microsoft Foundation Classes) or newer frameworks like WinUI.

    Predefined functions of C Library – windows.h

    The windows.h header in Windows programming provides declarations for various functions, data types, and constants used in Windows API. Here are some commonly used predefined functions available in windows.h:

    C Library – windows.h predefined functions List

    Function Category Function Note
    Window Management Functions: CreateWindowEx()  Creates an extended window.
    ShowWindow()  Sets the specified window’s show state.
    UpdateWindow()  Updates the client area of the specified window.
    DestroyWindow()  Destroys the specified window.
    DefWindowProc()  The default window procedure for window messages not processed by your window procedure.
    Message Handling Functions: GetMessage()  Retrieves a message from the calling thread’s message queue.
    TranslateMessage()  Translates virtual-key messages into character messages.
    DispatchMessage()  Dispatches a message to a window procedure.
    PostQuitMessage()  Posts a quit message to the message queue.
    Thread Functions: CreateThread()  Creates a new thread for parallel execution.
    GetCurrentThreadId()  Retrieves the thread identifier of the calling thread.
    Synchronization Functions: CreateMutex()  Creates or opens a named or unnamed mutex object.
    CreateEvent()  Creates or opens a named or unnamed event object.
    WaitForSingleObject()  Waits until the specified object is in the signaled state.
    ReleaseMutex()  Releases ownership of the specified mutex object.
    File and File I/O Functions: CreateFile()  Creates or opens a file or I/O device.
    ReadFile, WriteFile()  Reads from or writes to a file or I/O device.
    CloseHandle()  Closes an open object handle.
    Memory Management Functions: VirtualAlloc()  Reserves or commits a region of memory within the virtual address space of a specified process.
    VirtualFree()  Releases, decommits, or releases and decommits a region of memory.
    Time Functions: GetSystemTime()  Retrieves the current system date and time.
    Sleep()  Suspends the execution of the current thread for a specified interval.
    Miscellaneous Functions: MessageBox()  Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message.
    GetLastError()  Retrieves the calling thread’s last-error code value.
    LoadLibrary, GetProcAddress()  Loads a dynamic-link library (DLL) into the calling process’s address space.
    C Library – windows.h predefined functions

    Note

    These are just a few examples, and there are many more functions provided by Windows.h for various purposes. When working with Windows programming, documentation is an essential resource to understand and use these functions effectively. The next section only discussed Sleep() function.

    sleep() in C Library – windows.h

    if you have the following questions like

    • How to use the delay function in C?
    • How to use the C program delay function in Windows?

    Here is your solution,

    If you wanna use the delay feature, you can use the Sleep() function in the Windows platform, based on the compiler the Sleep() will call in different library files(Some times Sleep function will be winbase.h, Sometimes different). Don’t worry about that, if you include the windows.h the header file, that will be taken care of. why because everything all the sleep function necessary headers are already included in windows.h the file.

    • Rule-1: You should add the header file #include <windows.h>
    • Rule-2: Sleep() function first letter always Uppercase, if you declare in the small case the compiler might generate error “Undefined reference to sleep”.
    • Rule-3: Sleep() function argument (Milliseconds) should be unsigned long type. If your call [Example : Sleep("ArunEworld")] Sleep() function with char type or other types, the compiler will generate an Error.

    Please note that the sleep function may not be very precise, and the actual delay could be slightly longer due to system-related factors. If you need more precise timing, you might want to explore other methods or libraries for that purpose.

    Sleep() Example-1

    Code

    #include <stdio.h>
    #include <unistd.h>  // for sleep function
    
    int main() {
        printf("ArunEworld: This is before the delay.\n");
    
        // Sleep for 3 seconds
        sleep(3);
    
        printf("ArunEworld: This is after the delay.\n");
    
        return 0;
    }
    

    Explanation

    In this above example, the program will print the first message, then pause for 3 seconds using sleep(3), and finally print the second message after the delay.

    Remember to include the <unistd.h> header for the sleep function to work.

    Sleep() Example-2

    The below code will be printed ArunEworld website every 1 second

    #include <stdio.h>
    #include <Windows.h>
    int main() 
    {
    	while(1)
    	{
    		//print the aruneworld website address
    		printf("www.ArunEworld.com.\r\n");
    		
    		//make a delay every in millisecond
    		Sleep(1000);
    	}
    	
    	return 0; 
    }

    Slep() Example-3

    #include <stdio.h>
    #include <Windows.h>
    int main() 
    {
    	while(1)
    	{
    		//print the aruneworld website address
    		printf("www.ArunEworld.com.\r\n");
    		
    		//make a delay every in millisecond
    		Sleep("ArunEworld");
    	}
    	
    	return 0; 
    }

    The above code will show the Error like "[Note] expected 'DWORD' but argument is of type 'char *'". Why because Sleep() the argument should be unsigned long. here ‘ArunEworld' is a charter pointer.

    Refer to the C Examples – Time Delay


    Next

    C – Basic
    C – Operator
    C – Decision making, Branching, Looping
    C – Functions
    C – Storage Class
    C – Extern
    C – Array
    C – Pointer
    C – Memory Allocation
    C – Structure
    C – Union
    C – Structure and Union
    C – Macros
    C – Preprocessor and Macros
    C – File Management
    C – Coding Standard
    C – Compilers
    C – Compiling Process
    C File Management
    C Library
    C Example

    Hits (since 2024-Jan-26) — 17,850

    Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии
  • Активация windows 10 через блокнот
  • Как отключить озвучивание действий на windows 10
  • Как сделать окно поверх других окон windows 11
  • Что делать если забыл пароль от ноутбука samsung windows 7
  • Windows 10 сброс пароля администратора через реестр