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 shell operating system. Network Services provide access to the network capabilities of Windows OS.

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 software interface 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 features include interprogram communication, process management, computer networks, files, printers, servers and communication ports.

Specification

C WinAPI is an abstract programming interface specification for the operating system Windows systems. 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 version OS and new update packages. 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— 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 designed so that multiple programs could use their information simultaneously, helping to preserve memory. Allows the user to edit the coding of several applications at once without changing them. DLLs can be converted to static libraries 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 programs, from simple file processing to building GUI for programming low-level device drivers.

Before you start programming in WinAPI, you need to set up your code environment on Windows. Because it's not 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.

The Windows API (application programming interface) is a user-mode system programming interface for the Windows family of operating systems. Before 64-bit versions of Windows were released, the programming interface for 32-bit versions of Windows operating systems was called the Win32 API to distinguish it from the original 16-bit version of the Windows API (which served as the programming interface for the initial 16-bit versions of Windows).

The Windows API consists of several thousand callable functions, which are divided into the following main categories:

  • Base Services.
  • Component Services.
  • User Interface Services.
  • Graphics and Multimedia Services.
  • Messaging and Collaboration.
  • Networking.
  • Web Services.

A description of the WindowsAPI can be found in the documentation for the Windows Software Development Kit (SDK). This documentation is available at www.msdn.microsoft.com. It is also included with all subscription levels in the Microsoft DeveloperNetwork (MSDN), a network designed for developers.

Microsoft. NET Framework consists of a class library called the Framework Class Library (FCL) and a managed code runtime, the Common Language Runtime (CLR). The CLR has features such as just-in-time compilation, type checking, garbage collection, and code access security. By offering these features, the CLR provides a development environment that improves programmer productivity and reduces common programming errors.

The CLR is implemented as a classic COM server, the code of which is located in a standard Windows DLL library designed to work in user mode. Virtually all components of the .NET Framework are implemented as standard Windows User-mode DLLs overlaid on top of unmanaged ones Windows functions API. (None of the .NET Framework components run in kernel mode.) The relationship between these components is shown in the figure.

Services, functions and standard programs.

Some terms in Windows user and software documentation have different meanings in different contexts. different meanings. For example, the word service can refer to a standard routine called by the operating system, a device driver, or a service process. What exactly these or other terms mean is shown in the following list:

  • Windows API functions. Documented, callable routines in WindowsAPI. For example, CreateProcess, CreateFile and GetMessage.
  • Native system services (or system calls) . Undocumented core services in the operating system that are called when running in user mode. For example, NtCreateUserProcess is an internal service that the Windows CreateProcess function calls to create a new process.
  • Kernel support functions (or routines). Routines within the Windows operating system that can only be called from kernel mode. For example, ExAllocatePoolWithTag is a routine called by device drivers to allocate memory from Windows system dynamically allocated areas (called pools).
  • Windows Services. Processes started by the Service Control Manager (Windowsservicecontrolmanager). For example, the Task Manager service runs as a user-mode process that supports the at command (similar to the UNIX at or cron commands).
  • DLLs (dynamic-link libraries). A set of callable routines linked together as a binary file that can be loaded dynamically by applications that use the routines. Examples include Msvcrt.dll (a runtime library for applications written in C) and Kernel32.dll (one of the Windows API subsystem libraries). DLLs are widely used by components and Windows applications, which work in user mode. The advantage that DLLs provide over static libraries is that they can be used by multiple applications, and Windows ensures that there is only one copy of the DLL code in memory for those applications that reference the DLL. Note that non-executable .NET assemblies are compiled as DLLs, but without any exported routines. The CLR parses the compiled metadata to access the appropriate types and class members.

History of Win32 API.

Interestingly, Win32 was not intended to be the original programming interface for what was then called Windows NT. Since the Windows NT project was launched as a replacement for OS/2 version 2, the initial programming interface was the 32-bit OS/2 PresentationManagerAPI. But a year after the launch of the project, the product went on sale took off. Microsoft Windows 3.0. As a result, Microsoft changed direction and made Windows NT a future replacement for the Windows family of products rather than a replacement for OS/2. In this regard, the need arose to develop a Windows API specification - before that, in Windows 3.0, the API existed only as a 16-bit interface.

Although the Windows API was intended to introduce many new features not available in Windows 3.1, Microsoft decided to make the new API as compatible in naming, semantics, and data types as possible with the 16-bit Windows API, to ease the burden of porting existing 16-bit Windows -applications in Windows NT. This, in fact, explains the fact that many function and interface names may seem inconsistent: this was necessary to ensure compatibility new Windows API with the old 16-bit Windows API.

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 dialog boxes Windows has strictly suppressed such amateur activities. Have anyone who has 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 message WM_COMMAND (MAKEWPARAM(id, 1)) 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, F ALSE); // 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 here is HandleAccelArray, guided by the descriptor passed to it on active window, looks for this very window in our map, and if it is there, sends this message for broadcast to the TranslateAccelerator, and then (if the first one did not see what it needed) 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

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 X,Y coordinates; new width, height. bRepaint 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 usually used if they want to replace an existing window procedure with their own, and in order not to lose the functionality of the window, 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

API (Application Programming Interface) is an application programming interface, a term often mentioned by software developers. If the application you are developing has a function that allows you to access it from other applications, then this is your application's API. The parameters your function accepts make up its API because they are the means by which other applications interact with the function.

The Windows operating system provides a large set of features that allow various applications, including Visual FoxPro applications, to exchange information with Windows at a fairly low level. These functions are commonly called Windows API. Using the Windows API in Visual FoxPro applications allows you to implement capabilities that are unattainable by standard language tools.

Declaring Windows API Functions in Visual FoxPro

Windows API functions are packaged into dynamically linked libraries (Dynamic Link Library, DLL). As a rule, the files of such libraries have the extension dll. Before you can use a Windows API function in your application, you must announce. To declare a function, use the DECLARE..DLL command:

DECLARE [ cFunctionType] FunctionName IN LibraryName ; [cParamType1 [@] ParamName1, cParamType2 [@] ParamName2, ...]

Command parameters:

cFunctionType
optional parameter, specifies the type of data returned by the function:

cFunctionType Size,
byte
Description
Short 16-bit integer
Integer, Long 4 32-bit integer
Single 4 32-bit real number
Double 8 64-bit real number
String - Character string

FunctionName
name of the function in the DLL. The function name is case sensitive, meaning GetDC and GETDC are completely different function names.

LibraryName
The name of the DLL in which the function is located. For the Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll, and Advapi32.dll libraries, you can use the WIN32API synonym.

AliasName
optional parameter, allows you to use an alias of your choice instead of the function name. The spelling of an alias, unlike a function name, is not case sensitive. Typically, an alias is used when the API name of a function is the same as the name of a built-in (or your) Visual FoxPro function.

cParamType
indicates the data type of the value passed to the function:

The parameter can be passed either by value or by reference. To indicate that the parameter is passed by reference, the "@" symbol is used.

From a Visual FoxPro perspective, there is no difference between the Long and Integer data types. Typically, the Integer type is used to represent signed integers, and the Long type is used to represent unsigned integers.

ParamName
an optional parameter that is purely descriptive and is generally ignored.

All Windows API functions, as well as Windows itself, are written in the C programming language. Therefore, in order to understand how to correctly use API functions in Visual FoxPro (which, by the way, is also written in C, at least its core), let’s get acquainted with what data types are used in C and Windows, and, no less important, let's understand data types such as enumerations, structures and pointers. In addition, you will learn what function prototypes are in C, and how, based on the description of a function prototype in MSDN, it is correct to declare it in the DECLARE..DLL command.

Basic C data types

If you are familiar with the C programming language, then you know how easy it is to create Various types data. It is enough to write the following code:

Typedef int INT32;

and here you have it new type INT32, which fully corresponds to the int type. But from Xi's point of view, this is completely different types, and attempting to assign a variable of type INT32 to the value of a variable of type int will result in an error!

The abundance of data types makes many developers think that programming using APIs is difficult. But that's not true! The following data types are mainly used in C:

    type char - character in ANSI format. It is 8 bits long (one byte).

    type wchar - character in Unicode format. It is 16 bits long (two bytes).

    type int - whole numbers. They are divided into three types in C: int, short int And long int. The latter are usually shortened to short And long. Type short- this is 16-bit, and the types int And long- 32-bit integers.

    type float- real numbers having fractional part. They are 32 bits long (4 bytes).

    type double- double precision real numbers. They are 64 bits long (8 bytes).

    type enum - enumerated data type.

    type void used to denote quantities that have zero length and have no meaning.

    type pointer - pointer; it does not contain information in the conventional sense - like other C types; instead, each pointer contains the address of the memory location where the actual data is stored. It is 32 bits long (4 bytes).

Oddly enough, there is no string type in C. In fact, all strings are represented in C as arrays of characters.

Some types can be declared as unsigned. Modifier unsigned (unsigned) is used with the following data types: char, short, int And long.

For example, the following variable declaration in C:

Signed int variable_name;

means that this variable is a 32-bit unsigned integer.

Modifier const indicates that a variable of the specified type is a constant, that is, its value cannot be changed.

Enum type enum associates a set of named constants called enumerated constants with a variable. An enumerated type declaration looks like this:

Enum tag_field { const1, const2, ... } variable;

If tag_field is omitted, then after the closing curly brace you must specify variable. If tag_field indicated or not indicated variable.

Historically, the enum type is equivalent to the int type - that is, a variable of an enumerated type takes up 4 bytes in memory. Each enumerated constant has a value determined by its serial number in the list; numbering starts from zero. Consider the CombineMode enumeration:

Enum CombineMode( CombineModeReplace, CombineModeIntersect, CombineModeUnion, CombineModeXor, CombineModeExclude, CombineModeComplement );

In this enumeration, the CombineModeReplace constant has the value 0, the CombineModeIntersect constant has the value 1, and so on; the CombineModeComplement constant has the value 5.

The values ​​of enumerated constants can be specified explicitly, as in the following example:

Enum DashCap( DashCapFlat = 0, DashCapRound = 2, DashCapTriangle = 3 );

The data types listed cover 99% of all data types used in Windows API programming. It sounds too simple, doesn't it? Why do API function descriptions contain all these types - HWND, HINSTANCE, POINT and the like?

The reason for this is that C has a feature called strict-typing. Once created, a variable of one type can only take values ​​that correspond to its type. You can't first store a string in a variable and then assign a number to it. In Visual FoxPro, we usually try to simulate this through a naming convention. For example, cName is a character variable, while nCount is a numeric variable. Strict-typing allows you to create a new data type by giving an existing data type a new name. Each new type appears to be different from other types, even though internally they store the same thing.

Windows makes this concept difficult to use. For example, the type LONG is actually a long int, and the type UINT is actually an unsigned int. Both types are 32-bit integers. Derived data types are defined in various include files (files with the .h extension). If you purchased Visual Studio.NET, you can find these files in the folder ..\VC7\PlatformSDK\Include\.

Windows data types

Determining which basic C type actually represents the data type used in a function's API is one of the hardest tasks in API programming. Use the following rule: if you can't find a word float, double, char or str anywhere in a function or parameter name, then it is usually a 32-bit integer. It will take some time to understand and develop skills, but then you will be able to easily convert data types. The following table shows the basic Windows data types and their corresponding types used when declaring a function in Visual FoxPro:

Data type
Windows
Type in ad
functions
Description
BOOL Long 32-bit integer. 0 means false, everything else means true.
BOOLEAN Long same as BOOL.
BYTE String 8-bit integer
CHAR String 8-bit integer
CLSID String
COLORREF Long 32-bit integer
DWORD Long 32-bit integer
DOUBLE Double 64-bit real number
FLOAT Single 32-bit real number
GUID String 128-bit number (16 bytes)
HANDLE Long
HBITMAP Long 32-bit unsigned integer
HDC Long 32-bit unsigned integer
HICON Long 32-bit unsigned integer
HGLOBAL Long 32-bit unsigned integer
HKL Long 32-bit unsigned integer
HLOCAL Long 32-bit unsigned integer
HINSTANCE Long 32-bit unsigned integer
HRESULT Long 32-bit unsigned integer
HWND Long 32-bit unsigned integer
LONG Long 32-bit integer
LPARAM Long 32-bit unsigned integer
SHORT Integer 16-bit integer
SIZE_T Long 32-bit unsigned integer
TCHAR String Corresponds to CHAR for ANSI format strings and WCHAR for Unicode format strings
UCHAR String ANSI character
UINT Long 32-bit unsigned integer
ULONG Long 32-bit unsigned integer
USHORT Integer
UUID String 128-bit number (16 bytes)
VOID No Doesn't matter
WCHAR String UNICODE character
WNDPROC Long 32-bit unsigned integer
WORD Integer 16-bit unsigned integer
WPARAM Long 32-bit unsigned integer

Signposts

Another concept widely used in C is pointers. A pointer is a variable that contains the address of a memory location where data is stored. The type of a pointer is always determined by the type of data it points to; its size is always four bytes. For example, a pointer to a SHORT variable is a 32-bit integer, just like a pointer to any other data type. The description of a pointer, as adopted in Windows API programming, begins with the characters "LP", which means Long Pointer, or "long pointer" working with the 32-bit memory model. This may be followed by a "C" (const) character indicating that the data should not be changed. The following is a description of the data type of the variable whose address is stored in the pointer. For example, LPDWORD is a pointer to type variable DWORD.

When declaring a Windows API function, pointers to numeric data are passed by reference. As an example, consider the GetFileSize function. Here is its prototype (more about function prototypes will be discussed below):

DWORD GetFileSize(HANDLE hFile, // file handle LPDWORD lpFileSizeHigh // pointer);

The second parameter passed to the function is a pointer to a DWORD variable into which the function will place the file size in bytes.

Declaring this function in Visual FoxPro:

DECLARE GetFileSize IN WIN32API Long hFile, Long @ FileSizeHight

As you can see, the FileSizeHight parameter is passed to the function link, because passing by reference is precisely passing a pointer.

The situation with strings is more complicated. As already mentioned, character strings in C are arrays, so in programming API functions the type str, defining an array of characters of type CHAR (respectively, type wstr defines a character array of type WCHAR). The following table shows the types of pointers to character strings:

Pointer type
per line
Description
LPSTR Pointer to a modifiable null-terminated string in ANSI format. Sent via link
LPCSTR A pointer to a non-modifiable null-terminated string in ANSI format. Passed by value
LPTSTR Corresponds to the LPSTR type for ANSI format strings and the LPWSTR type for UNICODE format strings. Sent by reference.
LPCTSTR Corresponds to the LPSTR type for ANSI format strings and the LPWSTR type for UNICODE format strings. Passed by value.
LPWSTR Pointer to a modifiable null-terminated UNICODE string. Sent via link
LPCWSTR Pointer to a non-modifiable null-terminated UNICODE string. Passed by value

Pointers to character data can be passed either by reference or by value - the String type in a function declaration in Visual FoxPro always passes a pointer to a variable containing character data. Use character data passing by reference only when the API function needs to change the value of a parameter.

Structures

A structure can be thought of as a collection of variables of different types that form a single whole. In C, a structure is created using keyword struct followed by an optional tag field(tag) and list elements structures:

Struct tag_field { element_type element1; element_type element2; ..... element_type elementN; };

It is also possible to declare a structure like this:

Struct( element_type element1; element_type element2; ..... element_type elementN; } variable;

This ad does not include tag field and a so-called anonymous structural type is created; This syntax allows one or more variables to be associated with this structure type, as in the following example:

Struct(WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; ) SYSTEMTIME, *PSYSTEMTIME;

Structures are very similar to Visual FoxPro table entries. So, if the table entry personal contains fields fio,address,tlfnumber and email, then the following syntax is used to access the tlfnumber field:

Personal.tlfnumber

This is also what an appeal to a structure field looks like:

SYSTEMTIME.wMinute

Visual FoxPro uses variables that contain strings of characters to form structures. For example, for the SYSTEMTIME structure discussed above, you will need a variable that is 16 bytes long. The first two bytes of this variable contain the value of the field wYear, in the next two bytes - the field value wMonth, in the next two bytes - the field value wDayOfWeek, and so on until the structure is completely formed. And when declaring an API function in Visual FoxPro, the type of the parameter in which the variable containing the structure is passed must be of type String. You will learn how to write numeric data to a string variable a little later.

When programming the Windows API in C, the description of a pointer to a structure begins with the characters LP (Long Pointer), followed by the name of the structure. Thus, a pointer to a SYSTEMTIME structure will be of type LPSYSTEMTIME, a pointer to a POINT structure will be of type LPPOINT, and so on. As you can see, nothing complicated, but thanks to this concept, there are an extremely large number of types of pointers to structures.

If the data in the transferred structure should not change, then a pointer to such a structure is declared like this:

CONST structure_name *

Here, the CONST modifier means that the data in the structure should not change, and the symbol (*) after the structure name means that this entire line is a description of a pointer to the structure. The following example shows a prototype of the CopyRect function, which copies one structure to another:

BOOL CopyRect(LPRECT lprcDst, CONST RECT * lprcSrc);

Description of function prototypes in MSDN

Now that everything has become more or less clear with data types, let’s take a closer look at the C concept of function prototypes.

According to the ANSI standard, all functions in C must have prototypes. The function prototype is quite simple:

return_type function_name( parameter_type(s) parameter_name(s));

If the prototype specifies the VOID type as return_type, this means that the function does not return any values. If the VOID type is specified as parameter_type, this means that the function has no parameters.

You can find information about the Windows API prototypes included in the Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll, and Advapi32.dll libraries on MSDN for Visual Studio.NET by accessing the following Help topics in the Contents section: :

MSDN Library

Windows Development Win32 API SDK Documentacion Reference

In the Reference section, you can view function descriptions by opening one of the following subsections:

Here is another address on MSDN, which also contains information about API functions:

MSDN Library

User Interface Design and Development SDK Documentacion Windows Shell Shell Reference Shell Functions

The following figure shows a fragment of the window help system MSDN:

Here is how, for example, the CopyRect function is described in MSDN:

CopyRect

The CopyRect function copies the coordinates of one rectangle to another.

BOOL CopyRect(
LPRECT lprcDst, // destination rectangle
CONST RECT* lprcSrc// source rectangle
);

Parameters

lprcDst
Pointer to the RECT structure that receives the logical coordinates of the source rectangle.
lprcSrc
Pointer to the RECT structure whose coordinates are to be copied in logical units.

Return Values

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Windows NT/2000/XP: To get extended error information, call GetLastError.

Remarks

Because applications can use rectangles for different purposes, the rectangle functions do not use an explicit unit of measure. Instead, all rectangle coordinates and dimensions are given in signed, logical values. The mapping mode and the function in which the rectangle is used determine the units of measure.

Example Code

For an example, see Using Rectangles.

Requirements

Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.

As you can see, the information is quite comprehensive. The function returns a BOOL type value, two parameters are passed to it: LPRECT type and CONST RECT* - pointers to structures of RECT type. When declaring this function in Visual FoxPro, you must specify that the first parameter is passed by reference and the second by value:

DECLARE Long CopyRect IN User32.dll String @ Dst,String Src

How did I determine that this function is located in the User32.dll library? Very simple. In the recommendations section ( Requirements) the Library item says: Use User32.lib. Substitute for extension lib extension dll- and that’s it! By the way, there, in the Header paragraph, it is reported which include file contains the description of the function prototype.

But that is not all! Since the function works with structures, its description contains a hyperlink to the RECT structure. Click on this hyperlink and a detailed description of the structure will appear on the screen.

Forming structures in Visual FoxPro

The ninth version of Visual FoxPro has significantly expanded the capabilities of the built-in functions BINTOC and CTOBIN. These functions can now be used to convert numeric data into a format suitable for use in structures. Let me remind you that the BINTOC function converts a number to a string, and the CTOBIN function converts a string to a number.

BINTOC function syntax:

BINTOC( nExpression, eFlag)

Of all the possible values ​​that a parameter can take eFlag, we are interested in the following:

CTOBIN function syntax:

CTOBIN(c Expression, eFlag)

Possible parameter values eFlag:

Examples of using these functions are shown below:

CTOBIN(BINTOC(1000.55,"4RS"), "4RS") && Result: 1000 ? CTOBIN(BINTOC(-1000.55,"4RS"), "4RS") && Result: -1000 ? CTOBIN(BINTOC(1000.55,"F"), "4N") && Result: 1000.549987929 ? CTOBIN(BINTOC(-1000.55,"F"), "4N") && Result: -1000.549987929 ? CTOBIN(BINTOC(1000.55,"B"), "8N") && Result: 1000.55 ? CTOBIN(BINTOC(-1000.55,"B"), "8N") && Result: -1000.55

As an example, let's write the RECT structure to a Visual FoxPro variable. This structure is used in the CopyRect function discussed earlier to describe the coordinates of a rectangular area. This is how this structure is described in MSDN:

Typedef struct _RECT ( LONG left; LONG top; LONG right; LONG bottom; ) RECT, *PRECT;

As you can see, the RECT structure contains four fields, each of which stores a LONG value. To generate it in Visual FoxPro, you will need a string 16 bytes long.

Below is the code that declares the CopyRect function, constructs the Dst and Src structures to pass them as parameters to the function, and then copies one structure to the other. The example uses the BINTOC function to convert a number to a string:

DECLARE Long CopyRect IN WIN32API String @ Dst, String Src * Form the Src structure cSrc = BINTOC(nLeft,"4RS") + BINTOC(nTop,"4RS") + ; BINTOC(nRight,"4RS") + BINTOC(nBottom,"4RS") * Prepare space for the Dst structure cDst = REPLICATE(CHR(0),16) nResult = CopyRect(@cDst, cSrc) && Copying

The following example shows how, using the CTOBIN function, you can "parse" a structure, obtaining the numeric values ​​of its fields:

NLeft = CTOBIN(SUBSTR(cDst,1,4), "4RS") && RECT.left nTtop = CTOBIN(SUBSTR(cDst,5,4), "4RS") && RECT.top nRight = CTOBIN(SUBSTR(cDst, 9,4), "4RS") && RECT.right nBottom = CTOBIN(SUBSTR(cDst,13,4), "4RS") && RECT.bottom

Structures containing pointers

Quite often there is a situation when the structure passed to a Windows API function contains pointers. As an example, consider the StartDoc function, which creates a document for printing on a printer. Here is its prototype:

Int StartDoc(HDC hdc, // handle to DC CONST DOCINFO* lpdi// contains file names);

As you can see, the second parameter passed to the function is a pointer to the DOCINFO structure. This is the structure:

Typedef struct(int cbSize; LPCTSTR lpszDocName; LPCTSTR lpszOutput; LPCTSTR lpszDatatype; DWORD fwType; ) DOCINFO, *LPDOCINFO;

The first field of the structure, cbSize, contains the length of the structure in bytes. But the next three fields are pointers to variables containing character data. In particular, the field lpszDocName contains pointer on the line with the name of the document being printed (this is the same document name that you see when viewing the queue of printed documents).

In Visual FoxPro it is quite difficult to create a structure containing pointers. First, you need to allocate a block of memory and get a pointer to it. Secondly, it is necessary to rewrite the value of the Visual FoxPro variable into this memory - thus, we will have a fully implemented pointer mechanism. The last thing left to do is to place the pointer value into the structure. In this case, we need to fulfill one essential requirement: the allocated memory must not be relocatable - otherwise it may turn out that our pointer at some point will point to an area to which our data has nothing to do!

There are several options to obtain a memory block. You can take a “slice” from both Windows shared memory and memory allocated to the process (that is, your application). The second method has higher performance, however, here we will consider the Windows method of working with memory as a simpler one.

The GlobalAlloc function obtains a block of memory of the specified size from Windows and returns a pointer to it. Here is the prototype of this function:

HGLOBAL GlobalAlloc(UINT uFlags, // memory allocation attributes SIZE_T dwBytes// size in bytes);

Parameter uFlags determines how memory will be allocated. MSDN says it can take one of the following values:

From the table it follows that for the parameter uFlags the GPTR value should be used. But how do you know which is this the meaning? Search MSDN for a description of the GlobalAlloc function and in the section Requirements look in which include file its prototype is located. This is the Winbase.h file. It is here that one should look for a description of the values ​​of the constants. Here is a fragment of this file, which defines the constants listed in the table:

/* Global Memory Flags */ #define GMEM_FIXED 0x0000 #define GMEM_MOVEABLE 0x0002 #define GMEM_NOCOMPACT 0x0010 #define GMEM_NODISCARD 0x0020 #define GMEM_ZEROINIT 0x0040 #define GMEM_MODIFY 0x0080 #define GMEM_DISCARDABLE 0x01 00 #define GMEM_NOT_BANKED 0x1000 #define GMEM_SHARE 0x2000 #define GMEM_DDESHARE 0x2000 #define GMEM_NOTIFY 0x4000 #define GMEM_LOWER GMEM_NOT_BANKED #define GMEM_VALID_FLAGS 0x7F72 #define GMEM_INVALID_HANDLE 0x8000 #define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT) #define GPTR (GMEM_FIXED | GMEM_ZEROINIT)

Therefore, GPTR = GMEM_FIXED + GMEM_ZEROINIT = 0x0000 + 0x0040 = 0x0040.

What size should the allocated memory block be? Of course, equal to the length of the line in which the document name is stored. The following example shows the steps from declaring an API function to allocating a block of memory:

uFlags,Long dwBytes cDocumentName = "Name of the document to be printed" && Document name nLenDocumentName = LEN(cDocumentName) && String length hGlobal = GlobalAlloc(GPTR, nLenDocumentName)

The next task is to rewrite the contents of the cDocumentName variable into the resulting memory block. Let's use the built-in Visual FoxPro SYS(2600) function for this. Here is its syntax:

SYS(2600, dwAddress, nLength [, cNewString])

The function behaves differently depending on what parameter is specified cNewString or not.

If the parameter cNewString indicated, then the function copies nLength byte from variable cNewString into memory at the address specified in dwAddress.

If the parameter cNewString not specified, then the function returns nLength byte from memory at the address specified in dwAddress.

As you can see, the parameter dwAddress- This pointer.

Let's write the contents of the cDocumentName string into the memory allocated by the GlobalAlloc function:

SYS(2600, hGlobal, nLenDocumentName, cDocumentName)

Here full code formation of the DOCINFO structure:

#DEFINE GPTR 0x0040 DECLARE Long GlobalAlloc IN WIN32API Long uFlags,Long dwBytes cDocumentName = "Name of the document to be printed" nLenDocumentName = LEN(cDocumentName) hGlobal = GlobalAlloc(GPTR, nLenDocumentName) SYS(2600, dwAddress, nLength [, cNewString]) * We begin to form the DOCINFO structure * cDocInfo is a variable in which the structure cDocInfo = BINTOC(20,"4RS") && DOCINFO is formed. cbSize cDocInfo = cDocInfo + BINTOC(hGlobal,"4RS") && DOCINFO. lpszDocName cDocInfo = cDocInfo + REPLICATE(CHR(0),12) && Other fields of the structure * End of structure formation

The structure is formed in a variable cDocInfo. In the first four bytes we write the number 20 - this is the size of the structure (field cbSize). The next four bytes added to the variable are a pointer to the memory area into which the contents of the variable cDocumentName - the name of the document - are written. Then twelve more zero bytes are added to the variable - these are the fields of the structure lpszOutput, lpszDatatype And fwType, which according to the documentation can be ignored; this means that the fields must have null values. Thus, the result was a string 20 bytes long - which is what was required.

Features of memory use

When using Windows API functions in your applications, you must be aware that Visual FoxPro cannot manage the memory reserved by these functions. Therefore, if you allocate memory, for example, using the GlobalAlloc function, then you must return this memory to Windows after use by calling the GlobalFree function. For every API function that reserves memory, there is an antipode function that releases the reserved memory.

Here is a prototype of the GlobalFree function:

HGLOBAL GlobalFree(HGLOBAL hMem // pointer to a memory block);

The function receives only one parameter - a pointer to a memory block. Here is its declaration and use in Visual FoxPro:

DECLARE Long GlobalFree IN WIN32API Long hGlobal GlobalFree(hGlobal)

here hGlobal is a pointer to the memory block returned by the GlobalAlloc function.

If you forget to reclaim allocated memory, then you risk running into a problem called a memory leak. The consequences of such a leak can be very sad - from a sharp slowdown of your application caused by memory fragmentation to the crash of the operating system.

Passing arrays to a function

In C terms, an array is a variable containing several elements of the same type. Each individual element of the array is accessed using an index. All array elements have the same size; arrays with mixed data types cannot be described. All array elements are stored in memory sequentially, one after the other, with the minimum index value corresponding to the first element, and the maximum value to the last.

A Visual FoxPro array has a completely different organization that allows you to store completely different types of data in its elements. Therefore, it is not possible to pass an array from Visual FoxPro to a Windows API function simply by specifying its name as a parameter. Moreover, the DECLARE..DLL command does not contain a data type such as arrays.

Nevertheless, there is a way out of this situation. As with structures, character variables are used to pass arrays. The numeric data from the array must be converted to a string, which you pass as a parameter to the Windows API function. The following example shows the code for the ArrayToString function, which generates a string from an array. Function gets gets array taArray(link) and flag tlTypeOfValue, indicating how the values ​​of the array elements should be converted - as integers ( tlTypeOfValue=.F.) or real ( tlTypeOfValue=.T.) numbers:

FUNCTION ArrayToString PARAMETERS taArray, tlTypeOfValue EXTERNAL ARRAY taArray LOCAL lnLenArray, lnIndex, lcStruct, lcType lcType = IIF(tlTypeOfValue = .t., "F", "4RS") lnLenArray = ALEN(taArray) && Number of elements in the array lcStruct = "" FOR lnIndex = 1 TO lnLenArray lcStruct = lcStruct + BINTOC(taArray, lcType) ENDFOR RETURN lcStruct ENDFUNC

The function works with both one-dimensional and two-dimensional arrays.

Character data encoding: ANSI and UNICODE formats

In ANSI encoding (used in Visual FoxPro), each character is defined by one byte, that is, the maximum number of characters is 256, which, of course, is very small. For example, during Russification, some standard ANSI characters are replaced with Cyrillic characters. And in some alphabets, for example, Japanese kana, there are so many characters that one byte is simply not enough to encode them. To support such languages, and, more importantly, to make programs easier to "translate" into other languages, Unicode was developed. Each character in Unicode consists of two bytes, which made it possible to expand the set of valid characters to 65536.

A fairly large number of Windows API functions use the Unicode format when working with character strings. To work with such functions, you must convert character data from one format to another.

The built-in Visual FoxPro STRCONV function converts strings from ANSI to UNICODE and vice versa. Here is its syntax:

STRCONV( cExpression, nConversionSetting [, nRegionalIdentifier [, nRegionalIDType]])

Parameter cExpression is the string that needs to be converted. Parameter nConversionSetting indicates the nature of the conversion. Of all its possible values, we are interested in only two:

  • 5 - converting from ANSI to UNICODE
  • 6 - converting from UNICODE to ANSI

Optional Parameters nRegionalIdentifier And nRegionalIDType define additional regional settings and can be safely ignored.

Examples of using the STRCONV function are shown below:

CUnicodeString = STRCONV(cANSIString, 5) && Convert to Unicode cANSIString = STRCONV(cUnicodeString, 6) && Convert to ANSI

You may have gotten the impression from reading this section that working with Windows API functions in Visual FoxPro is fairly easy. Yes and no. For example, some API functions use data types that are not supported by the DECLARE..DLL command, such as 64-bit integers. There are also a number of functions called callback functions. In the prototype of such a function, the CALLBACK modifier is present before the description of the return type. Unfortunately, you can't use such functions, at least not directly. It also happens that the structure contains a pointer to a function - such APIs cannot be used in Visual FoxPro either.