Getting Started with Windows API in C
Introduction
If you’re new to Windows programming in C, this guide will walk you through the essentials. We’ll cover the basics of the Windows API, how to create windows, handle messages, draw graphics, manage user input, and more.
Chapter 1: Introduction to Windows API
What is Windows API?
- Definition and purpose.
- History and evolution.
Basic Concepts
- Difference between Windows API and other APIs.
- Advantages and use cases.
Setting Up the Environment
- Installing and configuring a C compiler (like MinGW or Visual Studio).
- Creating a basic project.
Chapter 2: Windows API Basics
Headers and Libraries
- Common headers (
windows.h
,winuser.h
, etc.). - Linking necessary libraries.
Hello World Program
- Creating a simple Windows application.
- Understanding the WinMain function.
Message Loop
- How Windows processes messages.
- Example of a basic message loop.
Chapter 3: Window Creation and Management
Creating a Window
- Registering a window class.
- Creating a window with
CreateWindowEx
. - Example code for creating a window.
Handling Messages
- Handling window messages in a Window Procedure.
- Common messages like
WM_PAINT
,WM_DESTROY
.
MessageBox Example
- Displaying a simple message box.
Chapter 4: GDI (Graphics Device Interface)
Introduction to GDI
- What is GDI and its purpose.
Drawing Basics
- Drawing shapes (lines, rectangles, ellipses).
- Using device contexts (DCs).
Text Output
- Drawing text on a window.
- Font selection and management.
Chapter 5: User Input
Keyboard Input
- Handling keyboard messages (
WM_KEYDOWN
,WM_KEYUP
). - Example code for keyboard handling.
Mouse Input
- Handling mouse messages (
WM_LBUTTONDOWN
,WM_MOUSEMOVE
). - Example code for mouse handling.
Chapter 6: Dialog Boxes and Controls
Standard Dialog Boxes
- Using common dialogs (open file, save file).
Custom Dialog Boxes
- Creating and displaying custom dialog boxes.
Controls
- Using standard controls (buttons, edit boxes, list boxes).
- Example code for creating and handling controls.
Chapter 7: File I/O
File Handling Functions
- Using
CreateFile
,ReadFile
,WriteFile
. - Example code for basic file operations.
File Management
- Managing file attributes, copying, deleting files.
- Example code for file management tasks.
Chapter 8: Multithreading and Synchronization
Creating Threads
- Using
CreateThread
and thread management functions. - Example code for creating and managing threads.
Synchronization
- Using critical sections, mutexes, and events.
- Example code for synchronizing threads.
Chapter 9: Networking
Windows Sockets (Winsock)
- Introduction to Winsock API.
- Example code for creating a simple client and server.
Network Communication
- Sending and receiving data over the network.
- Example code for basic network communication.
Chapter 10: Advanced Topics
Dynamic-Link Libraries (DLLs)
- Creating and using DLLs.
- Example code for DLL creation and usage.
Chapter 1: Introduction to Windows API
What is Windows API?
The Windows API, also known as WinAPI, is Microsoft’s core set of application programming interfaces available in the Microsoft Windows operating systems. It allows developers to create applications that can interact with Windows components, providing functionalities such as creating windows, handling input, and managing system resources.
History and Evolution
The Windows API has evolved significantly since its inception. Initially introduced with the early versions of Windows, it has expanded and adapted to accommodate new technologies and features, making it the foundation for Windows programming.
Basic Concepts
- Windows API vs. Other APIs: Unlike high-level libraries or frameworks, the Windows API provides direct access to the operating system’s core functionalities, offering more control but also requiring a deeper understanding of the system.
- Advantages and Use Cases: Using the Windows API allows for fine-tuned performance, access to low-level system features, and the ability to create custom solutions not available in higher-level frameworks.
Setting Up the Environment
Before you start coding, you’ll need a suitable development environment.
Installing a C Compiler
For Windows API development, you can use:
- MinGW: A minimalist GNU for Windows, providing a complete GCC (GNU Compiler Collection) toolchain. link
- Visual Studio: Microsoft’s integrated development environment (IDE) with powerful debugging and project management features. link
Creating a Basic Project
- MinGW:
- Install MinGW and add it to your system PATH.
- Create a new directory for your project and create a
main.c
file.
2. Visual Studio:
- Install Visual Studio and create a new “Windows Desktop Application” project.
- Add a new C source file to your project.
Chapter 2: Windows API Basics
Headers and Libraries
To use the Windows API, you need to include specific headers and link against the required libraries.
Common Headers
windows.h
: The main header file that includes most of the Windows API functions.winuser.h
: Contains user interface functions and constants.
Linking Necessary Libraries
When compiling, ensure you’re linking against the correct libraries, such as user32.lib
for user interface functions.
Hello World Program
Creating a simple Windows application involves defining the entry point and setting up a message loop.
Understanding the WinMain Function
The WinMain
function is the entry point for a graphical Windows-based application.
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MessageBox(NULL, "Hello, Windows API!", "Hello", MB_OK);
return 0;
}
This program displays a message box with “Hello, Windows API!”.
Message Loop
The message loop processes messages sent to the application.
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
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, // Optional window styles.
CLASS_NAME, // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
This code sets up a basic window and message loop.
Chapter 3: Window Creation and Management
Creating a Window
Registering a Window Class
To create a window, you first need to register a window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
Creating a Window with CreateWindowEx
After registering the class, you create a window using CreateWindowEx
.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL
);
Example Code for Creating a Window
Here’s the complete example:
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
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;
}
Handling Messages
Handling Window Messages in a Window Procedure
Messages are handled in the Window Procedure function. For example, WM_PAINT
and WM_DESTROY
are common messages.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 5, 5, "Hello, Windows API!", 19);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
MessageBox Example
You can use MessageBox
to display a simple message box.
MessageBox(hwnd, "Hello, Windows API!", "Message Box", MB_OK);
This displays a message box with the specified text.
Chapter 4: GDI (Graphics Device Interface)
Introduction to GDI
The Graphics Device Interface (GDI) is responsible for representing graphical objects and transmitting them to output devices such as monitors and printers.
Drawing Basics
Drawing Shapes
You can draw shapes using functions like LineTo
, Rectangle
, and Ellipse
.
void DrawShapes(HDC hdc) {
// Draw a line
MoveToEx(hdc, 10, 10, NULL);
LineTo(hdc, 100, 100);
// Draw a rectangle
Rectangle(hdc, 120, 10, 220, 100);
// Draw an ellipse
Ellipse(hdc, 240, 10, 340, 100);
}
Using Device Contexts (DCs)
A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
DrawShapes(hdc);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Text Output
Drawing Text on a Window
You can use TextOut
to draw text.
void DrawTextExample(HDC hdc) {
TextOut(hdc, 50, 50, "Hello, GDI!", 10);
}
Font Selection and Management
You can select different fonts using CreateFont
and SelectObject
.
void DrawTextWithFont(HDC hdc) {
HFONT hFont = CreateFont(24, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
HFONT hOldFont = SelectObject(hdc, hFont);
TextOut(hdc, 50, 50, "Hello, GDI with font!", 20);
SelectObject(hdc, hOldFont);
DeleteObject(hFont);
}
Chapter 5: User Input
Keyboard Input
Handling Keyboard Messages
Keyboard messages include WM_KEYDOWN
and WM_KEYUP
.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
DestroyWindow(hwnd);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Mouse Input
Handling Mouse Messages
Mouse messages include WM_LBUTTONDOWN
, WM_MOUSEMOVE
, etc.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_LBUTTONDOWN:
MessageBox(hwnd, "Left mouse button clicked", "Mouse Click", MB_OK);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Chapter 6: Dialog Boxes and Controls
Standard Dialog Boxes
Using Common Dialogs
Windows provides several standard dialog boxes, such as open and save file dialogs.
OPENFILENAME ofn;
char szFile[260];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn) == TRUE) {
MessageBox(hwnd, ofn.lpstrFile, "File Selected", MB_OK);
}
Custom Dialog Boxes
Creating and Displaying Custom Dialog Boxes
You can create custom dialog boxes using resource files and the DialogBox
function.
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hwndDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
void ShowCustomDialog(HINSTANCE hInstance, HWND hwnd) {
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DialogProc);
}
Controls
Using Standard Controls
Controls include buttons, edit boxes, list boxes, etc.
HWND hButton = CreateWindow(
"BUTTON", // Predefined class; Unicode assumed
"OK", // 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
(HMENU) ID_BUTTON_OK, // Control identifier
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
Example Code for Creating and Handling Controls
Here’s a complete example:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
CreateWindow(
"BUTTON", "OK",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10, 100, 30,
hwnd, (HMENU)1, (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);
return 0;
}
case WM_COMMAND:
if (LOWORD(wParam) == 1) {
MessageBox(hwnd, "Button clicked", "Notification", MB_OK);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Chapter 7: File I/O
File Handling Functions
Using CreateFile
, ReadFile
, WriteFile
You can perform file operations using these functions.
HANDLE hFile = CreateFile("example.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
char buffer[100];
DWORD bytesRead;
ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
CloseHandle(hFile);
}
File Management
Managing File Attributes, Copying, Deleting Files
Functions such as GetFileAttributes
, CopyFile
, and DeleteFile
help manage files.
DWORD attributes = GetFileAttributes("example.txt");
if (attributes != INVALID_FILE_ATTRIBUTES) {
// Do something with the attributes
}
CopyFile("example.txt", "copy_of_example.txt", FALSE);
DeleteFile("example.txt");
Example Code for Basic File Operations
Here’s a complete example:
void FileOperationsExample() {
HANDLE hFile = CreateFile("example.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
char buffer[100];
DWORD bytesRead;
ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
CloseHandle(hFile);
}
CopyFile("example.txt", "copy_of_example.txt", FALSE);
DeleteFile("example.txt");
}
Chapter 8: Multithreading and Synchronization
Creating Threads
Using CreateThread
and Thread Management Functions
Threads allow concurrent execution within a program.
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
MessageBox(NULL, "Thread running", "Thread", MB_OK);
return 0;
}
void CreateMyThread() {
HANDLE hThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL);
if (hThread != NULL) {
CloseHandle(hThread);
}
}
Synchronization
Using Critical Sections, Mutexes, and Events
Synchronization objects help manage access to shared resources.
CRITICAL_SECTION cs;
void InitializeCriticalSectionExample() {
InitializeCriticalSection(&cs);
}
void UseCriticalSection() {
EnterCriticalSection(&cs);
// Access shared resource
LeaveCriticalSection(&cs);
}
void DeleteCriticalSectionExample() {
DeleteCriticalSection(&cs);
}
Example Code for Synchronizing Threads
Here’s a complete example:
CRITICAL_SECTION cs;
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
EnterCriticalSection(&cs);
MessageBox(NULL, "Thread running", "Thread", MB_OK);
LeaveCriticalSection(&cs);
return 0;
}
void CreateMyThread() {
InitializeCriticalSection(&cs);
HANDLE hThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL);
if (hThread != NULL) {
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
DeleteCriticalSection(&cs);
}
Chapter 9: Networking
Windows Sockets (Winsock)
Introduction to Winsock API
The Winsock API allows for network communication.
Example Code for Creating a Simple Client and Server
Simple TCP Client
#include <winsock2.h>
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons(80);
connect(sock, (struct sockaddr*)&server, sizeof(server));
char *message = "GET / HTTP/1.1\r\n\r\n";
send(sock, message, strlen(message), 0);
closesocket(sock);
WSACleanup();
return 0;
}
Simple TCP Server
#include <winsock2.h>
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET server_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server, client;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(80);
bind(server_sock, (struct sockaddr*)&server, sizeof(server));
listen(server_sock, 3);
int client_size = sizeof(struct sockaddr_in);
SOCKET client_sock = accept(server_sock, (struct sockaddr*)&client, &client_size);
char *message = "Hello Client\r\n";
send(client_sock, message, strlen(message), 0);
closesocket(client_sock);
closesocket(server_sock);
WSACleanup();
return 0;
}
Network Communication
Sending and Receiving Data Over the Network
You can use send
and recv
functions for data transmission.
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0);
Example Code for Basic Network Communication
Here’s a complete example:
void NetworkCommunicationExample() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons(80);
connect(sock, (struct sockaddr*)&server, sizeof(server));
char *message = "GET / HTTP/1.1\r\n\r\n";
send(sock, message, strlen(message), 0);
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0);
closesocket(sock);
WSACleanup();
}
Chapter 10: Advanced Topics
Dynamic-Link Libraries (DLLs)
Creating and Using DLLs
DLLs allow code to be modular and reusable.
// DLL Main
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// Exported function
__declspec(dllexport) void HelloWorld() {
MessageBox(NULL, "Hello from DLL!", "Hello", MB_OK);
}
Using the DLL
typedef void (*HelloWorldFunc)();
void LoadAndUseDLL() {
HMODULE hLib = LoadLibrary("example.dll");
if (hLib != NULL) {
HelloWorldFunc helloWorld = (HelloWorldFunc)GetProcAddress(hLib, "HelloWorld");
if (helloWorld != NULL) {
helloWorld();
}
FreeLibrary(hLib);
}
}
COM Programming
Introduction to Component Object Model (COM)
COM is a platform-independent, distributed, object-oriented system for creating binary software components.
Example Code for Basic COM Client and Server
COM Server
#include <windows.h>
#include <unknwn.h>
// Sample COM interface
interface ISample : IUnknown {
virtual HRESULT STDMETHODCALLTYPE DoSomething() = 0;
};
// Sample COM class
class Sample : public ISample {
public:
Sample() : refCount(1) {}
// IUnknown methods
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override {
if (riid == IID_IUnknown || riid == __uuidof(ISample)) {
*ppvObject = static_cast<ISample*>(this);
AddRef();
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override {
return InterlockedIncrement(&refCount);
}
ULONG STDMETHODCALLTYPE Release() override {
ULONG count = InterlockedDecrement(&refCount);
if (count == 0) {
delete this;
}
return count;
}
// ISample method
HRESULT STDMETHODCALLTYPE DoSomething() override {
MessageBox(NULL, "Hello from COM!", "COM", MB_OK);
return S_OK;
}
private:
LONG refCount;
};
COM Client
#include <windows.h>
#include <objbase.h>
#include "Sample.h"
int main() {
CoInitialize(NULL);
ISample* pSample;
HRESULT hr = CoCreateInstance(CLSID_Sample, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pSample));
if (SUCCEEDED(hr)) {
pSample->DoSomething();
pSample->Release();
}
CoUninitialize();
return 0;
}
Error Handling and Debugging
Using GetLastError
and FormatMessage
These functions help retrieve and format error messages.
DWORD error = GetLastError();
char buffer[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, sizeof(buffer), NULL);
MessageBox(NULL, buffer, "Error", MB_OK);