Windows API笔记

简介

Windows API

本教程是纯Windows API教程,不包含MFC,使用的是C99标准。

Windows API是创建Windows应用程序的接口,要使用Windows API就要先下载Windows SDk(Software Development Kit,软件开发环境),SDK包括头文件,函数库,样例,文档和工具等。

Windows API是由C和C++写的,被分为以下几个部分:

  • 基础服务
  • 安全
  • 绘图
  • 用户接口
  • 多媒体
  • Windows shell
  • 网络

基础服务提供了文件系统,设备,进程,线程,注册表,以及错误处理等功能。
安全提供了函数,接口,对象和其他的认证,版权等安全相关的元素。
绘图提供了图形输出到屏幕,打印机及其他输出设备的功能。
用户接口提供了创建窗口和部件的功能。
多媒体提供了视频,音频等输出设备。
Windows Shell提供了由操作系统提供的shell功能。
网络提供了网络服务。

Windows API的官方实现是在动态库里(DLLS),例如kenel32.dll,user32.dll,gdi32.dll,shell32.dll是在Windows system目录下。
也有第三方的Windows API:最常见的就是Wine工程和ReactOS工程。
Windows API是一个动态实体,函数的数量每年都在增长。

Pelles C

教程里使用的是Palles C,我用的是VS2017.

MSDN

两个好的参考资料:Desktop App Development DocumentationWindows API list

主函数

main

主函数是C语言编程的一个进入点,然而他不是第一个运行的程序。
在C运行库中,程序开始的第一个函数叫做mainCRTStartup(),他负责初始化内存管理,文件I/O,以及参数调用等,之后mainCRTStartuo()将会调用main(),以下是main函数的函数原型:

int main (void);
int main(int argc,char **argv);
int main(int argc,char *argv[]);

样例:

#include<stdio.h>

int main(void)
{
      puts("This is a classic C Program.");
      return 0;
}

wmain

前面的main函数原型只能接收 ASCII 字符,如果我们想让程序从命令行接收宽字符,就需要使用wmain:

int wmain(void);
int wmain(int argc,wchar_t **argv);
int wmain(int argc, wchar_t *argv[]);

样例;
这个程序在命令行使用宽字符,并输出命令行的第一个参数,众所周知,第0个参数是程序的运行目录。

#include <windows.h>
#include <wchar.h>
#include<clocale>

int wmain(int argc, wchar_t **argv) { //wchar_t类型即宽字符类型

    setlocale(LC_ALL,"zh_CN.UTF-8"); //设置语言环境
    PDWORD cChars = NULL;
    HANDLE std = GetStdHandle(STD_OUTPUT_HANDLE);   //获取标准输出的句柄
    
    if (std == INVALID_HANDLE_VALUE) { //如果我们收到的返回代码是错误值INVALID_HANDLE_VALUE,
        wprintf(L"Cannot retrieve standard output handle\n (%d)",  
            GetLastError()); //就输出一条错误信息,并输出错误代码
    }
 
    if (argv[1]) {
    
        WriteConsoleW(std, argv[1], wcslen(argv[1]), cChars, NULL); //向控制台输出宽字符串
    }
    
    CloseHandle(std); //关闭标准输出的句柄
 
    return 0;
}

如果想要输入命令行参数,可以在VS2017中右侧,解决方案资源量管理器->右键->属性-> 调试->命令参数,输入汉字即可。

_tmain 函数原型

_tmain()函数是微软的扩展函数,通过定义一个UNICODE宏来判断创建main()函数还是wmain()函数。
在过去,要创建这两个函数都需要创建,现在只需要一个宏就解决了,但是目前程序最通用的方式还是UNICODE程序。
以下是_tmain()的函数原型,其中,TCHAR宏用于转换char或者wchar_t,通过UNICODE宏控制:

int _tmain(void);
int _tmain(int argc,TCHAR **argv);
int _tmain(int argc,TCHAR *argv[]);

以下是一个程序样例:

#define _UNICODE  //该宏用于C运行库转换
#define UNICODE  ////该宏用于Windows头文件转换

#include <windows.h> //这个头文件定义了TCHAR宏,TCHAR宏受UNICODE宏影响
#include <tchar.h> //为_tmain和_tcslen宏引入头文件,他们的转换受_UNICODE影响

int _tmain(int argc, TCHAR *argv[]) {

    PDWORD cChars = NULL;
    HANDLE std = GetStdHandle(STD_OUTPUT_HANDLE);

    if (std == INVALID_HANDLE_VALUE) {
        _tprintf(L"Cannot retrieve standard output handle\n (%d)", 
            GetLastError());
    }  
        
    if (argv[1]) {
    
        WriteConsole(std, argv[1], _tcslen(argv[1]), cChars, NULL);
    }
    
    CloseHandle(std);

    return 0;
}

WinMain

以上是控制台主函数,现在我们介绍图形用户界面的主函数:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    PWSTR pCmdLine, int nCmdShow);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow);
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPTSTR lpCmdLine, int nCmdShow);

其中,pCmdLine是命令行参数。

如果进入点是WinMain()函数时,那么程序开始于WinMainCRTStartup();
如果进入点是wWinMain()函数,那么程序开始于wWinMainCRTStartup().

下面是一个样例,在屏幕上显示一个弹窗:

#include <windows.h>

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
              PWSTR szCmdLine, int CmdShow) {
              
   MessageBoxW(NULL, szCmdLine, L"Title", MB_OK);

   return 0;
}

系统函数

系统函数用于接收系统信息并和系统通信。

屏幕尺寸

GetSystemMetrics() 用于查看系统指标和系统设定。
下面是一个例子,用于输出屏幕尺寸:

#include <windows.h>
#include <wchar.h>

#pragma comment(lib, "user32.lib") //编译需要user32.lib库

int wmain(void) {

    int x = GetSystemMetrics(SM_CXSCREEN);
    int y = GetSystemMetrics(SM_CYSCREEN);

    wprintf(L"The screen size is: %dx%d\n", x, y);
    
    return 0;
}

锁屏

#include <windows.h>
#include <wchar.h>

#pragma comment(lib, "user32.lib")

int wmain(void) { 

    int r = LockWorkStation();

    if( r == 0 ) {
    
        wprintf(L"LockWorkStation() failed %d\n", GetLastError());
        return 1;
    }

    return 0;
}

电脑名

GetComputerNameEx()用于检测NETBIOS和DNS名。

#include <windows.h>
#include <wchar.h>

int wmain(void) {

    wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1]; //MAX_COMPUTERNAME_LENGTH定义了电脑名的最大长度
    DWORD size = sizeof(computerName) / sizeof(computerName[0]);

    int r = GetComputerNameW(computerName, &size);

    if (r == 0) {
        wprintf(L"Failed to get computer name %ld", GetLastError());
        return 1;
    }

    wprintf(L"Computer name: %ls\n", computerName);

    return 0;
}

用户名

#include <windows.h>
#include <Lmcons.h> //ULEN定义
#include <wchar.h>

int wmain(void) {

    wchar_t username[UNLEN+1];
    DWORD len = sizeof(username) / sizeof(wchar_t);

    int r = GetUserNameW(username, &len);

    if (r == 0) {
        wprintf(L"Failed to get username %ld", GetLastError());
        return 1;
    }
  
    wprintf(L"User name: %ls\n", username);

    return 0;
}

当前目录

SetCurrentDirectoryW() 用于设置当前目录;GetCurrentDirectoryW()用于获取当前目录。
在下面的样例中,我们输出了当前目录,并通过命令行参数改变了当前目录。

#include <windows.h>
#include <wchar.h>

#define BUFSIZE MAX_PATH

int wmain(int argc, wchar_t **argv) {

    wchar_t buf[BUFSIZE];
    
    if(argc != 2) {

        wprintf(L"Usage: %ls <dir>\n", argv[0]);
        return 1;
    }

    DWORD r = SetCurrentDirectoryW(argv[1]);

    if (r == 0) {

        wprintf(L"SetCurrentDirectoryW() failed (%ld)\n", GetLastError());
        return 1;
    }

    r = GetCurrentDirectoryW(BUFSIZE, buf);

    if (r == 0) {

        wprintf(L"GetCurrentDirectoryW() failed (%ld)\n", GetLastError());
        return 1;
    }

    if (r > BUFSIZE) {

        wprintf(L"Buffer too small; needs %d characters\n", r);
        return 1;
    }

    wprintf(L"Current directory is: %ls\n", buf);

    return 0;
}

Windows版本

通过版本辅助函数可以检测当前的系统版本。

#include <windows.h>
#include <wchar.h>
#include <VersionHelpers.h>

int wmain(void) {

    //if (IsWindows10OrGreater()) {
        
    //    wprintf(L"This is Windows 10+");
    // }
    if (IsWindows8Point1OrGreater()) {
        wprintf(L"This is Windows 8.1+\n");
    } else if (IsWindows8OrGreater()) {
        wprintf(L"This is Windows 8\n");
    } else if (IsWindows7OrGreater ()) {
        wprintf(L"This is Windows 7\n");
    } else if (IsWindowsVistaOrGreater ()) {
        wprintf(L"This is Windows Vista\n");
    } else if (IsWindowsXPOrGreater()) {
        wprintf(L"This is Windows XP\n");
    }

    return 0;
}

内存

GlobalMemoryStatusEx() 函数用于检测当前系统的物理内存和虚拟内存。

#include <windows.h>
#include <wchar.h>

int wmain(void) {

    MEMORYSTATUSEX mem = {0};

    mem.dwLength = sizeof(mem);

    int r = GlobalMemoryStatusEx(&mem);

    if (r == 0) {
        wprintf(L"Failed to memory status %ld", GetLastError());
        return 1;
    }

    wprintf(L"Memory in use: %ld percent\n", mem.dwMemoryLoad);
    wprintf(L"Total physical memory: %lld\n", mem.ullTotalPhys);
    wprintf(L"Free physical memory: %lld\n", mem.ullAvailPhys);
    wprintf(L"Total virtual memory: %lld\n", mem.ullTotalVirtual);
    wprintf(L"Free virtual memory: %lld\n", mem.ullAvailVirtual);
    
    return 0;
}

Known Folders

从Windows Vista开始,使用了新的系统来标识重要的目录,它叫做Known Folders。Known Folders使用一组GUID(Globally unique identifier) 来索引重要的文件夹。

SHGetKnownFolderPath()函数通过GUID返回全路径。

#include <windows.h>
#include <initguid.h> //由于一些内部API的问题,不包含这个编译时会报错
#include <KnownFolders.h>
#include <ShlObj.h>
#include <wchar.h>

int wmain(void) {

    PWSTR path = NULL;
    
    HRESULT hr = SHGetKnownFolderPath(&FOLDERID_Documents, 0, NULL, &path);

    if (SUCCEEDED(hr)) {
        wprintf(L"%ls\n", path);
    }

    CoTaskMemFree(path); //释放内存

    return 0;
}

驱动名

使用GetLogicalDriveStringsW()函数来获取磁盘驱动名,他返回一个驱动路径的buffer:

#include <windows.h>
#include <wchar.h>

int wmain(void) {

    wchar_t LogicalDrives[MAX_PATH] = {0};
    DWORD r = GetLogicalDriveStringsW(MAX_PATH, LogicalDrives);
    
    if (r == 0) {
        wprintf(L"Failed to get drive names %ld", GetLastError());
        return 1;
    }

    if (r > 0 && r <= MAX_PATH) {
    
        wchar_t *SingleDrive = LogicalDrives;
        
        while(*SingleDrive) {
            wprintf(L"%ls\n", SingleDrive);

            SingleDrive += wcslen(SingleDrive) + 1;
        }
    }

    return 0;
}

剩余空间

函数GetDiskFreeSpaceExW() 用于检测磁盘的用量信息。他返回三条信息:空间总量,剩余空间总量,剩余可用空间总量,下面的样例检测c盘的空间情况:

#include <windows.h>
#include <wchar.h>

int wmain(void) {

    unsigned __int64 freeCall,
                     total,
                     free;

    int r = GetDiskFreeSpaceExW(L"C:\\", (PULARGE_INTEGER) &freeCall,
        (PULARGE_INTEGER) &total, (PULARGE_INTEGER) &free);

    if (r == 0) {

        wprintf(L"Failed to get free disk space %ld", GetLastError());
        return 1;
    }	

    wprintf(L"Available space to caller: %I64u MB\n", freeCall / (1024*1024));
    wprintf(L"Total space: %I64u MB\n", total / (1024*1024));
    wprintf(L"Free space on drive: %I64u MB\n", free / (1024*1024));
    
    return 0;
}

CPU速度

CPU速度通过检测注册表的值来检测,需要查询HARDWARE\DESCRIPTION\System\CentralProcessor\0 key

#include <windows.h>
#include <wchar.h>

int wmain(void) {

    DWORD BufSize = MAX_PATH;
    DWORD mhz = MAX_PATH;
    HKEY key;

    long r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
        L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key);

    if (r != ERROR_SUCCESS) {

        wprintf(L"RegOpenKeyExW() failed %ld", GetLastError());
        return 1;
    }

    r = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE) &mhz, &BufSize);

    if (r != ERROR_SUCCESS) {

        wprintf(L"RegQueryValueExW() failed %ld", GetLastError());
        return 1;
    }

    wprintf(L"CPU speed: %lu MHz\n", mhz);

    r = RegCloseKey(key);

    if (r != ERROR_SUCCESS) {

        wprintf(L"Failed to close registry handle %ld", GetLastError());
        return 1;
    }
    
    return 0;
}

字符串

C语言中是没有字符串数据类型的。字符串本质是一个字符型数组,无论什么时候我们说到字符串,都是指字符串数组。

我们有五个处理字符串的API,包括CRT和Windows API的:

  • ANSI C standard functions
  • Security enhanced CRT functions
  • Windows API kernel and user functions
  • Windows API Shell Lightweight Utility functions
  • Windows API StrSafe functions

ANSI C 字符串函数

CRT函数调用了Windwos API函数,因此会有一点小开销,这些函数虽然实现了字符串函数但是有一些限制,当不被正确使用时,会造成安全问题,当执行失败时不会返回错误值。

#include <windows.h>
#include <wchar.h>

#define STR_EQUAL 0

int wmain(void) {

    wchar_t str1[] = L"There are 15 pines";

    wprintf(L"The length of the string is %lld characters\n", wcslen(str1)); //输出宽字符串的数量

    wchar_t buf[20];
    wcscpy(buf, L"Wuthering"); //将字符串拷贝到buffer
    wcscat(buf, L" heights\n"); //向buffer中添加字符串
    wprintf(buf);

    if (wcscmp(L"rain", L"rainy")== STR_EQUAL) { //比较字符串

        wprintf(L"rain and rainy are equal strings\n");
    } else {

        wprintf(L"rain and rainy are not equal strings\n");
    } 

    return 0;
}

安全提高的CRT函数

安全提高的CRT函数不是标准函数而是微软的扩展函数。

这些函数验证了参数,buffer大小,字符串空检测以及提供了强化的错误报告。

安全提高的CRT函数都有一个_s后缀:

#include <windows.h>
#include <wchar.h>

#define BUF_LEN 25

int wmain(void) {

    wchar_t str1[] = L"There are 15 pines";

    const int MAX_CHARS = 50;
    size_t count = wcsnlen_s(str1, MAX_CHARS); 

    wprintf(L"The length of the string is %ld characters\n", count);

    wchar_t buf[BUF_LEN] = {0};

    int r = wcscpy_s(buf, BUF_LEN, L"Wuthering");

    if (r != 0) {

        wprintf(L"wcscpy_s() failed %ld", r);
    }

    r = wcscat_s(buf, BUF_LEN, L" heights\n");

    if (r != 0) {

        wcscat_s(L"wcscat_s() failed %ld", r);
    }

    wprintf_s(buf);

    return 0;
}

内核和用户字符串函数

这些函数只用于Windows系统,他们比CRT更轻量。

内核级字符串函数根植于Windows内核开发,他们以字母l为前缀。

字符串长度

TODO.

日期和时间

TODO.

窗口

在Windows中,一切都是窗口,主窗口、按钮,静态文本甚至是图标,都是一个窗口,静态文本只不过是一个特殊窗口。

样例

simplewindow.c

#include <windows.h>

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

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    PWSTR pCmdLine, int nCmdShow) {
    
    MSG  msg;    
    HWND hwnd;
    WNDCLASSW wc;

    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.lpszClassName = L"Window";
    wc.hInstance     = hInstance;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpszMenuName  = NULL;
    wc.lpfnWndProc   = WndProc;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  
    RegisterClassW(&wc);
    hwnd = CreateWindowW(wc.lpszClassName, L"Window",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 350, 250, NULL, NULL, hInstance, NULL);  

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

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

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, 
    WPARAM wParam, LPARAM lParam) {
    
    switch(msg) {
  
      case WM_DESTROY:
      
          PostQuitMessage(0);
          break;
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

参考链接:
https://zetcode.com/gui/winapi/

posted @ 2021-02-10 18:33  多弗朗强哥  阅读(269)  评论(0编辑  收藏  举报