吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2494|回复: 11
收起左侧

[系统底层] win32笔记十二 第一个Windows程序

[复制链接]
huchen 发表于 2024-5-7 00:16
之前的笔记索引

第一个Windows程序

环境:winxp  VC++ 6

首先vc6中新建选择Win32 Application,也可以选控制台,没有什么本质区别 ,但是缺什么就要自己添,然后选一个简单的Win32 Application

image-20240425201551995.png

可以看到会给我们生成一个入口

我们来看看winmain的参数

int WINAPI WinMain(
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int nCmdShow              // show state
);

hInstance

在win32里面,只要是h开头的,通常都称为句柄,句柄又有各种各样的类型,比如HANDLE、HWND、HDC,HINSTANCE是模块的句柄,当然还是索引的本质,并且对象是在内核,还是DWORD,四字节,不同的名字是为了可读性更好

hPrevInstance

这个参数永远为空,不用管他

lpCmdLine

之前说创建进程时,任何一个进程都是其他进程创建得,我们用CreateProcess  API创建进程得函数,第二个参数是命令行参数,这个参数就会传给这个lpCmdLine

nCmdShow

CreateProcess函数得倒数第二参数,启动信息就会传给nCmdShow参数

输出内存地址

#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    // TODO: Place code here.
    char OutBuff[0x80];

    DWORD dwAddr = (DWORD)hInstance;
    sprintf(OutBuff,"模块地址:%x\n",dwAddr);//格式化输出

    OutputDebugString(OutBuff);//打印
    return 0;
}

这里得函数自行查阅,在头文件添上包含stdio.h

image-20240425203719219.png

image-20240425203446905.png

输出了400000

窗口程序代码

// 第一个窗口程序.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

LRESULT CALLBACK WindowProc(
                    HWND hwnd,      // handle to window
                    UINT uMsg,      // message identifier
                    WPARAM wParam,  // first message parameter
                    LPARAM lParam   // second message parameter
);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    // TODO: Place code here.

    //第一步:定义那你的窗口是什么样的?
    char OutBuff[0x80];

    TCHAR className[] = TEXT("My First Window");//类名字
    WNDCLASS wndclass = {0};//初始化
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;//背景颜色
    wndclass.lpszClassName = className;//类名字
    wndclass.hInstance = hInstance;//属于当前程序
    wndclass.lpfnWndProc = WindowProc;//窗口函数

    RegisterClass(&wndclass);//注册窗口类

    //第二步:创建并显示窗口
    HWND hwnd = CreateWindow(
        className,      //注册的类名字
        TEXT("我的第一个窗口"),    //窗口的名字
        WS_OVERLAPPEDWINDOW,    //
        10,
        10,
        600,
        300,
        NULL,
        NULL,
        hInstance,
        NULL
        );
    if(hwnd == NULL)    //创建失败
    {
        sprintf(OutBuff,"Error:%d\n",GetLastError());   //失败原因
        OutputDebugString(OutBuff);
        return 0;
    }

    ShowWindow(hwnd,SW_SHOW);   //显示窗口

    //第三步:接收消息并处理
    MSG msg;
    BOOL bRet;
    while((bRet = GetMessage(&msg,NULL,0,0)) !=0)
    {
        if(bRet == -1)
        {
            //handle the error and possibly exit
            sprintf(OutBuff,"Error:%d\n",GetLastError());
            OutputDebugString(OutBuff);
        }
        else
        {
            //转换消息
            //TranslateMessage(&msg);

            //分发消息
            DispatchMessage(&msg);
        }
    }

    return 0;
}

LRESULT CALLBACK WindowProc(
                    HWND hwnd,      // handle to window
                    UINT uMsg,      // message identifier
                    WPARAM wParam,  // first message parameter
                    LPARAM lParam   // second message parameter
)
{
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

第一步:定义那你的窗口是什么样的?

在windows窗口有一个类,叫WNDCALSS,里面得成员有许多,不用去理解每一个成员的含义,只有4个成员是有用的,只要知道是用来创建窗口就行了

wndclass.hbrBackground

背景颜色,可以选择系统已经有的背景色(msdn文档里有),然后这是个画刷类型

wndclass.lpszClassName

给类起一个名字

wndclass.hInstance

属于那个程序

wndclass.lpfnWndProc

窗口函数,用来处理消息后的动作,比如当我点关闭,关闭这个动作就是这个窗口程序完成的,这个格式不是随便起的,在文档中有详细说明

LRESULT CALLBACK WindowProc(  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message paramete
);

那先不管,直接复制过来,WindowProc名称看你自己

注意下面要有对应的函数定义,(代码尾部,可以写return 0后面还要改的),代码顶部,是函数的声明

这里并不是调用函数,只是告诉操作系统,当前窗口对应的消息窗口回调函数在哪里,你只管告诉我函数名就可以了,操作系统会调用这个函数

RegisterClass

把创建好的类告诉给操作系统,好找到他

第二步:创建并显示窗口

CreateWindow
HWND CreateWindow(
  LPCTSTR lpClassName,  // registered class name
  LPCTSTR lpWindowName, // window name
  DWORD dwStyle,        // window style
  int x,                // horizontal position of window
  int y,                // vertical position of window
  int nWidth,           // window width
  int nHeight,          // window height
  HWND hWndParent,      // handle to parent or owner window
  HMENU hMenu,          // menu handle or child identifier
  HINSTANCE hInstance,  // handle to application instance
  LPVOID lpParam        // window-creation data
);
lpClassName

注册的类名字

lpWindowName

窗口名字

dwStyle

风格

image-20240425210450959.png

有点多就不截完了,可自行查找资料了解风格

int x, int y,

相对于父窗口的坐标

nWidth,nHeight

窗口的宽度,高度

hWndParent

父窗口的句柄

hMenu

菜单的句柄

hInstance

属于那个模块

lpParam

附加数据

ShowWindow
BOOL ShowWindow(
  HWND hWnd,     // handle to window
  int nCmdShow   // show state
);
hWnd

显示那个窗口

nCmdShow

什么形式显示

MSDN里有具体的形式介绍

image-20240425212308034.png

当创建了窗口对象,就会生成消息队列

第三步:接收消息并处理

GetMessage
BOOL GetMessage(
  LPMSG lpMsg,         // message information
  HWND hWnd,           // handle to window
  UINT wMsgFilterMin,  // first message
  UINT wMsgFilterMax   // last message
);
lpMsg

out类型参数

hWnd,   wMsgFilterMin,  wMsgFilterMax

全是过滤参数,比如说,这个线程有100个窗口,消息队列里是所有窗口的信息,你要准确的找自己想要的,就需要这些过滤的参数,空的话就是取所有的消息

如果没有就会阻塞

Return Values

If the function retrieves a message other than WM_QUIT, the return  value is nonzero.

If the function retrieves the WM_QUIT message, the return value is  zero.

If there is an error, the return value is -1. For example, the function fails  if hWnd is an invalid window handle or lpMsg is an invalid  pointer. To get extended error information, call [GetLastError](JavaScript:hhobj_1.Click()).

Warning Because the return value can be nonzero, zero, or  -1, avoid code like this:

while (GetMessage( lpMsg, hWnd, 0, 0)) ... 

The possibility of a -1 return value means that such code can lead  to fatal application errors.

TranslateMessage

转换消息

DispatchMessage

分发消息,调用消息处理函数。

使用分发的原因是:这种消息处理函数不是我们调用的,而是由操作系统调用的

目的:我们取得了消息,但我们并不知道,每一个窗口对应的消息处理函数在哪,也就是我不能区分是那个窗口的消息,但是,内核知道,所以要进内核,拿着句柄去比对是那个窗口

image-20240425214451826.png

最终执行必须要调用一个默认的消息处理函数,即代码末尾的return ……

DefWindowProc

这个函数包含了基本的常用功能,比如,缩小、放大、拖动等,不用我们去实现,只需要去调用就行了

总结

  1. 知道了窗口程序的大致流程
  2. 消息队列与窗口的关系
  3. 为什么要分发消息的原因

免费评分

参与人数 2吾爱币 +7 热心值 +2 收起 理由
asciibase64 + 1 谢谢@Thanks!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ytfrdfiw 发表于 2024-5-7 09:48
mengxz2023 发表于 2024-5-7 07:43
现在估计学习桌面程序的很少很少了,这种API编程估计更是少了。上世纪的1995年那个时候很多

是的,用原生API写代码的人不多了,效率不高,而且学习难度也大。
但学好了,还是很有用的,对底层的原理比其他人能更清楚,解决问题思路也更多一些。
 楼主| huchen 发表于 2024-5-7 10:06
mengxz2023 发表于 2024-5-7 07:43
现在估计学习桌面程序的很少很少了,这种API编程估计更是少了。上世纪的1995年那个时候很多

窗口话可以直接用MFC来写了,但是MFC也只是在win32的基础上封装了,所以直接索性学win32来了解底层了
mengxz2023 发表于 2024-5-7 07:43
现在估计学习桌面程序的很少很少了,这种API编程估计更是少了。上世纪的1995年那个时候很多
满不懂 发表于 2024-5-7 09:27
感谢分享!收藏学习。
nitian0963 发表于 2024-5-7 10:04
跟着楼主一起学习。
shengrumenghuan 发表于 2024-5-8 08:37
感谢分享
skm415889471109 发表于 2024-5-9 05:50
学习好,谢谢分享
mengxz2023 发表于 2024-5-10 13:58
huchen 发表于 2024-5-7 10:06
窗口话可以直接用MFC来写了,但是MFC也只是在win32的基础上封装了,所以直接索性学win32来了解底层了

虽然我是干桌面程序好多年,现在桌面程序真看不到出路和未来。
Bigstmart 发表于 2024-5-11 09:19
最近也在学相关的知识,跟着大佬学
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 22:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表