C语言:WindowsAPI个人学习笔记(二)

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    ......
}

一、LRESULT(Long RESULT)是Win32 API中用于表示消息处理函数返回值的类型。在Windows编程中,当窗口过程(Window Procedure)处理了一个窗口消息后,它通常会返回一个LRESULT值。这个返回值用于指示消息处理的结果,例如,如果是处理一个键盘消息,LRESULT可能会返回一个表示字符代码的值。LRESULT通常被定义为32位的长整数(long),这样可以保证即使在64位操作系统上,LRESULT也保持32位的大小,以保持与32位应用程序的兼容性。

二、在Windows编程中,LPARAM(Long PARAMeter)和WPARAM(Word PARAMeter)是两种数据类型,用于在Win32 API中传递消息参数。这些参数通常用于窗口过程函数(Window Procedure)中,以传递与特定窗口消息相关的信息。

        1.WPARAM: 通常用于传递消息的辅助信息,如键盘的虚拟键代码、鼠标按钮状态、通知码等。WPARAM是一个无符号的16位整数(WORD),但在32位程序中,它被扩展为32位(DWORD)以保持一致性。在64位Windows系统中,WPARAM 仍然是一个32位的无符号整数(UINT),即使是在64位的应用程序中。这是为了保持与32位Windows应用程序的兼容性,因为Windows API在32位和64位系统中保持了一致性。

        【问:WPARAM在如今的32位系统中和64位系统中都为32位,可是现在很少人会用16位系统?那为什么不直接说WPARAM是一个32位的参数?】

        【答:是的,目前16位操作系统已经很少使用了,而且现代的编程实践中几乎都是在32位或64位环境下进行的。但是在早期的16位Windows系统中,WPARAM 确实是一个16位的参数。随着Windows操作系统的发展,为了保持向后兼容性,在32位和64位系统中,WPARAM 为32位,但WPARAM的定义宏没有被修改。而且许多现有的代码库、文档和教程可能会继续提到WPARAM 的16位历史。此外,Windows API的许多部分都非常古老,并且自从它们最初被设计以来就没有改变过,以保持旧应用程序的兼容性。因此,即使WPARAM 在32位和64位系统中实际上是一个32位的参数,它仍然被介绍为是一个无符号的16位整数,这是为了与旧的文档和代码保持一致,以及为了区分它与其他参数类型,如LPARAM 和 LRESULT。】

        【问:WPARAM为什么要扩展成32位的?因为32位系统通常比16位系统性能更好,所以WPARAM扩展成32位是不是性能会更好?】

        【答:WPARAM 在32位系统中被“扩展”成32位时,指的是它的宽度从原来的16位增加到32位。这种扩展并不能直接提高性能,而是为了保持与旧代码的兼容性,并允许在新的系统中传递更多的信息。在16位Windows系统中,WPARAM 实际上是一个16位的 WORD 类型。随着32位Windows系统的引入,为了保持API的一致性和向后兼容性,WPARAM 被扩展为32位的 UINT 类型。这样,即使是在32位或64位系统中,旧的16位应用程序仍然可以正常运行,因为它们使用的 WPARAM 参数会被自动转换为新的大小。WPARAM 需要扩展为32位是因为在32位Windows操作系统中,所有的消息参数都被统一为32位宽度,以保持一致性和简化消息传递机制。虽然32位应用程序仍然支持16位整数类型的使用,但是在Win32 API中,WPARAM 和 LPARAM 被定义为32位类型(UINT 和 LONG),这是为了适应更广泛的数据传递需求。】

        【问:既然将WPARAM扩展为32位是为了旧的16位应用程序可以在32位系统上正常运行,那么为什么32位应用程序不需要在64位系统上被扩展为64位?】

        【32位应用程序在64位系统上不需要被扩展,因为64位Windows操作系统提供了所谓的“-wow64”(Windows 32-bit on Windows 64-bit)兼容层。这个兼容层允许32位应用程序在64位版本的Windows上无缝运行。它负责将32位应用程序的指令和数据转换为64位操作系统的格式,同时也处理32位和64位地址空间之间的转换。(Wow64兼容层会使32位应用程序在64位Windows操作系统上运行时引入一定的性能开销。这是因为32位应用程序通过Wow64执行时,需要进行额外的地址空间转换和模拟操作,这些操作虽然经过优化,但仍然无法与原生的64位应用程序相提并论。)】

        更多杂问:

        【问:为什么当初微软没有搞32位操作系统兼容16位应用程序的兼容层?】

        【微软在推出32位操作系统时,确实考虑到了向后兼容性问题,特别是对于16位应用程序的兼容性。实际上,Windows 95和后来的Windows操作系统(如Windows 98、Windows NT、Windows 2000等)都包含了一种名为“虚拟86”的模式,它允许在32位操作系统上运行16位应用程序。这种模式模拟了16位的环境,使得16位应用程序可以在32位系统上运行。然而,这种兼容性并不是通过一个完整的“兼容层”来实现的,而是通过操作系统的双重性质来实现的。Windows 95和98等操作系统在内核级别同时支持16位和32位应用程序,这是因为它们是基于MS-DOS和16位Windows的混合体。这些操作系统的设计允许它们在需要时切换到16位模式来运行旧的16位应用程序。】

        【问:有WIN32API是不是还有WIN16API? 怎么好像没听说过WIN64API?】

        【是的,存在所谓的Win16 API,它是指16位Windows操作系统的应用程序编程接口。Win16 API是在早期的Windows 3.x系列操作系统中使用的,这些操作系统运行在16位处理器上,如Intel 8086和80286。Win16 API提供了一套用于开发16位Windows应用程序的函数和接口。另外实际上,随着64位处理器的普及和64位操作系统的出现,Microsoft确实引入了64位的API,但这些API通常被视为Win32 API的扩展,而不是一个全新的API。64位Windows操作系统上的应用程序仍然使用Win32 API,但是这些API在64位环境下能够利用更多的寄存器和更大的内存地址空间。】

        2.LPARAM: 用于传递更复杂或更大范围的信息,如指针、句柄、偏移量等。LPARAM是一个有符号的32位整数(LONG),即使在64位程序中,它也保持32位大小,以确保与32位程序兼容。

        【问:为什么在16位的操作系统中,能够支持32位的LPARAM?】

        【答:在16位操作系统中,LPARAM 是一个32位的参数,这并不是因为它实际上需要32位的大小,而是因为它是16位Windows API的一部分,而这个API是在16位架构上设计的。在16位系统中,LPARAM 被定义为32位的 LONG 类型,这主要是因为16位Windows API的设计和规范。在设计16位Windows API时,可能考虑到了未来的扩展性,因此将LPARAM 定义为32位,以避免未来可能需要增加的参数大小限制。需要注意的是,尽管LPARAM 在16位系统中定义为32位,但这并不意味着它可以传递真正的32位数据。在16位系统中,所有的数据都是按照16位的大小进行处理的,包括LPARAM。如果LPARAM 中包含了一个32位的值,它会被存储在两个连续的16位寄存器中,或者被转换为一个32位的值。】

        【问:那为什么当初的WPARAM不设计为32位?】

        【答:在早期16位Windows操作系统中,WPARAM 设计为16位是因为它主要用于传递一些简单的消息辅助信息,例如键盘的虚拟键代码、鼠标按钮状态等。这些信息通常不需要32位的大小来表示。(早期Windows API的设计哲学是尽可能简单和易于使用。通过使用16位的WPARAM,开发者可以更容易地理解消息参数的含义和处理方式。)】

三、在Windows API中,UINT 是一个无符号整数类型,它被定义为16位或32位的大小,具体取决于操作系统版本和上下文。在早期的16位Windows操作系统中,UINT 通常被定义为16位,而在现代的32位和64位Windows操作系统中,UINT 被定义为32位。

        【问:UINT 是一个无符号整数类型,那我不能直接用C语言自带的整数类型直接替代它么?】

        【答:虽然你可以在C语言中使用标准库提供的无符号整数类型来替代 UINT,但在Windows API中,UINT 有一些特定的用途和含义,这使得它不适合简单地用标准库的无符号整数类型来替换。

        1.在不同的Windows版本中,UINT 被定义为不同的大小,以保持与旧代码的兼容性。在16位Windows系统中,UINT 通常是16位的,而在32位和64位Windows系统中,UINT 通常是32位的。

        2.UINT 是Windows API的一部分,它被广泛用于Windows API的函数和结构中。使用标准库的无符号整数类型可能会导致与Windows API的不兼容性。

        3.UINT 在Windows API中用于特定的用途,如传递消息参数、句柄、标识符等。这些用途可能需要特定的类型大小和行为,而标准库的无符号整数类型可能无法完全满足这些需求。】

四、在Windows编程中,HWND 是一个缩写,代表“handle to a window”。它是Windows API中用于表示窗口句柄的一个类型。窗口句柄是一个特殊的整数,用于唯一标识一个窗口。每个窗口在系统中都有一个唯一的 HWND,就像一个身份证号码一样。

五、在Windows编程中,ShowWindow 函数用于控制窗口的显示状态。它接受两个参数:

    ShowWindow(hwnd, nCmdShow);

        1.hwnd:这是窗口的句柄,一个整数,用于标识窗口。

        2.nCmdShow:这是一个枚举值,用于指定窗口的显示状态。它可以是以下值之一:

       

        SW_SHOWNORMAL:窗口出现在屏幕上,并且有正常大小和位置。

        SW_SHOWMINIMIZED:窗口最小化后出现在任务栏上。

        SW_SHOWMAXIMIZED:窗口最大化后出现在屏幕上。

        SW_SHOWNOACTIVATE:窗口出现在屏幕上,但不激活它。

        SW_SHOW:窗口出现在屏幕上,并且有正常大小和位置。如果窗口当前是隐藏的,它将恢复到上次的状态。

        SW_MINIMIZE:窗口最小化。

        SW_SHOWMINNOACTIVE:窗口最小化,但不被激活。

        SW_SHOWNA:窗口出现在屏幕上,但不激活它。不会改变窗口的状态,也不会显示在任务栏上。

        SW_RESTORE:窗口被还原到正常大小和位置,并且成为活动窗口。

        SW_SHOWDEFAULT:用于MDI应用程序,表示如果父窗口是最大化的,则激活子窗口;如果父窗口是激活的,则最小化子窗口。

        【当你使用 ShowWindow 函数时,它只是告诉操作系统你想要显示窗口,但并不保证窗口的内容会立即更新到屏幕上。例如,如果你使用 SW_SHOW 参数显示一个隐藏的窗口,窗口将会出现在屏幕上,但窗口的内容可能不会立即更新,因为它可能需要重绘。】

六、在Windows编程中,UpdateWindow 函数用于强制重绘指定窗口的客户区域。当窗口的客户区域需要更新时,使用 UpdateWindow 函数可以确保所有变化被立即反映到屏幕上。

        hwnd:这是窗口的句柄,一个整数,用于标识窗口。

UpdateWindow(hwnd); // 确保窗口内容立即更新到屏幕

        

七、在Windows编程中,MSG 是一个结构体,它用于表示消息。每个消息都包含了一系列的参数,这些参数描述了消息的类型、发送者和接收者等信息。

        以下是 MSG 结构体的定义:

typedef struct tagMSG {
    HWND   hwnd;       // 消息发送到的窗口句柄
    UINT   message;    // 消息的类型
    WPARAM wParam;     // 消息的第一个参数,通常是辅助信息
    LPARAM lParam;     // 消息的第二个参数,通常是附加信息
    DWORD  time;       // 消息的时间戳
    POINT  pt;         // 鼠标位置
} MSG, *PMSG;

八、在Windows编程中,HINSTANCE 是一个指针类型,它代表一个Windows实例句柄。每个Windows应用程序都至少有一个实例句柄,这个句柄是在应用程序启动时由操作系统分配的,用于标识应用程序的实例。

        关于HINSTANCE

        唯一标识HINSTANCE 用于唯一标识一个Windows应用程序的实例。每个应用程序实例都有其唯一的 HINSTANCE 句柄。

        全局访问HINSTANCE 句柄可以在整个应用程序中全局访问,用于在不同函数之间传递应用程序实例的引用。

        窗口创建:在创建窗口时,HINSTANCE 句柄被用作窗口创建函数(如 CreateWindowEx 或 CreateWindow)的参数之一,以确保新创建的窗口属于正确的应用程序实例。

        资源访问HINSTANCE 句柄还可以用于访问应用程序的资源,如图标、光标、菜单等。在Windows API中,资源通常与应用程序实例相关联,因此需要 HINSTANCE 句柄来加载这些资源。

        模块操作HINSTANCE 句柄还可以用于操作应用程序的模块,如动态链接库(DLLs)。在Windows API中,模块通常与应用程序实例相关联,因此需要 HINSTANCE 句柄来加载或卸载模块。

        全局函数和数据:在某些情况下,HINSTANCE 句柄可以用于访问全局函数和数据,这些函数和数据在整个应用程序中是唯一的。

九、在Windows编程中,LPSTR 是一个缩写,代表“long pointer to a string”。它是C语言中的一个指针类型,指向一个字符串,即以空字符('\0')结尾的字符数组。

        关于LPSTR

        指向字符串LPSTR 是一个指针,它指向一个字符串,即一个字符数组。这个字符数组以空字符('\0')结尾,表示字符串的结束。

        无长度限制LPSTR 没有指定字符串的长度,这使得它可以指向任意长度的字符数组。

        Windows API使用:在Windows API中,LPSTR 通常用于传递字符串参数,如命令行参数、文件路径等。

        与 LPCSTR 的区别LPSTR 和 LPCSTR 都是指针类型,它们都可以指向字符串。LPSTR 是未初始化的指针,而 LPCSTR 是常量指针,表示指向一个只读字符串。

        与 LPWSTR 的区别LPSTR 用于指向8位字符(ASCII码)的字符串,而 LPWSTR 用于指向宽字符(Unicode)的字符串。

十、在Windows编程中,WINMain 函数是Windows应用程序的主入口点,它接受以下五个参数:

        hInstance:这是应用程序的实例句柄,由操作系统在启动应用程序时分配。这个句柄用于标识应用程序的实例,并在整个应用程序中保持不变。

        hPrevInstance:这是上一个运行的相同应用程序实例的句柄。如果应用程序是首次运行,这个参数通常为NULL

        lpCmdLine:这是一个指向命令行字符串的指针,包含启动应用程序时传递给它的命令行参数。这个参数是一个字符串,以空字符('\0')结尾。

        nCmdShow:这是一个整数,用于指定窗口应该如何显示。它可以是SW_SHOWNORMALSW_SHOWMINIMIZEDSW_SHOWMAXIMIZED等值,用于控制窗口的初始显示状态。

        lpReserved:这是一个保留参数,通常设置为NULL。(这个参数是为了向后兼容而保留的,最初是为了支持多实例应用程序的创建,但现在它已经被废弃,并且不再是多实例应用程序的必需参数。)

最基本的windows窗口(可选择GB 2312的文件编码防止乱码):

//引入使用WindowsAPI所需的头文件
#include <windows.h>

//窗口过程函数【接受窗口句柄,消息,辅助消息,辅助信息】
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    //根据消息类型选择处理方式
    switch (uMsg) {
        //当窗口被销毁时使用
        case WM_DESTROY:
            //发出退出消息循环的消息
            PostQuitMessage(0); 
            //返回0表示消息已被处理
            return 0;
    }
    //没有与消息类型匹配的处理方式,则调用默认的窗口过程,让消息由操作系统处理(系统的预设操作)
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//主窗口函数【操作系统分配的实例句柄,上次运行相同应用程序实例的句柄(首次则为NULL),指向命令行字符串的指针,
//          指定窗口如何显示】
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    //初始化WNDCLASS所有成员变量
    WNDCLASS wc = {0};
    //分配实例句柄
    wc.hInstance = hInstance;
    //设置窗口名称
    wc.lpszClassName = "MyWindowClass";
    //设置窗口过程函数
    wc.lpfnWndProc = WindowProc;
    //注册窗口类
    RegisterClass(&wc);

    //创建窗口
    //【窗口类名称,窗口标题,窗口样式,窗口位置及大小,父窗口句柄,窗口菜单句柄,
    //  窗口实例句柄,窗口附加创建数据】
    HWND hwnd = CreateWindowEx(
        0,
        "MyWindowClass",
        "我的窗口",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    
    //如果窗口创建失败,返回-1
    if (hwnd == NULL) {
        return -1;
    }
    
    //显示窗口
    ShowWindow(hwnd, nCmdShow);
    //更新窗口
    UpdateWindow(hwnd);
    
    //初始化MSG
    MSG msg = {0};
    
    //消息循环
    while (GetMessage(&msg, NULL, 0, 0)) {
        //将消息转换为字符输入消息
        TranslateMessage(&msg);
        //将消息分发到窗口过程函数
        DispatchMessage(&msg);
    }
    //将wParam的值作为整数值返回。
    return (int)msg.wParam;
}

(若在vscode里运行需要在task.json文件的arg里添加"-mwindows")

(哥们学累了浅浅放张图休息休息)

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值