Where can I view a description of the windows api functions. What is Windows API. Description of function prototypes in MSDN

FindWindow
GetWindow
GetWindowText
SetWindowText
IsWindow
MoveWindow
IsWindowVisible
EnableWindow
IsWindowEnabled
WindowFromPoint
ShowWindow
CloseWindow
SetWindowPos
GetClassLong
SetClassLong
GetWindowLong
SetWindowLong
GetDesktopWindow
GetParent

FindWindow function

function FindWindow(className,WindowName: PChar) : HWND;
The function returns a window descriptor that satisfies the request (0 if no such window is found).

ClassName The name of the class used to search among ALL windows of the system. WindowName Window title

One of the parameters may be equal to nil, then the search is carried out using another parameter.
Example:

GetWindow function

function GetWindow(Wnd: HWND; Param) : HWND
The function returns a window descriptor that satisfies the request.

Wnd A handle to some initial window Param Takes one of the following constant values: gw_Owner The handle to the ancestor window is returned (0 if there is no ancestor). gwHWNDFirst Returns a handle to the first window (relative to Wnd). gw_HWNDNext Returns a handle to the next window (windows are iterated without repetition, i.e. if you did not change the Wnd parameter of the function, handles are not returned again) gw_Child Returns a handle to the first child window.

Example:

GetWindowText Function

function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer;
The function returns the window text. For a form this will be the title, for a button - the label on the button.

hWnd A descriptor of the window whose text you want to get. lpString Variable in which the result will be placed nMaxCount

Maximum text length; if the text is longer, it is cut off.


Example:

IsWindow function

function IsWindow(hWnd: HWND): BOOL; Returns True if a window with the given handle exists, False otherwise.

Hwnd Descriptor of the desired window

MoveWindow function

MoveWindow(hWnd: HWND; X, Y, nWidth, nHeight: Integer; bRepaint: BOOL): BOOL; Moves the window to a new position.

hWnd A handle to the window being moved. X, Y, nWidth, nHeight Accordingly: new coordinates X,Y; new width, height. bRepaint A Boolean value indicating whether the window will be redrawn.

IsWindowVisible function

function IsWindowVisible(hWnd: HWND): BOOL;
Returns True if the given window is visible.

hWnd Window descriptor.

EnableWindow function

function EnableWindow(hWnd: HWND; bEnable: BOOL): BOOL;
Sets the window's availability (a window is unavailable if it does not respond to mouse, keyboard, etc. events). An analogue in Delphi of the Enabled property of components. EnableWindow returns True if everything was successful and False otherwise.

hWnd Window descriptor. bEnable A Boolean value indicating whether the window is available.


Example:

IsWindowEnabled function

function IsWindowEnabled(hWnd: HWND): BOOL;
Returns for the given window: True if the window is available and False otherwise.

hWnd Window descriptor.

WindowFromPoint function

WindowFromPoint(Point: TPoint): HWND;
Returns a handle to the window located at a given point on the screen.

Point Screen point coordinate type TPoint(type definition see below)

Function

type TPoint = Record x: Longint; y:Longint; end;

ShowWindow function

function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; Shows or hides the window.

hWnd Descriptor of the desired window nCmdShow A constant that determines what will be done with the window: SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX


Example:

CloseWindow function

function CloseWindow(hWnd: HWND): BOOL; stdcall;
Closes the window.

hWnd A handle to the window to be closed.

SetWindowPos

function SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND; X, Y, cx, cy: Integer; uFlags: UINT): BOOL; stdcall;
Sets the window to a new position

hWnd window optsaler hWndInsertAfter Descriptor of the window before which in the list Z-Order a window will be inserted hWnd, or one of the following constants: HWND_BOTTOM Place window at the bottom of the Z-Order list HWND_TOP Place window on top of Z-Order list X, Y, cx, cy

Accordingly, new horizons. , vert. window positions ( X, Y), as well as the new width
and height ( cx, cy)

uFlags One or more (separated OR) the following constants: SWP_NOSIZE Do not resize window after moving ( cx, cy are ignored) SWP_NOZORDER Do not change the position of the window in the Z-Order list SWP_SHOWWINDOW Make window visible after moving SWP_HIDEWINDOW Hide window after moving SWP_NOACTIVATE Don't give focus to window after moving SWP_NOMOVE Don't move the window (ignored X, Y)

GetClassLong function

function GetClassLong(hWnd: HWND; nIndex: Integer): Integer;
This function returns a 32-bit integer taken from a specific field in a record TWndClassEx the specified window.

hWnd Window handle nIndex A constant defining what will be returned. Must be one of the following: GCL_MENUNAME Returns a pointer to a string containing the name of the class menu defined in the resource file associated with some program. GCL_HBRBACKGROUND Returns a handle (HBRUSH) to the background brush associated with the class GCL_HCURSOR Returns a handle (HCURSOR) to the cursor associated with the class GCL_HICON Returns a handle (HICON) to the icon associated with the class GCL_HMODULE Returns a handle to the process (HMODULE) that registered the class. GCL_CBWNDEXTRA Returns the size of memory (in bytes) allocated for storing additional data for THIS WINDOW. For a description of how to use this memory, see the function description GCL_CBCLSEXTRA Returns the size of memory (in bytes) allocated for storing additional GIVEN CLASS GCL_WNDPROC Returns the address of the window procedure associated with the class. GCL_STYLE Returns the class style (the presence of a particular style is checked by a bitwise operation And using constants like cs_XXX) GCL_HICONSM

note: in the case when the function returns a pointer, type casting is necessary (Integer ->

SetClassLong function

function SetClassLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Integer; Pair function function. Sets the required field to the appropriate value.
The function returns the old value of the field so that it can be corrected later, or returns zero if something went wrong.

hWnd Window handle nIndex One of the constants GCL_XXX from function. Depending on the value of this field, the required field will be changed.

note Pointer to type Integer.

GetWindowLong Function

function GetWindowLong(hWnd: HWND; nIndex: Integer): Longint; Returns information about a window as a 32-bit integer.

hWnd Window handle nIndex A constant defining what will be returned. It must be one of the following:
GWL_WNDPROC Returns the address of the window procedure associated with this window. The resulting address (after appropriate type casts) can be used in a function CallWindowProc. This value is usually used if they want to replace an existing window procedure with their own, and in order not to lose the window’s functionality, they usually use CallWindowProc. GWL_HINSTANCE Returns the application handle specified when the window was created by the function CreateWindowEx. GWL_HWNDPARENT Returns the handle (HWND) of the parent window GWL_STYLE Returns the window style. Specific style values ​​are found using a bitwise operation And and constants WS_XXX GWL_EXSTYLE Returns the extended window style. Specific style values ​​are found using a bitwise operation And and constants WS_EX_XXX GWL_USERDATA Returns the 32-bit integer associated with the window (this is the last parameter in the CreateWindow or CreateWindowEx call) GWL_ID Returns the window ID (it has nothing to do with the window handle!) specified by the hMenu parameter for child windows when calling CreateWindow or CreateWindowEx

note: in the case when the function returns a pointer, type casting is necessary (Integer -> Pointer). You can do it like this:

SetWindowLong function

function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint;
Paired to the function. Changes the attributes of a specific window.
The function returns the old value of the property if the call succeeds, or null otherwise.

hWnd Window handle nIndex A constant that specifies which property will be changed. Must be one of the constants GWL_XXX from the function description dwNewLong New value of a property defined by a constant nIndex

note: when setting pointer fields, a type cast is required Pointer to type Integer.

GetDesktopWindow Function

function GetDesktopWindow: HWND
The function returns a handle to the Desktop window. No parameters.

GetParent

function GetParent(hWnd: HWND): HWND;
Returns the parent window handle for a window hWnd.

hWnd Window handle

C WinAPI is Microsoft's core set of programming interfaces (APIs) available in operating systems. An early version was called the Win32 API.

Introduction

C WinAPI is an application programming interface that is used to create Windows applications. To get started, a novice user must download the Windows SDK, formerly known as the Platform SDK.

Contains header files, libraries, samples, documentation, and tools that are used to develop applications. API for C and C++ programming languages. This is the most direct way to create applications on the company's operating system.

C WinAPI can be divided into several areas:

    basic services;

    safety;

  • user interface;

    multimedia;

    Windows shell;

    network services.

Core services provide access to basic resources. These include C WinAPI functions, file systems, devices, processes, threads, registry, and error handling. The security area provides interfaces, objects, and other programming elements for authentication, authorization, cryptography, and other security-related tasks. The graphics subsystem provides functionality for outputting graphic content to monitors, printers, and other output devices. The user interface provides functionality for creating windows and controls.

The multimedia component provides tools for working with video, audio, and input devices. Shell interface functions allow applications to access functions provided by the operating system shell. Network services provide access to the networking capabilities of Windows.

Components

When creating WinAPI C, you should consider the basic capabilities provided by the Windows API, which can be organized into seven categories. Let's look at each of them in more detail.

Essential services provide access to the basic system resources available in Windows. Examples: file system, peripherals, processes, registry access, and exception management system. These functions are stored in the files kernel.exe, krnl286.exe, or krnl386.exe for 16-bit systems and kernel32.dll and advapi32.dll for 32-bit systems.

The GUI provides access to resources for display on monitors, printers, and other peripheral equipment. Stored in gdi.exe on 16-bit systems and gdi32.dll on 32-bit systems.

The user interface is responsible for viewing and controlling basic elements such as buttons and scroll bars, obtaining keyboard and mouse information, and related functions. These functions are stored in user.exe on 16-bit systems and user32.dll comctl32.dll on 32-bit systems. Starting with XP, the controls were grouped into comctl32.dll.

General dialogs - display information for opening and saving files, choosing colors and fonts. Found in the file comdlg.dll on 16-bit systems and comdlg32.dll on 32-bit systems.

Windows Shell is a component of WinAPI that allows applications to access the functionality provided by the operating system shell.

Network Services provides access to various networking capabilities of the operating system. Its subcomponents include NetBIOS, Winsock, RPC. In older versions - NetDDE.

Versions

Win16, Win32 and Win32s are standard sets of components that allow application software to use the functions of various operating systems of the Windows family.

Win32, the successor to Win16, was introduced in 1993 in 32-bit Windows family products such as Windows NT, 2000, 95. This programming interface is implemented by three software libraries: Kernel32.dll, User32.dll and GDI32.dll2. The same Win32 features are available in all Windows products, and depending on the product, using certain features may result in a service error.

Win32 capabilities include communication between programs, management of processes, computer networks, files, printers, servers, and communication ports.

Specification

C WinAPI is an abstract programming interface specification for the Windows operating system. Consists of declarations of functions, unions, structures, data types, macros, constants and other programming elements. WinAPI is described by a master and is found in the Windows C headers. The official implementation of WinAPI functions is found in dynamic link libraries (DLLs): for example, kernel32.dll, user32.dll, gdi32.dll or shell32.dll in the system directory. There are third-party implementations of the Windows API: most notably the Wine project and the ReactOS project.

Windows API is a dynamic object. The number of functions is constantly growing with each new OS version and new service packs. There are also important differences between the server and desktop versions of the operating system. Some functions are not officially documented.

Pelles C

Pelles C is a free program and the best C compiler and integrated development environment (IDE) for the C programming language. Supports 32-bit Windows (x86) and 64-bit Windows (x64). Implements both C99 and C11 standards. Pelles C has a built-in resource editor, bitmap, icon and cursor editor and hex dump editor. It is developed by Swedish developer Pelle Orinius. The compiler is named after its author. Comes with an SDK, so the programmer can immediately start creating applications without further installation.

Target architecture error

To create Windows API programs, you must enable Microsoft extensions. They are disabled by default, causing the compiler to issue the following error message, which is an example of a broken C WinAPI: fatal error #1014: #error: "No target architecture" To enable Microsoft extensions, go to the project settings and select the “Compiler” tab. On this tab, enable the “Enable Microsoft extensions” checkbox.

MSDN

Is the central portal for Windows development. This is a huge collection of materials related to application development using Microsoft tools. This is the most comprehensive database, along with documentation for desktop application development and a list of Windows APIs.

Using DLLs in WinAPI C

The Common Controls Library provides access to advanced operating system features such as status bars, progress bars, toolbars, and tabs. These commands are found in commctrl.dll on 16-bit systems and comctl32.dll and are grouped with the user interface.

DLL is a dynamic link library file format used to store multiple codes and procedures for Windows programs. DLL files were created in such a way that multiple programs could use their information at the same time, helping to save memory. Allows the user to edit the coding of several applications at once without changing them. DLLs can be converted to static ones using MSIL Disassembler or DLLs for Lib 3.00.

WinAPI, as an application programming interface for Windows, offers many powerful features that allow you to create your own programs, from simple file processing to building a graphical interface for programming low-level device drivers.

Before you start programming in WinAPI, you need to set up your code environment on Windows. Since it is not a Linux distribution, it does not have a built-in compiler for creating applications. Consider the following options for compiling the code:


A development kit is available for Windows that provides documentation and tools to enable developers to create software using the API and related technologies.

An abbreviation for API, Application Programming Interface (API) is simply some ready-made set of functions that application developers can use. In general, this concept is equivalent to what was previously more often called a subroutine library. However, most often API refers to some special category of such libraries.

During the development of almost any fairly complex application (MyApplication) for the end user, a set of specific internal functions is formed that are used to implement this particular program, which is called the MyApplication API. It often turns out that these functions can also be effectively used to create other applications, including by other programmers. In this case, the authors, based on the strategy for promoting their product, must decide the question - do they open access to this set for external users or not? If the answer is positive, in the description of the software package, as its advantage, the phrase appears that “the package includes an open set of API functions.”

Thus, most often an API refers to a set of functions that are part of one application, but are also available for use in other programs. For example, Excel, in addition to its end-user interface, has a set of Excel API functions that can be used, in particular, when creating applications using VB.

Accordingly, the Windows API is a set of functions that is part of the operating system itself and at the same time accessible to any other application. And in this regard, the analogy with the BIOS/DOS system interrupt set, which is actually a DOS API, is quite justified.

The difference is that the composition of the Windows API functions, on the one hand, is much wider compared to DOS, on the other hand, it does not include many of the tools for directly managing computer resources that were available to programmers in the previous OS. In addition, calls to the Windows API are performed using ordinary procedural calls, and calls to DOS functions are performed through a special processor instruction called Interrupt.

Win16 API and Win32 API

As you know, the change from Windows 3.x to Windows 95 marked the transition from 16-bit operating system architecture to 32-bit. At the same time, the 16-bit Windows API (Win16 API) was replaced with a new 32-bit version (Win32 API). In this case, you just need to keep in mind that, with a few exceptions, the Win32 API set is the same for the Windows 9x and Windows NT families.

When getting acquainted with the Win API, you discover that many built-in functions are nothing more than calls to the corresponding system procedures, but only implemented in the form of the syntax of a given language. Taking this into account, the need to use the API is determined by the following options:

API functions that are entirely implemented as built-in functions. However, sometimes in this case it is useful to switch to using the API, since this can sometimes significantly improve performance (in particular, due to the absence of unnecessary transformations of passed parameters).

Built-in functions implement only a special case of the corresponding API function. This is a fairly common option.

A huge number of API functions have no analogues at all in the current version of compilers. For example, you cannot delete a directory using VB - for this you need to use the DeleteDirectory function.

It should also be emphasized that some API functions (their share in the Win API is very small) cannot be called from programs due to a number of language limitations, for example, the inability to work with memory addresses. But in some cases, non-trivial programming techniques can help (in particular, in the case of the same addresses).

Win APIAndDynamic Link Library (DLL)

The Win API set is implemented in the form of dynamic DLLs.

In this case, by DLL we mean the traditional version of binary dynamic libraries, which provide applications with direct access to the necessary procedures - subroutines or functions (much like what happens when calling procedures within a project). Such libraries can be created using different tools - VC++, Delphi, Fortran, Assembler.

Dynamic library files usually have a .DLL extension, but this is not necessary. For Win16, the .EXE extension was often used; external device drivers are designated using .DRV.

It is difficult to determine the exact number of Windows API functions and the files that contain them (but they are all located in the system directory). In this regard, it is better to highlight the composition of the libraries that make up the core of the operating system, and the main libraries with key additional functions.

Win32 API libraries of the Windows 95/98 operating system kernel:

KERNEL32.DLL: low-level functions for managing memory, tasks and other system resources;

USER32.DLL: This is mainly where the user interface control functions are located;

GDI32.DLL: Graphics Device Interface library - various functions for output to external devices;

COMDLG32.DLL: Functions related to the use of general purpose dialog boxes.

Main libraries with extension functions:

COMCTL32.DLL: A set of additional Windows controls, including Tree List and Rich Text;

MAPI32.DLL: functions for working with email;

NETAPI32.DLL: controls and network functions;

ODBC32.DLL: the functions of this library are needed to work with various databases via the ODBC protocol;

WINMM.DLL: System media access operations.

This article is addressed to those who, like me, are new to C++ programming and who, by chance or desire, decided to learn WinAPI.
I want to warn you right away:
I don't claim to be a C++ or WinAPI guru.
I'm just learning and I want to give here some examples and tips that make it easier for me to learn the functions and mechanisms of WinAPI.

In this article, I assume that you have already become sufficiently familiar with C++ to be able to create classes and overload various operators for them, and that you have already “hidden” some of your mechanisms in the class.

Creating and using the console

To debug a Win32 application or just to see how it all happens inside, I always use the console.
Since you are creating a GUI application and not a console application, the console is not connected. In order to call it, this code was found in the depths of the Internet

If (AllocConsole())
{



std::ios::sync_with_stdio();
}
For convenience, I advise you to wrap it in a function. For example:
void CreateConsole()
{
if (AllocConsole())
{
int hCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 4);
*stdout = *(::_fdopen(hCrt, "w"));
::setvbuf(stdout, NULL, _IONBF, 0);
*stderr = *(::_fdopen(hCrt, "w"));
::setvbuf(stderr, NULL, _IONBF, 0);
std::ios::sync_with_stdio();
}

The called console works only in output mode and it works the same as in console applications. Output information as usual - cout/wcout.
For this code to work, you must include the following files in the project:
#include
#include #include
and include the std namespace in the global namespace:
using namespace std;
Of course, if you don’t want to do this, then just add std:: to all the entities that are in it.

Inheritance of objects for output and arithm. operations

When creating and studying the “windows” themselves, I always needed to output some value to the console.
For example:
You get the size of the window's client area using the GetClientRect function, where the address of a RECT structure object is passed as a parameter to fill this object with data. If you need to know the size of the received client area, you can simply display it in the already connected console

Cout<

But doing this every time (especially if you often have to do something like this) is very inconvenient.
This is where inheritance comes to our aid.
Create a class that openly inherits from the RECT structure and overload the output operator<< так, как вам угодно.
For example:

Class newrect:public RECT
{
public:
friend ostream& operator<<(ostream &strm,newrect &rect)
{
strm<<"Prtint RECT object:\n";
strm<return strm;
}
};

Now just output the object using cout/wcout:

Cout<

And everything will be displayed to you in a convenient form as you require.
You can do the same with any operators you need.
For example, if you need to compare or assign structures (for example, RECT or POINT), overload operator==() and operator=() respectively.
If you want to implement the less than operator< что бы быстро сравнивать размеры окна и т.д. перегрузите operator<().
You can do this, I suppose, with almost any structure, and the most important thing is that all functions that work with a regular RECT structure object will work just as well with its heir.
And I also recommend putting all this beauty into a separate included file and using it if necessary.

Your class

I don’t know about others, but since I’m completely green, I decided to create a new project for each function I studied or for each chapter/sub-chapter of a book, so that everything would be on the shelves and I could come back at any time and refresh my memory of the necessary points .
Since in WinAPI, even to create the simplest window, you need to fill out a class structure, register it and write a trivial window procedure, after the third or fourth project I remembered that I was still writing in C++.
In the end, I hid everything in a simple classroom. The window handle, its name, class name, address of the window procedure, window class (WNDCLASS) are all hidden in the private section of the class.
To obtain them, it is enough to describe simple Get methods, for example:
HWND GetHWND()
LPCTSTR GetClsName() etc.
Filling and registering a window class, creating the window itself and displaying it are done in the constructor.
For convenience, you can overload the constructor, and hide the filling and registration of the window class in a separate private class function and call it in each of the constructors. The convenience of overloading is that sometimes I need to create a very simple window and I call the constructor with two parameters - the window name and the application hinstance.
Other times I need to create a window with a special size, not with the default window procedure and with some other specific style - I call the constructor with the accompanying parameters.
I have this class defined in a separate include file, which is located in the include folder of the IDE.
A template for this class:
class BaseWindow
{
WNDCLASSEX_wcex;
TCHAR_className;
TCHAR_windowName;
HWND_hwnd;
bool _WindowCreation();
public:
BaseWindow(LPCTSTR windowName,HINSTANCE hInstance,DWORD style,UINT x,UINT y,UINT height,UINT width);
BaseWIndow(LPCTSTR windowName,HINSTANCE hInstance);
const HWND GetHWND()const(return HWND;)
LPCTSTR GetWndName()const(return _windowName;)
};

Once you have thoroughly thought through and written such a class, you will make your life easier and will spend more time learning and honing your skills than writing the same thing every time. Moreover, I think it is very useful to create such a class yourself and supplement it as necessary.

P.S.

Everything described is true for:
Platform - Windows 7 32 bit
IDE - Visual Studio 2010
Maybe these tips will cause laughter and irony for some, but still, we were all once beginners/trainees/juniors in something.
Please treat this post with understanding. Constructive criticism is, of course, welcome.

Disclaimer

It would seem that WinAPI is becoming a thing of the past. There have been a huge number of cross-platform frameworks for a long time, Windows is not only on desktops, and Microsoft itself does not welcome applications that use this monster into its store. In addition to this, there are thousands of articles on how to create windows using WinAPI, not only here, but throughout the Internet, at a level from preschoolers and above. This whole process is no longer even broken down into atoms, but into subatomic particles. What could be simpler and clearer? And here I am...

But not everything is as simple as it seems.

Why about WinAPI now?

At one point, while studying the guts of one of the games in a very good one, I thought: This seems to be a good emulsion, but the debugger does not have such a simple thing as navigating through the keyboard buttons, which is available in any normal debugger.

What am I talking about? And here is a piece of code:

Case WM_KEYDOWN: MessageBox(hwndDlg,"Die!","I"m dead!",MB_YESNO|MB_ICONINFORMATION); break;
Thus, the authors wanted to add keyboard support, but the harsh reality of the depths of the architecture of dialog boxes in Windows severely suppressed such initiative. Have anyone who used an emulator and a debugger in it ever seen this message?
What's the problem?

The answer is: you can’t do that!

And, returning to the original question about WinAPI: a lot of popular, and not so popular, projects continue to use it at the present time, because Many things cannot be done better than using a pure API (here you can endlessly give analogies like comparing high-level languages ​​and assembly language, but that’s not about that now). And who knows why? They just use it and that’s it.

About the problem

Dialog boxes make working with the GUI easier, while at the same time depriving us of the opportunity to do something ourselves. For example, WM_KEYDOWN/WM_KEYUP messages coming to the window procedure are “eaten” in the depths of DefDlgProc, taking on such things as: Tab navigation, processing the Esc, Enter keys, etc. In addition, dialogs do not need to be created manually: it’s easier, after all, to sketch buttons, lists in the resource editor, call CreateDialog/DialogBox in WinMain and you’re done.

It's easy to get around such minor troubles. There are at least two completely legal ways:

  1. Create your own class via RegisterClassEx and grab WM_KEYDOWN in the class processing procedure and redirect it to the dialog processing procedure itself. Yes Yes! You can create dialogs with your own class, and VS's built-in editor even lets you specify a class name for the dialog. But who knows about this and uses it?
    The disadvantage is obvious: You need to register one more class, have 1 more CALLBACK procedure, the essence of which will only be to broadcast a couple of messages. Besides, we won't know Where broadcast them, and you will have to fence in crutches.
  2. Use the built-in accelerator mechanism. And we don't even have to change the dialog procedure code! Well, maybe add one line to switch/case, but more on that below.

Tutorials?

I'm not afraid to say that All tutorials on creating windows via WinAPI begin with such simple code, denoting it as a “message processing loop” (I will omit the details of preparing the window class and other bindings):

While (GetMessage(&msg, nullptr, 0, 0)) ( TranslateMessage(&msg); DispatchMessage(&msg); )
It's really simple here:

  1. GetMessage() grabs the next message from the queue, and key moment: Blocks the thread if the queue is empty.
  2. TranslateMessage() from WM_KEYDOWN/WM_KEYUP generates WM_CHAR/WM_SYSCHAR messages (they are needed if someone wants to make their own text editor).
  3. DispatchMessage() dispatches a message to the window procedure (if one exists).
Let's start with the fact that this code is dangerous to use, and here's why. Please note the footnote:
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage(lpMsg, hWnd, 0, 0)) ...
And below is an example of a correct loop.

It is worth saying that in VS templates for Win32 applications, this is exactly what is written wrong cycle. And this is very sad. After all, few people will delve into what the authors themselves did, because this is a priori correct. And incorrect code multiplies along with bugs that are very difficult to catch.

After this code fragment, as a rule, a story about accelerators follows, and a couple of new lines are added (taking into account the remark in MSDN, I suggest immediately writing the correct cycle):

HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(msg.hwnd, hAccel, &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg) ; ) )
This is the option I've seen most often. And he ( ta-dam) is wrong again!

First, about what has changed (then about the problems with this code):

In the first line, a table of keys is loaded from the resources; when pressed, a WM_COMMAND message will be generated with the corresponding command id.

Actually, TranslateAccelerator does this: if it sees WM_KEYDOWN and the key code that are in this list, then (again the key point) it will generate a WM_COMMAND (MAKEWPARAM(id, 1)) message and send it to the corresponding one for the window handle specified in the first argument , processing procedure.

From the last phrase, I think it became clear what the problem with the previous code was.
Let me explain: GetMessage grabs messages for ALL objects of the “window” type (which include children: buttons, lists, etc.), and TranslateAccelerator will send the generated WM_COMMAND to where? Correct: back to the button/list, etc. But we process WM_COMMAND in our procedure, which means we are interested in receiving it in it.

It is clear that TranslateAccelerator must be called for our created window:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) )
And everything seems to be good and wonderful now: we have analyzed everything in detail and everything should work perfectly.

And again no. :-) This will work correctly as long as we have exactly one window - ours. As soon as a modeless new window (dialog) appears, all the keys that are pressed in it will be translated to WM_COMMAND and sent where? And again correctly: in our main window.

At this stage, I propose not to use crutches to solve this dead-end situation, but propose to consider things that are already less common (or almost never found) in tutorials.

IsDialogMessage

Judging by the name of this function, you might think that for some reason it determines whether a given message belongs to the dialogue or not. But first of all, why do we need to know this? And secondly, what should we do next with this information?

In fact, it does a little more than its name suggests. Namely:

  • Navigates through child controls using the Tab/Shift+Tab/up/down/right/left buttons. Plus one more thing, but that's enough for us
  • By pressing ESC it generates WM_COMMAND(IDCANCEL)
  • By pressing Enter, it generates WM_COMMAND(IDOK) or clicking on the current button by default
  • Switches the default buttons (the frame of such buttons is slightly brighter than the others)
  • Well, and various other things that make it easier for the user to work with the dialogue
What does it give us? First, we don't have to think about navigation within the window. They will do everything for us anyway. By the way, Tab navigation can be done by adding the WS_EX_CONTROLPARENT style to our main window, but this is clumsy and not so functional.

Secondly, it will make our life easier on all the other points listed on the list (and even a little more).

In general, it is used somewhere in the depths of Windows to ensure the work modal dialog boxes, and is given to programmers to call it for modeless dialogs. However, we can use it anywhere:

Although the IsDialogMessage function is intended for modeless dialog boxes, you can use it with any window that contains controls, enabling the windows to provide the same keyboard selection as is used in a dialog box.
Those. now if we design the loop like this:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( if (!IsDialogMessage(hMainWnd, &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) )
Then our window will have navigation, like in the native Windows dialog. But now we have two disadvantages:

  1. This code will also work well only with one (non-modal) window;
  2. Having received all the advantages of dialog navigation, we are deprived of the charms in the form of WM_KEYDOWN/WM_KEYUP messages (only for the window itself, and not for child controls);
And at this stage, in general, all the tutorials end and the questions begin: How to handle keyboard events in a winapi standard dialog?
This is the first link on Google, but believe me: there are thousands of them. About the proposed solutions (the best of which is to create your own dialog class, which I wrote about above, before subclassing and RegisterHotKey. Somewhere I even saw the “best” way: use Windows Hooks).

It's time to talk about what is not in the tutorials and answers.

As a rule (as a rule! If someone wants more, then you can register your class for dialogs and work like this. And, if someone is interested in this, I can add this to the article) they want WM_KEYDOWN when they want to process a click on a key that will perform a function regardless of the selected control in the window - i.e. some general function for all this particular dialogue. And if so, then why not take advantage of the rich opportunities that WinAPI itself offers us: TranslateAccelerator.

Used everywhere exactly one accelerator table, and only for the main window. Well, really: there is one GetMessage-loop cycle, which means there is one table. Where else should they go?

In fact, GetMessage-loops can be nested. Let's look at the PostQuitMessage description again:

The PostQuitMessage function posts a WM_QUIT message to the thread"s message queue and returns immediately; the function simply indicates to the system that the thread is requesting to quit at some time in the future.
And GetMessage:
If the function retrieves the WM_QUIT message, the return value is zero.
Thus, GetMessage-loop will exit if we call PostQuitMessage in the window procedure. What does it mean?

We can for everyone non-modal windows in our program create their own similar loop. In this case, DialogBoxParam is not suitable for us, because it spins its own cycle and we cannot influence it. However, if we create a dialog via CreateDialogBoxParam or a window via CreateWindow, then we can spin another loop. At the same time, in everyone In such a window and dialog, we must call PostQuitMessage:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( if (!IsDialogMessage(hMainWnd, &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) ) // .... LRESULT CALLBACK WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) ( switch(umsg) ( case WM_MYMESSAGE: ( HWND hDlg = CreateDialog( hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwnd, MyDialogBoxProc); HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR_FOR_MY_DIALOG)); BOOL bRet = 0, fSavedEnabledState = IsWindowEnabled(hwnd); EnableWindow(hwnd , FALSE); // disable parent window, as dialog window is modal while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hDlg, hAccel, &msg)) ( if (!IsDialogMessage(hDlg , &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) ) EnableWindow(hwnd, fSavedEnabledState); // enable parent window. Dialog was closed break; ) ) ) INT_PTR CALLBACK MyDlgProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) ( switch(umsg) ( case WM_CLOSE: ( // EndDialog(hwnd, 0); --DONT DO THAT! // EndDialog is valid ONLY for Modal Dialogs, created with DialogBox(Param) DestroyWindow(hwnd); break; ) case WM_DESTROY: ( PostQuitMessage(0); break; ) // .... ) return 0; )
Please note: now for each new window in our program we can add to processing own accelerator table. WM_QUIT will snatch GetMessage from the loop for the dialog, and the outer loop won’t even see it. Why is this happening?

The fact is that the outer loop “stopped” at the call to DispatchMessage, which called our procedure, which spins its own interior GetMessage loop with the same DispatchMessage. A classic nested call (in this case DispatchMessage). Therefore, the outer loop will not receive WM_QUIT and will not terminate at this stage. Everything will work smoothly.

But this also has its drawbacks:
Each such loop will process messages only for “your” window. We don't know about others here. This means that if another cycle appears somewhere, then all other windows will not receive the necessary processing of their messages by the TranslateAccelerator/IsDialogMessage pair.

Well, it’s time to take all these comments into account and finally write the correct processing of all messages from all windows of our program. I would like to note that below we consider the case for one thread. Because Each thread has its own queue of messages, then for each thread you will have to create your own structures. This is done with very trivial changes in the code.

We do it beautifully

Because the correct formulation of the problem is half the solution, then first you need to pose this very problem correctly.

Firstly, it would be logical that only active the window receives messages. Those. For an inactive window, we will not broadcast accelerators and pass messages to IsDialogMessage.

Secondly, if the accelerator table is not specified for the window, then there is nothing to broadcast; we will simply send the message to IsDialogMessage.

Let's create a simple std::map that will map the window descriptor to the accelerator table descriptor. Like this:

Std::map l_mAccelTable;
And as windows are created, we will add new windows to it with a descriptor to our favorite table (or zero, if such processing is not required).

BOOL AddAccelerators(HWND hWnd, HACCEL hAccel) ( if (IsWindow(hWnd)) ( l_mAccelTable[ hWnd ] = hAccel; return TRUE; ) return FALSE; ) BOOL AddAccelerators(HWND hWnd, LPCTSTR accel) ( return AddAccelerators(hWnd, LoadAccelerators( hInstance, accel)); ) BOOL AddAccelerators(HWND hWnd, int accel) ( return AddAccelerators(hWnd, MAKEINTRESOURCE(accel)); ) BOOL AddAccelerators(HWND hWnd) ( return AddAccelerators(hWnd, HACCEL(NULL)); )
Well, after closing the window, delete it. Like this:

Void DelAccel(HWND hWnd) ( std::map ::iterator me = l_mAccelTable.find(hWnd); if (me != l_mAccelTable.end()) ( if (me->second) ( DestroyAcceleratorTable(me->second); ) l_mAccelTable.erase(me); ) )
Now, as we create a new dialog/window, call AddAccelerators(hNewDialog, IDR_MY_ACCEL_TABLE). How to close: DelAccel(hNewDialog).

We have a list of windows with the necessary descriptors. Let's slightly modify our main message processing loop:

// ... HWND hMainWnd = CreateWindow(...); AddAccelerators(hMainWnd, IDR_ACCELERATOR); BOOL bRet = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!HandleAccelArray(GetActiveWindow(), msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) // ...
Much better! What's in HandleAccelArray and why is GetActiveWindow() there?

A little theory:

There are two functions that return a handle to the active window: GetForegroundWindow and GetActiveWindow. The difference between the first and the second is quite clearly described in the description of the second:

The return value is the handle to the active window attached to the calling thread"s message queue. Otherwise, the return value is NULL.
If the former will return a handle to any window in the system, then the latter will only return which uses our thread's message queue. Because We are only interested in the windows of our thread (and therefore those that will end up in our message queue), so we’ll take the latter.

So, HandleAccelArray, guided by the descriptor passed to it for the active window, searches for this very window in our map, and if it is there, sends this message for broadcast to TranslateAccelerator, and then (if the first one did not see the right one) to IsDialogMessage. If the latter did not process the message, then we return FALSE to go through the standard TranslateMessage/DispatchMessage procedure.

Looks like that:

BOOL HandleAccelWindow(std::map ::const_iterator mh, MSG & msg) ( const HWND & hWnd = mh->first; const HACCEL & hAccel = mh->second; if (!TranslateAccelerator(hWnd, hAccel, &msg)) ( // message not for TranslateAccelerator. Try it with IsDialogMessage if (!IsDialogMessage(hWnd, &msg)) ( // so, do default stuff return FALSE; ) ) // ok, message translated. Say to message-loop, to get next message return TRUE; ) BOOL HandleAccelArray (HWND hActive, MSG & msg) ( if (!hActive) return FALSE; // no active window. Nothing to do std::map ::const_iterator mh = l_mAccelTable.find(hActive); if (mh != l_mAccelTable.end()) ( // Got it! Try to translate this message for the active window return HandleAccelWindow(mh, msg); ) return FALSE; )
Now each child window has the right to add its favorite accelerator table and calmly catch and process WM_COMMAND with the necessary code.

And what about one more line in the WM_COMMAND handler code?

The description in TranslateAccelerator reads:
To differentiate the message that this function sends from messages sent by menus or controls, the high-order word of the wParam parameter of the WM_COMMAND or WM_SYSCOMMAND message contains the value 1.
Typically the WM_COMMAND processing code looks like this:

Switch(HIWORD(wParam)) ( case BN_CLICKED: // command from buttons/menus ( switch(LOWORD(wParam)) ( case IDC_BUTTON1: DoButton1Stuff(); break; case IDC_BUTTON2: DoButton2Stuff(); break; // ... ) break; ) )
Now you can write like this:

Switch(HIWORD(wParam)) ( case 1: // accelerator case BN_CLICKED: // command from buttons/menus ( switch(LOWORD(wParam)) ( case IDC_BUTTON1: DoButton1Stuff(); break; case IDC_BUTTON2: DoButton2Stuff(); break ; // ... ) break; ) )
And now, returning to the same fceux, adding just one line into the code for processing commands from buttons, we get what we want: control the debugger from the keyboard. It is enough to add a small wrapper around the main message loop and a new accelerator table with the necessary matches VK_KEY => IDC_DEBUGGER_BUTTON.

P.S.: Few people know, but you can create your own accelerator table, and now apply it on the fly.

P.P.S.: Because DialogBox/DialogBoxParam spins its own loop, then when calling the dialog through them, the accelerators will not work and our loop (or loops) will be “idle”.

P.P.P.S.: After calling HandleAccelWindow, the l_mAccelTable map may change, because TranslateAccelerator or IsDialogMessage calls DispatchMessage, and there we may encounter AddAccelerators or DelAccel in our handlers! Therefore, it is better not to touch it after this function.

You can feel the code. The code generated from the standard MS VS 2017 template was taken as a basis.

Tags: Add tags