Windows Programming with
C and C++
Table of contents
Chapter 1
Introduction to Windows Programming
Chapter 2
Basic Windows Concepts
Chapter 3
User Interface Basics
Chapter 4
Graphics and Drawing
Chapter 5
File and Data Management
Chapter 6
Multithreading and Concurrency
Chapter 7
Network Programming
Chapter 8
Advanced Windows Programming
Chapter 9
Debugging and Testing
Chapter 10
Deployment and Distribution
Chapter 1
Introduction to Windows Programming
Windows Operating System
The Windows operating system is a complicated system
made to do many different jobs effectively. It offers a visual
way to interact with the system, allows running multiple
tasks at the same time, and works with many different
hardware and software parts. At its core, Windows is based
on processes and threads. Each application runs as its own
process, and within each process, several threads can work
at the same time.
Key Components
Kernel: The main part of the operating system that handles
important tasks like managing memory, scheduling
processes and threads, and allowing the software to work
with hardware.
User Mode: This is where apps run, including parts like
Win32, . NET, and other tools that apps use.
Hardware Abstraction Layer (HAL): It creates a standard way
for the operating system (OS) to communicate with the
hardware. This allows Windows to work on different types of
hardware without needing any changes.
File System: In charge of arranging and keeping data on
storage devices. NTFS (New Technology File System) is the
main way Windows organizes and stores files.
Device Drivers: Programs that help the operating system
talk to hardware. Every piece of equipment has its own
software that helps it work.
Knowing these parts is important for good Windows
programming. It helps developers work better with the
system and make the most of what it can do.
Differences Between C and C++ in Windows
Programming
C is a programming language that focuses on procedures
and allows direct access to memory and hardware, which
makes it good for working on systems. It is the language
used to create the Windows API, which is the main set of
tools for working with the operating system.
C++ is a version of C that adds features for working with
objects. It helps create code that is easier to organize and
use again, which is helpful for complicated Windows
programs.
Key Differences in Windows Programming
Object-Oriented Programming (OOP):
C++ allows you to use important programming ideas like
keeping data safe (encapsulation), sharing features between
classes (inheritance), and using the same function in
different ways (polymorphism). These features help you
organize big programs and build applications in parts.
C uses a style of programming called procedural
programming, which can be easier to understand. However,
it can become messy when the program gets bigger.
Standard Library:
C++ has something called the Standard Template Library
(STL), which gives you helpful tools for organizing data and
solving problems. This can make many usual programming
jobs easier.
C has a smaller set of built-in tools, which often means you
have to create more data structures and algorithms by
yourself.
RAII (Resource Acquisition Is Initialization):
C++ uses RAII to handle resources, automatically freeing
them when they are no longer needed. This helps stop
wasting resources.
In C, you have to manage resources yourself. This means
you have to clearly set aside memory and free it up when
you're done. If you don't do this carefully, you might end up
with memory leaks.
Choosing Between C and C++
Choosing between C and C++ for Windows programming
usually depends on what the project needs. C is often used
for parts of a system that need to be fast and work closely
with hardware. C++ is usually better for developing
applications where it’s important to be able to easily update
and reuse the code.
Setting Up Your Development Environment
Choosing an IDE
A good Integrated Development Environment (IDE) can
really help you work better by giving you tools to write, fix,
and organize your code. The most popular program for
Windows coding with C and C++ is Microsoft Visual Studio.
Installing Visual Studio
Download: Go to the Visual Studio website and get the
installer.
To install, run the installer and pick the "Desktop
development with C++" option. This will set up the tools
and software needed for developing on Windows.
Settings: After you install Visual Studio, adjust it to your
liking by choosing the theme, font size, and other settings.
Understanding the Development Environment
Solution Explorer: Organizes your projects and files.
Editor: A place where you create and change your code.
Output Window: Shows messages about building and
running programs.
Debugger: Helps find and fix problems in your code.
Setting Up a New Project
Make a New Project: Click on "File," then "New," and choose
"Project. " Pick "Windows Desktop Application" for a
standard GUI app.
Set up your project by giving it a name, choosing a location,
and adjusting other settings like the target system type (x86
or x64).
Project Templates: Visual Studio offers ready-made setups
for various kinds of projects, like console apps, desktop
apps, and DLLs. Pick the one that works best for you.
Building and Running Your Project
Make Code: Use the editor to write the code for your app.
To create your program, click on "Build" and then select
"Build Solution" to turn your code into a working program.
To run your application, click on "Debug" and then select
"Start Without Debugging. "
Understanding the Windows API
What is the Windows API?
The Windows API is a set of functions and tools that
Microsoft gives to help people use the Windows operating
system. It helps developers create windows, handle what
users type, manage memory, and connect to hardware.
Key Components of the Windows API
Kernel32dll: Has tools for managing memory, handling
processes and threads, and reading/writing files.
User32dll: It helps create and manage windows, deal with
messages, and handle user input.
Gdi32dll: Holds tools for graphic tasks, like writing text,
drawing shapes, and displaying images.
Advapi32dll: Offers tools for advanced tasks like managing
security and the system registry.
Using the Windows API in C and C++
To use the Windows API in your programs, add the right
header files and connect to the needed libraries. The most
frequently used headers are windows. h for basic functions
and winuser. h for functions related to the user interface.
Creating a Simple Windows Application
Here is an example of an easy Windows program made
using C:
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam,
lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE
hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const char CLASS_NAME[] = "Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
"Learn to Program Windows",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Explanation of the Code
Adding Headers: The windows. h file is included to use
Windows API functions.
Window Procedure: The WindowProc function handles
messages sent to the window. In this example, it responds
to the WM_DESTROY message to close the app.
WinMain Function: The starting point for a Windows app. It
sets up a window type, makes a window, and starts the
message loop.
To register a window class, you use something called the
WNDCLASS structure. This tells the system about the
window class, including how the window should behave,
what program it's part of, and what the class is named.
Making a Window: The CreateWindowEx function makes a
window with the given class name, title, and style.
Showing the Window: The ShowWindow function makes the
window visible.
Message Loop: The message loop gets and sends messages
to the window.
This basic example shows how a Windows application is set
up. It includes making a window, managing messages, and
running a loop to handle those messages. From here, you
can make more advanced apps by adding new features and
working with other parts of the Windows system.
Chapter 2
Basic Windows Concepts
The Windows Message System
The Windows operating system uses messages to manage
user actions and system events. Every time you move the
mouse or press a key, it creates a message. These
messages are sent to the right handler, which deals with
them as needed.
Message Loop
Every Windows app relies on a message loop to function.
This loop gets messages from the application's message
queue and sends them to the right window functions. Let's
take a closer look at the message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage: Gets a message from the list. It waits until a
message is ready.
TranslateMessage: Changes keyboard key messages into
character messages.
DispatchMessage: Sends the message to the window's
handler.
Window Procedure
The window procedure is a function that takes care of
messages for a window. Each window has its own way of
doing things. This involves handling messages like
WM_PAINT, which helps to draw on the window, and
WM_DESTROY, which helps to free up resources when the
window is closed.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT:
// Handle painting
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam,
lParam);
}
}
Common Messages
WM_CREATE: This message is sent when a window is being
made.
WM_CLOSE: This message is sent when a window is about to
be closed.
WM_PAINT: This message is sent when the window needs to
be redrawn.
WM_DESTROY: This message is sent when the window is
closing.
The WinMain Function
Purpose of WinMain
The WinMain function is where a Windows program starts. It
starts the application, creates the main window, and runs
the message loop.
Structure of WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE
hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Step 1: Register the window class
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "SampleWindowClass";
RegisterClass(&wc);
// Step 2: Create the window
HWND hwnd = CreateWindowEx(
0, // Optional window styles
"SampleWindowClass", // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, // Size and position
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Step 3: Run the message loop
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Parameters of WinMain
hInstance: A reference to the current version of the
application.
hPrevInstance: Always set to NULL in current Windows apps.
lpCmdLine: The command line inputs as one string.
nCmdShow: A marker that indicates if the main window will
be small, big, or displayed normally.
Creating a Simple Window
Registering a Window Class
Before you can make a window, you need to set up a
window class first. This means completing a WNDCLASS
form and then using RegisterClass.
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "SampleWindowClass";
RegisterClass(&wc);
Creating the Window
Use CreateWindowEx to make a window. This function uses
inputs to set up the window's type, name, appearance, size,
and location.
HWND hwnd = CreateWindowEx(
0, // Optional window styles
"SampleWindowClass", // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, // Size and position
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
Showing the Window
After the window is made, use ShowWindow and
UpdateWindow to make it appear.
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
Handling Messages
Set up the window procedure to manage messages that are
sent to the window. This is where you handle actions like
painting the window or shutting it.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam,
lParam);
}
return 0;
}
Handling Messages
Managing messages is an important part of Windows
programming. Every window has a specific way to handle
messages that the system sends to it.
Defining the Window Procedure
The way to handle a window is explained like this:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
hwnd: A label for the window getting the message.
uMsg: The message ID.
wParam: Extra information about the message (specific to
the message).
lParam: Extra information about the message (specific to
the message).
Common Messages and Their Handling
WM_CREATE: Sent when the window is being created.
case WM_CREATE:
// Initialization code
break;
WM_PAINT: Sent when the window needs to be repainted.
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Drawing code
EndPaint(hwnd, &ps);
break;
WM_CLOSE: Sent when the user clicks the close button.
case WM_CLOSE:
DestroyWindow(hwnd);
break;
WM_DESTROY: Sent when the window is being destroyed.
c
Copy code
case WM_DESTROY:
PostQuitMessage(0);
break;
The Default Window Procedure
If a message isn’t handled in the window procedure, it
should be sent to the default window procedure using
DefWindowProc. This makes sure that all messages are
taken care of properly.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
Example: Handling Mouse Clicks
To manage mouse clicks, process the WM_LBUTTONDOWN
message in the window function.
case WM_LBUTTONDOWN:
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
// Handle mouse click at (xPos, yPos)
break;
Example: Handling Keyboard Input
To manage keyboard input, take care of the WM_KEYDOWN
message in the window function.
case WM_KEYDOWN:
switch (wParam) {
case VK_LEFT:
// Handle left arrow key press
break;
case VK_RIGHT:
// Handle right arrow key press
break;
// Handle other keys
}
break;
Advanced Message Handling
To manage messages better, you can use PeekMessage.
This lets you check for messages without stopping your
application, so it can keep doing other tasks while waiting
for new messages.
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
// Perform background tasks
}
}
Conclusion
In this chapter, we talked about the basics of Windows
programming. We learned about the Windows message
system, the WinMain function, how to create a simple
window, and how to deal with messages. These basics give
you a strong base to create more complicated Windows
apps. As you keep learning, you will learn about more
complicated topics like how to use buttons and menus,
graphics, handling files, and doing many things at once.
These all connect to the main ideas we've discussed. By
learning these basic skills, you will be able to make strong
and quick Windows programs using C and C++.
Chapter 3
User Interface Basics
Introduction to Windows Controls
Windows controls, or widgets, are the basic parts that make
up the user interfaces in Windows apps. They have buttons,
text fields, labels, lists, and other things. These controls are
very important for making applications that are easy to use
and interactive.
Types of Windows Controls
Static Controls: Used to show text or pictures that the user
does not need to interact with, like labels.
Button Controls: Used to do things when you click them, like
command buttons.
Edit Controls: These are used for typing text, like single-line
boxes and boxes where you can write multiple lines.
List Controls: These are used to show lists of things, like list
boxes and combo boxes.
Scroll Bars: Used to move through content in a window.
Group Boxes: Used to keep similar controls together.
Creating and Using Controls
You make controls by using the CreateWindow or
CreateWindowEx function and by choosing the right class
name for the type of control you want. For instance, to make
a button:
HWND hButton = CreateWindow(
"BUTTON", // Predefined class; Unicode assumed
"Click Me", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
BS_DEFPUSHBUTTON, // Styles
10, // x position
10, // y position
100, // Button width
30, // Button height
hWnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hWnd,
GWLP_HINSTANCE),
NULL // Pointer not needed.
);
Handling Control Messages
Controls send messages to the main window when things
happen. These messages are handled in the window
process. For example, to manage when a button is clicked:
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
int id = LOWORD(wParam);
if (id == BUTTON_ID) {
// Handle button click
}
}
break;
Working with Buttons, Text Boxes, and Labels
Button Controls
Buttons are one of the most common features in Windows
programs. They are used to do things when the user clicks
on them. You can make buttons using the CreateWindow or
CreateWindowEx function with the BUTTON type.
Creating a Button
HWND hButton = CreateWindow(
"BUTTON", // Button class
"Click Me", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD |
BS_DEFPUSHBUTTON, // Button styles
50, // x position
50, // y position
100, // Button width
30, // Button height
hWnd, // Parent window
(HMENU)ID_BUTTON, // Button ID
hInstance, // Application instance
NULL // Additional application data
);
Handling Button Clicks
Button clicks are managed in the main window's message
handler using the WM_COMMAND message.
case WM_COMMAND:
if (LOWORD(wParam) == ID_BUTTON &&
HIWORD(wParam) == BN_CLICKED) {
// Handle button click
MessageBox(hWnd, "Button clicked!", "Notification",
MB_OK);
}
break;
Text Boxes (Edit Controls)
Text boxes, also called edit boxes, let users type in text.
They can be one line or many lines.
Creating a Single-Line Text Box
HWND hEdit = CreateWindow(
"EDIT", // Edit class
"", // Initial text
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT, // Edit
styles
50, // x position
100, // y position
200, // Edit width
25, // Edit height
hWnd, // Parent window
(HMENU)ID_EDIT, // Edit ID
hInstance, // Application instance
NULL // Additional application data
);
Creating a Multi-Line Text Box
HWND hEdit = CreateWindow(
"EDIT", // Edit class
"", // Initial text
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
ES_MULTILINE | ES_AUTOVSCROLL, // Edit styles
50, // x position
150, // y position
300, // Edit width
100, // Edit height
hWnd, // Parent window
(HMENU)ID_EDIT, // Edit ID
hInstance, // Application instance
NULL // Additional application data
);
Handling Text Box Input
You can get the text from a text box using the
GetWindowText function.
case WM_COMMAND:
if (LOWORD(wParam) == ID_EDIT && HIWORD(wParam)
== EN_CHANGE) {
char buffer[256];
GetWindowText(hEdit, buffer, sizeof(buffer));
// Handle text change
}
break;
Label Controls (Static Controls)
Labels, also called static controls, show text that people
don't need to click on or interact with.
Creating a Label
HWND hLabel = CreateWindow(
"STATIC", // Static class
"Enter text:", // Label text
WS_CHILD | WS_VISIBLE, // Label styles
50, // x position
20, // y position
100, // Label width
25, // Label height
hWnd, // Parent window
NULL, // No menu
hInstance, // Application instance
NULL // Additional application data
);
Labels stay the same and don’t need to send messages to
the main window, so you don't need to do anything extra.
Using Dialog Boxes
Dialog boxes are special windows that ask users for
information or action. They are often used for things like
opening files, saving files, or showing messages.
There are two kinds of dialog boxes: modal and modeless.
Modal dialog boxes need the user to close them before they
can use the main window.
Modeless dialog boxes let users work with both the dialog
box and the main window at the same time.
Creating Modal Dialog Boxes
Modal dialog boxes are usually made with a dialog template
from a resource file and opened using the DialogBox
function.
Dialog Resource Template
IDD_DIALOG1 DIALOGEX 0, 0, 276, 95
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP |
WS_CAPTION | WS_SYSMENU
CAPTION "Sample Dialog"
FONT 8, "MS Shell Dlg"
BEGIN
LTEXT "Enter name:",IDC_STATIC,7,14,50,8
EDITTEXT IDC_EDIT1,7,24,262,12,ES_AUTOHSCROLL
PUSHBUTTON "OK",IDOK,110,74,50,14
PUSHBUTTON "Cancel",IDCANCEL,163,74,50,14
END
Invoking the Dialog Box
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam)
== IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
void ShowDialog(HWND hWnd) {
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
hWnd, DialogProc);
}
Creating Modeless Dialog Boxes
Modeless dialog boxes are made with the CreateDialog
function and need to be displayed using the ShowWindow
function.
Creating and Showing a Modeless Dialog Box
HWND hDlg = CreateDialog(hInstance,
MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DialogProc);
ShowWindow(hDlg, SW_SHOW);
Handling Dialog Box Messages
Dialog boxes manage messages like normal windows, but
their message handling functions give back TRUE if they
deal with a message and FALSE if they don’t.
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam)
== IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Managing Menus and Toolbars
Menus are an important part of the user interface, helping
users find commands and features. Menus are usually found
at the top of the screen and can have smaller menus and
options inside them.
Creating Menus
Menus are set up in a file and are added to the app while it's
running.
Menu Resource Definition
IDR_MENU1 MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", ID_FILE_OPEN
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Help"
BEGIN
MENUITEM "&About", ID_HELP_ABOUT
END
END
Loading and Attaching Menus
Menus are added with the LoadMenu function and
connected to the window using the SetMenu function.
HMENU hMenu = LoadMenu(hInstance,
MAKEINTRESOURCE(IDR_MENU1));
SetMenu(hWnd, hMenu);
Handling Menu Commands
Menu commands are managed in the window procedure
with a WM_COMMAND message.
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_OPEN:
// Handle file open
break;
case ID_FILE_EXIT:
DestroyWindow(hWnd);
break;
case ID_HELP_ABOUT:
MessageBox(hWnd, "About this application",
"About", MB_OK);
break;
}
break;
Overview of Toolbars
Toolbars have buttons that let you quickly use common
commands. They are usually found right under the menu
bar.
Creating Toolbars
You can make toolbars using the CreateWindowEx function
and the TOOLBARCLASSNAME class.
HWND hToolbar = CreateWindowEx(0,
TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE |
TBSTYLE_WRAPABLE, 0, 0, 0, 0, hWnd, NULL, hInstance,
NULL);
Adding Buttons to the Toolbar
You can add toolbar buttons by using the TBADDBUTTONS
message.
TBBUTTON tbButtons[] = {
{ MAKELONG(STD_FILENEW, 0), ID_FILE_NEW,
TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)"New"
},
{ MAKELONG(STD_FILEOPEN, 0), ID_FILE_OPEN,
TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)"Open"
},
{ MAKELONG(STD_FILESAVE, 0), ID_FILE_SAVE,
TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, (INT_PTR)"Save"
}
};
SendMessage(hToolbar, TB_ADDBUTTONS,
sizeof(tbButtons)/sizeof(TBBUTTON), (LPARAM)&tbButtons);
Handling Toolbar Commands
Toolbar commands work like menu commands in the window
procedure by using the WM_COMMAND message.
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_FILE_NEW:
// Handle new file
break;
case ID_FILE_OPEN:
// Handle open file
break;
case ID_FILE_SAVE:
// Handle save file
break;
}
break;
Conclusion
In this chapter, we learned the basics of creating user
interfaces for Windows apps using C and C++. We talked
about important controls such as buttons, text boxes, and
labels. We talked about how to create and use dialog boxes,
menus, and toolbars.
Knowing these basic parts is important for making apps that
are easy to use and interactive. In the next chapter, we will
explore more advanced user interface features and
methods. This includes creating custom controls, managing
input from different devices, and making the user
experience better with animations and visual effects.
Chapter 4
Graphics and Drawing
Introduction to GDI (Graphics Device Interface)
The Graphics Device Interface (GDI) is an important part of
the Windows operating system that helps programs show
images and graphics. It offers tools to create text, shapes,
and pictures on screens and printers.
GDI Components
Device Context (DC): A system that describes a group of
visual elements and their features, along with the settings
that influence how things appear on the screen.
Pens: Used to make lines and shapes.
Brushes: Used to color in shapes.
Fonts: They are used to create letters and words.
Bitmaps: Used for handling pictures.
Getting a Device Context
You get a device context by using the GetDC function for a
window or by using the BeginPaint function when handling
the WM_PAINT message.
HDC hdc = GetDC(hwnd);
After using the device context, make sure to free it by using
ReleaseDC.
ReleaseDC(hwnd, hdc);
Drawing Basics
Drawing Lines and Shapes
Drawing in GDI is done using functions that work with a
device context.
Drawing Lines
You draw lines using the MoveToEx and LineTo functions.
MoveToEx(hdc, 10, 10, NULL); // Move to starting point
LineTo(hdc, 100, 100); // Draw line to endpoint
Drawing Rectangles
You can make rectangles using the Rectangle function.
Rectangle(hdc, 50, 50, 200, 150); // Draw a rectangle from
(50, 50) to (200, 150)
Drawing Ellipses
You can draw ellipses using the Ellipse tool.
Ellipse(hdc, 60, 60, 180, 120); // Draw an ellipse within the
bounding rectangle (60, 60) to (180, 120)
Using Pens and Brushes
Pens and brushes create the look of lines and colors.
Making and Choosing a Pen
Make a pen using the CreatePen function and put it into the
device context with SelectObject.
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); //
Create a solid red pen with a width of 2
SelectObject(hdc, hPen);
Creating and Selecting a Brush
Make a brush with the CreateSolidBrush function and use
SelectObject to add it to the device context.
HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0)); //
Create a solid green brush
SelectObject(hdc, hBrush);
Drawing Text
Text is drawn using the TextOut function.
TextOut(hdc, 10, 10, "Hello, GDI!", strlen("Hello, GDI!")); //
Draw text at (10, 10)
Handling the WM_PAINT Message
The WM_PAINT Message
The WM_PAINT message is sent to a window when it needs
to be redrawn. To work with this message, you need to use
the BeginPaint and EndPaint functions to get and then
release the drawing area.
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Perform drawing operations here
EndPaint(hwnd, &ps);
break;
}
Example: Drawing in the WM_PAINT Handler
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Draw a rectangle
Rectangle(hdc, 50, 50, 200, 150);
// Draw an ellipse
Ellipse(hdc, 60, 60, 180, 120);
// Draw text
TextOut(hdc, 10, 10, "Hello, GDI!", strlen("Hello, GDI!"));
EndPaint(hwnd, &ps);
break;
}
Working with Bitmaps
Loading and Displaying Bitmaps
Bitmaps are often used for pictures in Windows programs.
They can be taken from resources or files and shown using
GDI functions.
Loading a Bitmap from a Resource
HBITMAP hBitmap = LoadBitmap(hInstance,
MAKEINTRESOURCE(IDB_BITMAP1));
Showing a Bitmap
To show a bitmap, you first need to choose it for a memory
area and then use BitBlt to move it to the device where you
want to display it.
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);
BitBlt(hdc, 0, 0, bmpWidth, bmpHeight, hdcMem, 0, 0,
SRCCOPY);
DeleteDC(hdcMem);
Example: Displaying a Bitmap in WM_PAINT
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBITMAP hBitmap = LoadBitmap(hInstance,
MAKEINTRESOURCE(IDB_BITMAP1));
HDC hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);
BITMAP bitmap;
GetObject(hBitmap, sizeof(bitmap), &bitmap);
BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
EndPaint(hwnd, &ps);
break;
}
Advanced Drawing Techniques
Using Regions
Regions are areas made up of shapes like rectangles,
polygons, or ellipses. They are helpful for complicated
cutting and checking actions.
Creating and Using Regions
HRGN hRgn = CreateEllipticRgn(50, 50, 200, 150); // Create
an elliptical region
SelectClipRgn(hdc, hRgn); // Select the region into the
device context for clipping
Double Buffering
Double buffering is a method that helps eliminate flickering
by first drawing images in a hidden area and then quickly
showing them on the screen.
Using Double Buffering
Make a device context and bitmap that work well together.
Draw on the device that works with it.
Show the contents on the screen.
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmMem = CreateCompatibleBitmap(hdc, width,
height);
SelectObject(hdcMem, hbmMem);
// Perform drawing operations on hdcMem
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
Using Alpha Blending
Alpha blending lets you draw with transparency by mixing
two images together using alpha values.
Implementing alpha blending
BLENDFUNCTION blendFunction;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.BlendFlags = 0;
blendFunction.SourceConstantAlpha = 128; // Half
transparency
blendFunction.AlphaFormat = 0;
AlphaBlend(hdc, xDest, yDest, width, height, hdcSrc, xSrc,
ySrc, width, height, blendFunction);
GDI+ for Advanced Graphics
GDI+ is a graphics tool that adds more options to GDI. It
includes things like smooth colors, better image quality, and
the ability to work with more types of images.
Setting Up GDI+
To use GDI+, you need to set it up when your app starts and
turn it off when your app closes.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Perform GDI+ operations
GdiplusShutdown(gdiplusToken);
Drawing with GDI+
GDI+ offers a way to draw using objects. Some common
classes are Graphics, Pen, Brush, and Bitmap.
Drawing Shapes with GDI+
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255)); // Red pen
graphics.DrawLine(&pen, 10, 10, 100, 100); // Draw a line
SolidBrush brush(Color(255, 0, 255, 0)); // Green brush
graphics.FillRectangle(&brush, 50, 50, 150, 100); // Draw a
filled rectangle
Drawing Images with GDI+
Bitmap bitmap(L"example.jpg");
graphics.DrawImage(&bitmap, 0, 0, bitmap.GetWidth(),
bitmap.GetHeight());
Anti-Aliasing
GDI+ helps make the edges of shapes and text smoother so
they look nicer.
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
Conclusion
In this chapter, we have looked at the basic and advanced
methods of graphics and drawing in Windows apps. We've
explained important ideas and provided code examples,
starting from simple drawing with GDI to more complex
tools with GDI+, to help you make great graphical
interfaces. In the next chapter, we will look at how to work
with input from different devices and how to manage user
interactions well to improve the user experience.
Chapter 5
File and Data Management
Introduction to File Handling in Windows
File handling in Windows means doing things with files, like
making them, opening them, writing in them, and
organizing them. The Windows API has tools that help
programs manage files on the computer.
File operation
Making a File: This means starting a new file or opening a
file that already exists if it’s not already there.
Reading from a file means getting the information inside a
file so you can use it.
Writing to a file means changing or adding information to a
file.
Closing a file makes sure that everything is saved correctly
and frees up any resources used.
File Functions in Windows
The main tasks for handling files are CreateFile (to make a
file), ReadFile (to open and read a file), WriteFile (to add or
change information in a file), and CloseHandle (to close the
file when done).
Creating and Opening Files
The CreateFile
The CreateFile function is used to make a new file or open
an existing one. It provides a handle that you can use for
more tasks.
HANDLE hFile = CreateFile(
"example.txt", // File name
GENERIC_READ | GENERIC_WRITE, // Desired access
0, // Share mode
NULL, // Security attributes
CREATE_ALWAYS, // Creation disposition
FILE_ATTRIBUTE_NORMAL, // Flags and attributes
NULL // Template file
);
File Name: The location of the file.
Desired Access: The kind of access you want for the file (like
reading or writing).
Share Mode: Shows how the file can be shared.
Creation Disposition: Decides how the file is made or
accessed.
Flags and Attributes: Shows the file's features (like if it’s a
regular file).
Error Handling
Make sure that CreateFile worked by checking if the handle
is INVALID_HANDLE_VALUE.
if (hFile == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
// Handle error
}
Reading from Files
The ReadFile Function
The ReadFile function reads data from a file into a buffer.
CHAR buffer[1024];
DWORD bytesRead;
BOOL result = ReadFile(
hFile, // File handle
buffer, // Buffer to receive data
sizeof(buffer), // Number of bytes to read
&bytesRead, // Number of bytes read
NULL // Overlapped structure
);
Buffer: The place where data that is read is kept.
Bytes Read: The total amount of data taken from the file.
Checking for Errors
Make sure to see if ReadFile doesn't work by looking at what
it returns, and use GetLastError to find out what the error is.
if (!result) {
DWORD error = GetLastError();
// Handle error
}
Writing to Files
The WriteFile Function
The WriteFile function saves data from a storage area to a
file.
const CHAR *data = "Hello, World!";
DWORD bytesWritten;
BOOL result = WriteFile(
hFile, // File handle
data, // Data to write
strlen(data), // Number of bytes to write
&bytesWritten, // Number of bytes written
NULL // Overlapped structure
);
Data: The temporary storage holding the information that
needs to be written.
Bytes Written: The total amount of data written to the file.
Checking for Errors
Just like when you're reading, check if WriteFile has an error
by looking at the return value, and use GetLastError to get
more details about the error.
if (!result) {
DWORD error = GetLastError();
// Handle error
}
Closing Files
The CloseHandle Function
The CloseHandle function shuts down an open file and frees
up its resources.
CloseHandle(hFile);
Always remember to close file handles after you're done
using them to avoid wasting resources.
File Attributes and Operations
Getting Info About Files
The GetFileAttributes function gets the details about a file or
folder.
DWORD attributes = GetFileAttributes("example.txt");
if (attributes == INVALID_FILE_ATTRIBUTES) {
DWORD error = GetLastError();
// Handle error
}
Changing File Settings
The SetFileAttributes function changes the settings of a file
or folder.
BOOL result = SetFileAttributes("example.txt",
FILE_ATTRIBUTE_READONLY);
if (!result) {
DWORD error = GetLastError();
// Handle error
}
Deleting a File
The DeleteFile function deletes a specified file.
BOOL result = DeleteFile("example.txt");
if (!result) {
DWORD error = GetLastError();
// Handle error
}
Working with Directories
Creating and Removing Directories
Making a Folder: Use CreateDirectory to make a new folder.
BOOL result = CreateDirectory("new_directory", NULL);
if (!result) {
DWORD error = GetLastError();
// Handle error
}
To delete a folder, use RemoveDirectory if the folder is
empty.
BOOL result = RemoveDirectory("new_directory");
if (!result) {
DWORD error = GetLastError();
// Handle error
}
Enumerating Files and Directories
Use the FindFirstFile and FindNextFile functions to list files
and folders.
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile("*.txt", &findFileData);
if (hFind == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
// Handle error
} else {
do {
// Process file or directory
} while (FindNextFile(hFind, &findFileData) != 0);
FindClose(hFind);
}
Working with File Mappings
File mappings let programs read and use the data from a file
directly in memory. This method can make working with big
files or accessing things often faster.
Creating a File Mapping
Use CreateFileMapping to make a file mapping object.
HANDLE hMapping = CreateFileMapping(
hFile, // File handle
NULL, // Security attributes
PAGE_READWRITE, // Protection
0, // Maximum size high
fileSize, // Maximum size low
NULL // Name of the mapping object
);
Mapping a View of the File
Use MapViewOfFile to make a part of the file visible in the
program's memory.
LPVOID pMapView = MapViewOfFile(
hMapping, // Mapping handle
FILE_MAP_ALL_ACCESS, // Desired access
0, // File offset high
0, // File offset low
fileSize // Number of bytes to map
);
Accessing the Mapped File
You can use the pointer from MapViewOfFile to get the file
data.
memcpy(pMapView, "New Data", 8); // Write data to the file
Removing the File Mapping
Use UnmapViewOfFile to remove the file from memory and
CloseHandle to close the mapping object.
UnmapViewOfFile(pMapView);
CloseHandle(hMapping);
Error Handling and Diagnostics
Dealing with Mistakes
Good error handling is very important when managing files
and data. Use GetLastError to get the error number when a
function doesn't work.
DWORD error = GetLastError();
Using Tools to Figure Things Out
Error Codes: Check Windows instructions to understand
error codes.
Debugging: Use tools to follow file activities and find
problems.
Conclusion
In this chapter, we talked about the important parts of
handling files and data in Windows programming. We looked
into making files, reading them, writing in them, and closing
them. We also learned how to manage file details, folders,
and how files relate to each other. Knowing these ideas is
essential for managing files and data well in your apps. In
the next chapter, we will explore multi-threading and
concurrency, looking at ways to improve how well
applications work and respond.
Chapter 6
Multithreading and Concurrency
Introduction to Multithreading
Multithreading is a way of programming that allows many
threads to run at the same time in one program. Threads
use the same resources but run separately. This method can
make applications run better by using multiple processor
cores and providing a smoother experience for users.
Important Ideas
Thread: The smallest part of a program that can run on its
own.
Process: A running program that has one or more threads
inside it.
Concurrency means the system can do many tasks at the
same time.
Advantages of Using Multithreading
Responsiveness: Keeps apps working smoothly by moving
tasks like background work to other places.
Performance: Uses several CPU cores to do calculations at
the same time.
Resource Sharing: Lets threads use memory and resources
together in a process effectively.
Creating and Managing Threads
The CreateThread Function
In Windows, you make threads using the CreateThread
function. This function creates a new thread and gives you a
way to access it.
DWORD WINAPI ThreadFunction(LPVOID lpParam) {
// Thread code here
return 0;
}
HANDLE hThread = CreateThread(
NULL, // Default security attributes
0, // Default stack size
ThreadFunction, // Thread function
NULL, // Parameter to thread function
0, // Default creation flags
NULL // Pointer to thread identifier
);
Thread Function: The task that the thread will do.
Thread Handle: Used to control the thread (for example, to
wait until it finishes).
Waiting for Threads
To wait for a thread to finish, use the WaitForSingleObject
function.
WaitForSingleObject(hThread, INFINITE); // Wait indefinitely
for the thread to complete
"Shutting Down Thread Handles"
Use CloseHandle to close thread handles and free up
resources.
CloseHandle(hThread);
Synchronization Techniques
Synchronization makes sure that several threads can use
shared resources safely, avoiding conflicts or problems with
the data. Common ways to manage synchronization include
locks (mutexes), critical areas, and semaphores.
Important Parts
Critical sections help keep threads in the same process
working together smoothly. They are easier to use and
quicker than mutexes.
Using Critical Sections
Initialize: Initialize a critical section object.
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
Enter: Enter the critical section before accessing shared
resources.
EnterCriticalSection(&cs);
// Access shared resources
Leave: Leave the critical section when done.
LeaveCriticalSection(&cs);
Delete: Delete the critical section when no longer needed.
DeleteCriticalSection(&cs);
Mutexes
Mutexes are used to keep things in order when different
parts of a program are working at the same time. They
make sure that only one thread can use the mutex at a
time.
Using Mutexes
Create: Create or open a named mutex.
HANDLE hMutex = CreateMutex(NULL, FALSE, "MyMutex");
Acquire: Wait for the mutex to be available.
WaitForSingleObject(hMutex, INFINITE);
Release: Release the mutex when done.
ReleaseMutex(hMutex);
Close: Close the mutex handle.
CloseHandle(hMutex);
Semaphores
Semaphores control how many people can use a limited
number of resources. They are used to manage how many
threads can use a resource at the same time.
Using Semaphores
Create: Create a semaphore with an initial count.
HANDLE hSemaphore = CreateSemaphore(NULL, 1, 1,
"MySemaphore");
Wait: Decrement the semaphore count and wait if the count
is zero.
WaitForSingleObject(hSemaphore, INFINITE);
Release: Increment the semaphore count.
ReleaseSemaphore(hSemaphore, 1, NULL);
Close: Close the semaphore handle.
CloseHandle(hSemaphore);
Thread Communication
Threads often need to talk to each other or share
information. This can be done using shared memory,
sending messages, or using synchronization tools.
Using Common Memory
Threads in the same process can directly use shared
memory by using global or static variables. We need to keep
things in sync to avoid messing up the data.
// Shared resource
int sharedData;
void ThreadFunction(LPVOID lpParam) {
EnterCriticalSection(&cs);
sharedData++; // Modify shared resource
LeaveCriticalSection(&cs);
}
Message Passing
Threads can use message queues or ways to communicate
between processes to send messages to each other.
Example: PostMessage Function
PostMessage(hwnd, WM_USER + 1, (WPARAM)wParam,
(LPARAM)lParam);
hwnd: A reference to the window that is getting the
message.
wParam: Information related to a specific message.
lParam: Extra information related to the message.
Managing Thread Lifecycles
Making Threads on the Go
Threads can be started whenever they are needed, which
helps applications use resources better.
HANDLE hThread = CreateThread(NULL, 0, ThreadFunction,
NULL, 0, NULL);
Stopping Threads
Threads should be ended properly to prevent wasting
resources. Only use TerminateThread as a last option.
TerminateThread(hThread, 0); // Not recommended; use
proper synchronization
Thread pools
Thread pools are groups of worker threads that can be used
again for different jobs, which helps make things run better
and use resources more efficiently.
Using the Thread Pool API
HANDLE hPool = CreateThreadpool(NULL);
SetThreadpoolThreadMaximum(hPool, 4);
SetThreadpoolThreadMinimum(hPool, 2);
CreateThreadpool: Makes a group of threads ready to use.
SetThreadpoolThreadMaximum: This sets the highest
number of threads allowed.
SetThreadpoolThreadMinimum: Sets the least number of
threads needed.
Handling Thread Errors
Looking for Mistakes
Always look for mistakes in thread operations by using the
GetLastError function.
DWORD error = GetLastError();
Debugging Threads
Use debugging tools to watch how threads run and find
problems.
Visual Studio Debugger: Lets you check and manage
threads.
Fixing API issues: Use tools like DebugActiveProcess and
DebugSetProcessKillOnExit.
Advanced Multithreading Concepts
Thread Safety
Make sure that shared resources are safe and can be used
properly by different threads at the same time.
Atomic Operations: Use atomic operations to safely update
data when multiple threads are working at the same time.
Lock-Free Data Structures: Use data structures that allow
safe operations without needing to lock them when multiple
threads are working.
Load balancing
Spread the work evenly across threads to get the best
performance.
Task scheduling: Use methods to manage tasks to ensure
workloads are balanced.
Work Stealing: Use work-stealing methods to evenly
distribute tasks.
Real time thread
Real-time threads are designed to meet certain timing
needs for programs that need to respond in a consistent
way.
SetThreadPriority: Change the importance of threads to
meet immediate needs.
Real-Time Scheduling: Use special rules to prioritize tasks
that need to be completed quickly.
Conclusion
In this chapter, we looked at the basic and more advanced
ideas of multithreading and concurrency in Windows
programming. We discussed how to make and control
threads, how to keep them in sync, how they can talk to
each other, and how to manage their lives. By learning and
using these ideas, you can create fast and responsive apps
that make good use of multi-core processors.
Chapter 7
Network Programming
Network programming means making programs that can
talk to each other through a network. It includes various
methods to send and get information between computers or
devices. In Windows, most network programming uses the
Windows Sockets API (Winsock) to connect and
communicate through TCP/IP and other methods.
Main Ideas
Socket: A point used to send or receive data over a network.
Protocol: A group of rules for sharing data, like TCP
(Transmission Control Protocol) and UDP (User Datagram
Protocol).
Client-Server Model: A way to set up a network where a
client asks for help or information, and a server gives it to
them.
Advantages of Network Programming
Communication: Allows programs to share information and
talk to each other across different systems.
Distributed Systems: Help build applications that work
across many computers.
Scalability: Lets apps grow by sharing tasks across different
servers.
The Winsock API
The Windows Sockets API (Winsock) is a tool that helps
programmers create network applications in Windows. It
simplifies the network rules and provides tools to handle
network connections and data transfer.
Starting Winsock
Before you use Winsock functions, start the library with
WSAStartup.
#include <winsock2.h>
#include <ws2tcpip.h>
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
printf("WSAStartup failed: %d\n", result);
return 1;
}
WSAStartup: Starts the Winsock library.
MAKEWORD: Specifies which version of Winsock to use (2. 2
in this example).
Cleaning up Winsock
After finishing network tasks, use WSACleanup to tidy up.
WSACleanup();
Creating a Socket
The Socket Function
Make a socket using the socket function, and choose the
type of network, what kind of socket it is, and the protocol
to use.
SOCKET sock = socket(AF_INET, SOCK_STREAM,
IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
printf("Socket creation failed: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
AF_INET: A type of address for IPv4.
SOCK_STREAM: This is a type of socket used for TCP
connections.
IPPROTO_TCP: The protocol used for the connection.
Connecting a Socket
Connect the socket to a local address and port with the bind
command.
struct sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = htonl(INADDR_ANY);
service.sin_port = htons(27015);
int result = bind(sock, (SOCKADDR*)&service,
sizeof(service));
if (result == SOCKET_ERROR) {
printf("Bind failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
INADDR_ANY: Connects to all available network connections.
htons: Changes the port number to the format used for
network communication.
Listening and Accepting Connections
The Listen Function
Set up the socket to allow incoming connections by using
the listen function.
int result = listen(sock, SOMAXCONN);
if (result == SOCKET_ERROR) {
printf("Listen failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
SOMAXCONN: Specifies the maximum number of pending
connections.
The accept Function
Accept incoming connections with accept.
SOCKET clientSocket = accept(sock, NULL, NULL);
if (clientSocket == INVALID_SOCKET) {
printf("Accept failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
clientSocket: Handle to the newly connected socket.
Connecting to a Server
The Connect Function
For client apps, use "connect" to link up with a server.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(27015);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
int result = connect(sock, (SOCKADDR*)&server,
sizeof(server));
if (result == SOCKET_ERROR) {
printf("Connect failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
Sending and Receiving Data
The send function
Send information to a connected socket using the send
method.
const char* data = "Hello, Server!";
int result = send(sock, data, strlen(data), 0);
if (result == SOCKET_ERROR) {
printf("Send failed: %d\n", WSAGetLastError());
}
The recv Function
Receive data from a socket using recv.
char buffer[512];
int result = recv(sock, buffer, sizeof(buffer), 0);
if (result > 0) {
printf("Received data: %.*s\n", result, buffer);
} else if (result == 0) {
printf("Connection closed\n");
} else {
printf("Receive failed: %d\n", WSAGetLastError());
}
Closing Sockets
The closesocket function
Close the socket when you finish using it for network
communication.
closesocket(sock);
Always close sockets to free up resources and prevent
possible leaks.
Handling Multiple Connections
Using select for managing multiple connections
Select helps you check many sockets to see if they can read
data, send data, or if there are any special issues.
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
int result = select(0, &readfds, NULL, NULL, NULL);
if (result == SOCKET_ERROR) {
printf("Select failed: %d\n", WSAGetLastError());
}
FD_ZERO: Clears the list of file descriptors.
FD_SET: Puts sockets into the group.
Choose: Keeps an eye on the sockets.
Asynchronous I/O with WSAAsyncSelect
For GUI applications, use WSAAsyncSelect to work on
network events asynchronously.
WSAAsyncSelect(sock, hwnd, WM_SOCKET, FD_READ |
FD_WRITE | FD_CLOSE);
hwnd: A window ID that gets messages.
WM_SOCKET: A tag that shows when something happens
with a socket.
Error Handling and Debugging
Error codes
Use WSAGetLastError to get the last error code after a
Winsock function doesn’t work.
DWORD error = WSAGetLastError();
Fixing Problems with Network Connections
Wireshark: Use it to look at and fix network data.
Logging: Set up a way to record network activities and
problems.
Advanced Network Programming
Non-blocking Sockets
Change the sockets to non-blocking mode so they don’t stop
other processes from running.
u_long mode = 1;
ioctlsocket(sock, FIONBIO, &mode);
Using Other Communication Methods Besides TCP/IP
Look into other methods like:
UDP (User Datagram Protocol): Used for sending messages
without needing a connection.
HTTP/HTTPS: For websites and online apps.
FTP: For moving files.
Secure communication
Add security features like:
SSL/TLS: For secure messaging.
Authentication: To make sure only the right people can
access resources.
Conclusion
In this chapter, we talked about the basics of network
programming in Windows using the Winsock API. We looked
into how to make connections, wait for them, send and
receive information, and manage many connections at the
same time. By learning these ideas, you can create strong
networked applications that can communicate well using
different network methods.
Chapter 8
Advanced Windows Programming
Advanced Windows programming uses more advanced
features of the Windows API to build complicated and fast
applications. This chapter talks about more complex
subjects like how different parts of a program can
communicate with each other, how to manage memory,
advanced methods for keeping things in order, and using
special Windows features to make applications better.
Main Ideas
Inter-Process Communication (IPC): Methods for sharing
information between different processes.
Memory Management: Using and freeing up memory
effectively.
Advanced Synchronization: Keeping threads safe and
controlling access when multiple threads are used at the
same time.
Windows Features: Using special Windows tools for better
performance.
Inter-Process Communication (IPC)
IPC helps different programs talk to each other and share
information. Windows has several ways for programs to talk
to each other, like named pipes, shared memory, and
message queues.
Named pipes
Named pipes are a way for programs to talk to each other,
either over a network or on the same computer. They allow
communication in one direction or both ways.
Creating a Named Pipe
Use CreateNamedPipe to create a named pipe.
HANDLE hPipe = CreateNamedPipe(
TEXT("\\\\.\\pipe\\MyPipe"), // Pipe name
PIPE_ACCESS_DUPLEX, // Read/Write access
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE |
PIPE_WAIT, // Pipe mode
1, // Number of instances
1024 * 16, // Output buffer size
1024 * 16, // Input buffer size
0, // Client time-out
NULL // Default security attributes
);
Connecting to a Named Pipe
Use ConnectNamedPipe on the server side and CreateFile on
the client side.
BOOL connected = ConnectNamedPipe(hPipe, NULL);
On the client side:
HANDLE hPipe = CreateFile(
TEXT("\\\\.\\pipe\\MyPipe"), // Pipe name
GENERIC_READ | GENERIC_WRITE, // Desired access
0, // Share mode
NULL, // Default security attributes
OPEN_EXISTING, // Open existing pipe
0, // No flags
NULL // No template file
);
Shared memory
Shared memory lets different programs use the same part
of memory.
Creating a File Mapping Object
Use CreateFileMapping to create a file mapping object.
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // Use system paging file
NULL, // Default security attributes
PAGE_READWRITE, // Read/Write access
0, // Maximum object size (high-order
bits)
1024 * 1024, // Maximum object size (low-
order bits)
TEXT("SharedMemoryName") // Name of the
mapping object
);
Mapping a view of file
Show the file in the area of memory the process can use.
LPVOID pBuf = MapViewOfFile(
hMapFile, // Handle to file mapping object
FILE_MAP_ALL_ACCESS, // Desired access
0, // File offset high
0, // File offset low
0 // Number of bytes to map
);
Message Queues
Message queues let applications talk to each other without
needing to respond right away.
Making a Message Queue
Use CreateQueue or similar functions based on the message
queue system you are using.
Sending and Getting Messages
Use SendMessage and PostMessage to send messages
between windows or threads.
PostMessage(hwnd, WM_USER + 1, (WPARAM)wParam,
(LPARAM)lParam);
hwnd: A reference for the window that is getting the
message.
wParam: Extra information related to the message.
lParam: Extra information related to the message.
Memory Management
Good memory management helps things run well and
makes the best use of resources. It means giving, using, and
removing memory in a smart way.
Dynamic Memory Allocation
Use Windows-specific tools for better control of memory.
Allocating Memory
Heap Functions: Use HeapAlloc to get memory and
HeapFree to release it when you’re done.
HANDLE hHeap = GetProcessHeap();
LPVOID pMemory = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY, 1024);
Virtual Memory Functions: Use VirtualAlloc and VirtualFree
for larger allocations.
LPVOID pMemory = VirtualAlloc(
NULL, // Allocate memory in the address space
of the calling process
1024 * 1024, // Number of bytes to allocate
MEM_COMMIT, // Allocate memory
PAGE_READWRITE // Memory protection
);
Memory-Mapped Files
Memory-mapped files connect the data from a file directly
with a program's memory. This lets the program read and
write to the file as if it were using memory, making it faster
and easier to work with.
Using Memory-Mapped Files
Making a File Mapping Object: Use CreateFileMapping.
To see the file, use MapViewOfFile.
Advanced Synchronization Techniques
Advanced synchronization methods help make sure that
several threads or processes can use shared resources
safely and effectively.
Event Objects
Event objects tell threads to go ahead or to hold on.
Creating and Using Events
HANDLE hEvent = CreateEvent(
NULL, // Default security attributes
FALSE, // Manual reset (FALSE for auto-reset)
FALSE, // Initial state (FALSE for non-signaled)
TEXT("EventName") // Event name
);
WaitForSingleObject(hEvent, INFINITE); // Wait for event to
be signaled
SetEvent(hEvent); // Signal the event
Semaphores
Semaphores manage access to a limited number of
resources.
Creating and Using Semaphores
HANDLE hSemaphore = CreateSemaphore(
NULL, // Default security attributes
1, // Initial count
10, // Maximum count
TEXT("SemaphoreName") // Semaphore name
);
WaitForSingleObject(hSemaphore, INFINITE); // Wait for
semaphore
ReleaseSemaphore(hSemaphore, 1, NULL); // Release
semaphore
Mutexes
Mutexes allow only one thread or process to use a resource
at a time.
Creating and Using Mutexes
HANDLE hMutex = CreateMutex(
NULL, // Default security attributes
FALSE, // Initial owner (FALSE for no initial owner)
TEXT("MutexName") // Mutex name
);
WaitForSingleObject(hMutex, INFINITE); // Wait for mutex
ReleaseMutex(hMutex); // Release mutex
Leveraging Windows-Specific Features
Windows has extra tools to make applications better, like
COM, special Windows API features, and ways to check how
well they are running.
COM
COM is a Microsoft tool that helps create and use software
parts that can be reused.
Using COM
Starting COM: Use CoInitialize or CoInitializeEx.
HRESULT hr = CoInitialize(NULL);
Creating COM Objects: Use CoCreateInstance
to create an instance of a COM object.
ICustomInterface* pInterface = NULL;
hr = CoCreateInstance(CLSID_CustomObject, NULL,
CLSCTX_INPROC_SERVER, IID_ICustomInterface,
(void**)&pInterface);
Uninitializing COM: Use CoUninitialize when done.
CoUninitialize();
Performance Monitoring
Windows offers tools to keep track of how well applications
are running.
Using Performance Monitors
Making and Using Performance Counters: Use Performance
Counter APIs to keep track of system and application data.
PDH_HQUERY hQuery;
PDH_HCOUNTER hCounter;
PdhOpenQuery(NULL, 0, &hQuery);
PdhAddCounter(hQuery, TEXT("\\Processor(_Total)\\%
Processor Time"), 0, &hCounter);
PdhCollectQueryData(hQuery);
Windows API Extensions
Windows 10 UWP: For creating new apps using the Universal
Windows Platform (UWP).
Windows API Functions: Use extra functions from the
Windows API for special tasks.
Conclusion
In this chapter, we looked at more complex ideas in
Windows programming. We talked about how programs can
talk to each other (IPC), how to manage memory, advanced
ways to keep things in sync, and using special Windows
features. Learning these complex subjects will help you
create advanced apps that work better, faster, and have
more features.
Chapter 9
Debugging and Testing
Debugging and testing are important steps in making
software. Debugging means finding and fixing mistakes in
computer code, and testing checks if the software works as
it should and meets what it's supposed to do. In Windows
programming, there are many tools and methods that help
developers check and test their applications properly.
Main Ideas
Debugging: Finding and fixing mistakes in computer code.
Testing: Checking software to make sure it works like it
should.
Debugging Tools: Tools used to find and fix mistakes in
programs.
Testing Methods: Ways to check if software works correctly
and performs well.
Debugging Techniques
Using the Visual Studio Debugger
Visual Studio has a strong built-in tool that helps developers
find and fix problems in their code.
Setting Breakpoints
Breakpoints let you stop the program at a certain line of
code so you can check what's happening in the application.
To add breakpoints, click in the space next to a line of code
or press F9.
Walking Through Code
When you reach a breakpoint, you can go through the code
step by step to see what it does.
Step Into (F11): Use this to go through the code one line at a
time to find problems.
Step Over (F10): Run the current line and go to the next
one.
Step Out (Shift+F11): Leave the current function and go
back to where it was called from.
Examining at Variables
Use the Locals and Watch windows to look at variables and
what their values are.
Locals Window: Displays all local variables and what they
are set to.
Watch Window: Add certain variables or expressions to keep
an eye on their values.
Using the immediate Window
The Immediate Window lets you run commands and check
expressions while you are fixing problems in your code.
? variableName // Displays the value of variableName
Using Debugging APIs
Windows has tools called debugging APIs that help
developers find and fix problems in their programs.
Debugging Tools
OutputDebugString: Sends a message to the debugger.
OutputDebugString(TEXT("Debug message"));
DebugBreak: Forces a breakpoint in the code.
DebugBreak();
Testing Techniques
Unit Testing
Unit testing means checking single parts or functions of a
program on their own to make sure they function properly.
Using Microsoft Unit Test Tools
Visual Studio allows you to do unit testing using the
Microsoft Unit Testing Framework.
Making Test Projects: Add a new test project to your
solution.
Creating Test Methods: Use tags like [TestMethod] to set up
test cases.
[TestMethod]
public void TestMethod1() {
Assert.AreEqual(expectedValue, actualValue);
}
Integration Testing
Integration testing checks if the different parts of the
application work well together.
Testing Interfaces: Make sure how different parts and
outside systems work together.
Creating Real-Life Examples: Use test cases that copy real
user situations.
System Testing
System testing checks the whole software system to make
sure everything works together.
End-to-End Testing: Make sure the app works well from the
beginning to the end.
Testing Against Requirements: Check that the software
meets all the listed needs.
Performance Testing
Performance testing checks how well the application works
in different situations.
Load Testing: Find out how well the application works when
it has a lot of users or tasks at once.
Stress Testing: Check how the application works in very
tough situations.
Using Performance Testing Tools
Visual Studio Load Test: Pretend many users are using the
program at the same time and check how well it works.
Use tools like JMeter or LoadRunner for more complex
situations.
User Acceptance Testing (UAT)
UAT makes sure that the software works well for the people
who will use it.
Getting Users Involved: Let real users try the app in
everyday situations.
Getting Feedback: Ask for opinions and fix any problems or
worries.
Debugging Multithreaded Applications
Challenges in Multithreaded Debugging
It can be hard to fix problems in multithreaded applications
because they run at the same time and can have issues
working together.
Identifying Deadlocks
Deadlocks happen when two or more threads are stuck,
waiting forever for each other to free up resources.
Checking Thread States: Use tools to find deadlocks.
Debugging Tools: Use Visual Studio's tool to see how threads
are working.
Synchronization Issues
Synchronization problems can cause race conditions, where
multiple threads try to use shared resources at the same
time in the wrong way.
Checking Locks: Make sure that locks and synchronization
tools are used properly.
Finding Race Conditions: Use tools and methods to spot and
fix race conditions.
Debugging with Logging
Logging means keeping track of information while a
program is running. This helps to find problems and see how
the program works.
Setting Up Logging
Using the Windows Event Log: Record messages to the
Windows Event Log with the EventLog class.
EventLog.WriteEntry("Application", "Log message",
EventLogEntryType.Information);
File-Based Logging: Write logs to files for persistent storage.
using (StreamWriter writer = new StreamWriter("log.txt",
true)) {
writer.WriteLine("Log message");
}
Analyzing Logs
Checking Log Files: Look at the log files for error messages
and trends.
Using Log Analysis Tools: Use tools to look at and display log
data.
Automated Testing
Benefits of Automated Testing
Automated testing makes testing faster and makes sure the
tests are done the same way every time.
Getting Automated Tests Ready
Making Automated Test Sets: Build automated test sets
using tools like NUnit or MSTest.
Continuous Integration: Add automatic tests to the building
process using tools like Jenkins or Azure DevOps.
Implementing Automated Test Cases
Creating Test Scripts: Make scripts to automate testing
tasks.
Running Tests: Automatically check for issues while
developing or launching.
Debugging and Testing Best Practices
Debugging Best Practices
Reproduce Problems Regularly: Make sure you can recreate
the problems every time so you can fix them properly.
Find Problems: Break down the issues by focusing on the
specific code or part that is causing the problem.
Write down what you find: Keep clear notes of problems and
their answers for later use.
Testing Best Practices
Test Early and Often: Begin testing at the start of the project
and keep testing regularly.
Use real test data: Make sure the test data looks like real-life
situations.
Try to automate tasks whenever you can. This will help you
save time and make fewer mistakes.
Conclusion
In this chapter, we talked about ways to fix problems and
test software that are important for making dependable and
good-quality Windows apps. We looked into using Visual
Studio's debugger, creating small tests and combined tests,
fixing issues with multiple threads, and using logging and
automated testing. Following good methods for finding and
fixing problems, as well as testing, will help you create
strong apps that work well and satisfy users.
Chapter 10
Deployment and Distribution
Deployment and distribution are important parts of making
software. They involve getting the software to users and
handling its setup and updates. Good deployment strategies
make sure that applications are set up correctly and easily
on users' computers, while distribution strategies decide
how the software gets to the people who need it. This
chapter looks at different ways to set up and share Windows
applications effectively.
Main Ideas
Deployment: The process of putting software on users'
computers and setting it up.
Distribution: The way software is provided to users.
Installation Packages: Groups that have the app and
everything needed to install it.
Update Mechanisms: Ways to give updates and fixes to
software.
Deployment Strategies
Installation Packages: How to Set Things Up
Installation packages are complete collections that have
everything needed to set up an app.
Different Types of Installation Packages
MSI (Microsoft Installer): A standard for making installation
packages on Windows.
xml
<Package Id="MyApp" Manufacturer="MyCompany"
UpgradeCode="XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX">
<Product Id="*" Name="MyApp" Language="1033"
Version="1.0.0.0" UpgradeCode="XXXXXXXX-XXXX-XXXX-
XXXX-XXXXXXXXXXXX">
<Package InstallerVersion="500" Compressed="yes" />
</Product>
</Package>
EXE Installers: Files that start the installation process.
Making MSI Packages
Use tools like WiX to make MSI packages.
<Product Id="*" Name="MyApp" Language="1033"
Version="1.0.0.0" Manufacturer="MyCompany"
UpgradeCode="XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX">
<Package InstallerVersion="500" Compressed="yes" />
<Media Id="1" Cabinet="product.cab" DiskId="1" />
<Feature Id="ProductFeature" Title="MyApp" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
Making EXE Installers
You can use tools like Inno Setup or NSIS (Nullsoft Scriptable
Install System) to make EXE installer files.
OutFile "MyAppInstaller.exe"
InstallDir "$PROGRAMFILES\MyApp"
Section
SetOutPath "$INSTDIR"
File "MyApp.exe"
SectionEnd
ClickOnce Deployment
ClickOnce is a Microsoft tool that makes it easy to install
Windows applications without needing much input from
users.
Benefits of ClickOnce
Easy Installation: Users can install apps with just one click.
Automatic Updates: Apps can be updated by themselves.
Setting Up ClickOnce Deployment
Publishing: Use Visual Studio's ClickOnce publish tool to
publish the application.
Deployment: Users download the app from a website link or
a network folder.
Windows Store Deployment
Starting with Windows 8, apps can be shared through the
Microsoft Store.
Packaging for the Store
App Packages: Use Visual Studio to make an AppX package.
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/fou
ndation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/manifest/
phone/windows10"
xmlns:windows="http://schemas.microsoft.com/appx/ma
nifest/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/man
ifest/desktop/windows10">
<Identity Name="MyApp" Publisher="CN=MyCompany"
Version="1.0.0.0" />
</Package>
Submission: Send the package to the Microsoft Store using
the Partner Center.
Distribution Strategies
Direct Distribution
Direct distribution means providing the software so people
can download it or get a physical copy.
Ways of Direct Selling
Website Download: Put the installation file on a website so
users can download it.
<a
href="http://example.com/MyAppInstaller.exe">Download
MyApp</a>
Physical Media: Share software using CDs, DVDs, or USB
drives.
Distribution via Software Repositories
Software repositories are places where you can find, share,
and update software easily.
Examples of Storage Places
Package Managers: Tools like Chocolatey or Windows
Package Manager (winget) help to install and manage
software.
winget install MyApp
Enterprise Repositories: Use internal storage spaces to
share software within a company.
Automated Software Deployment
Automated deployment tools make it easier to install and
update software on different computers.
Tools for Automatic Setup
Microsoft Endpoint Manager: A tool for managing
installations in big businesses.
SCCM (System Center Configuration Manager): For
distributing software on a large scale.
Managing Updates and Patches
Update Mechanisms
Keeping updates in check makes sure users get new
features and fixes for problems.
Different Kinds of Updates
Patch Updates: Small changes that fix specific problems.
Feature Updates: Big changes that add new functions.
Putting in Place Update Processes
Automatic Updates: Use Windows Update or other services
to get updates automatically.
<PackageFamilyName>MyApp_1.0.0.0_x64__8wekyb3d8bb
we</PackageFamilyName>
<Dependencies>
<PackageDependency
Name="Microsoft.NET.Core.Runtime.2.1"
MinVersion="2.1.0.0" />
</Dependencies>
User-Started Updates: Let users choose to check for and
install updates themselves.
Handling Versioning
Using clear version names helps keep track of updates and
ensures everything works well together.
Semantic Versioning: Use version numbers like Major.
MinorPatch
xml
<Version>1.0.0</Version>
Version Control: Keep track of changes and handle different
updates using tools like Git.
Ensuring Successful Deployment
Pre-Deployment Testing
Testing before launching helps make sure that the setup and
the way the app works are right.
Different Kinds of Testing Before Deployment
Installation Testing: Check that the installation works
correctly on different systems.
Compatibility Testing: Make sure it works well with different
operating systems and setups.
Monitoring Post-Deployment
After launching the app, keep an eye on it to make sure it
works properly and fix any problems that come up.
Tools for Watching or Checking
Application Performance Monitoring (APM): Tools such as
New Relic or AppDynamics help you keep an eye on how
well your application is working.
Error Reporting: Set up ways to find and study problems that
happen while the program is running.
User Support
Helping users gives them answers and fixes problems after
the system is set up.
Help Desks: Create a help desk or support team to help
users.
Documentation: Create detailed guides and frequently
asked questions (FAQs) for users.
Best Practices for Deployment and Distribution
Deployment Best Practices
Use automation tools to make processes easier and cut
down on mistakes.
Check Carefully: Make sure to test the installation process
and how the application works very well.
Give simple steps: Provide easy-to-follow steps for setting
up and adjusting.
Distribution Best Practices
Pick the best ways to deliver your message so it reaches the
people you want to reach.
Check and Update: Keep an eye on the distribution process
and share updates when necessary.
Talk to users: Ask for their opinions and quickly help with
any problems they have.
Conclusion
In this chapter, we looked at the methods and tips for
putting Windows applications into use and sharing them. We
talked about different ways to make installation packages,
install software, manage updates, and ensure everything
runs smoothly during the setup. By doing these things, you
can successfully provide your software to users, keep it in
good shape, and fix any problems that come up.