DSP

学习笔记:一个简单的win32程序和复杂的背景知识

2019-07-13 20:32发布

/*一个简单的win32程序。
1.用vc6创建一个win32 application,因为dsp文件不太好写,接受vc6的帮助是无奈的选择,当然也可以研究一下。
2.添加一个cpp文件到工程,因为vc6一般都是编译cpp文件生成程序。
3.写代码,编译,调试...*/ #include   //此头文件定义了一些宏、包含了一些必要头文件,如果不要它,该如何写? //函数声明
BOOL InitWindows(HINSTANCE hIns, int iCmdShow);
LRESULT CALLBACK WinProcTest(HWND hWnd, UINT uiMsg, WPARAM wPar, LPARAM lPar); /*#define PASCAL      __stdcall
__stdcall告诉编译器参数传递方法和函数名转换规则,例如:__cdecl、__stdcall、__fastcall、thiscall 。
这属于编译器支持的关键字,不是c++语言定义的。
__stdcall是大多数语言(PASCAL、BASIC)等支持的方式,详细参考MSDN。
为什么WInMain要采用此方式,原因不明,可能是为了和其它语言编写的程序兼容。
__cdecl是c/c++默认的方式。*/
/*WinMain是win32程序的入口。
入口为什么是WinMain?这个大概是vc内定的,实际用win32asm编码,入口函数没有这个限制。
大约,事情都是有个条条框框的。
WinMain函数的参数和返回值也是限定的,而且似乎是给系统调用使的。*/
/*WinMain函数作用:创建主窗口,消息控制。可以说这是一种编码模式,也是一个框框。
HINSTANCE hInstance     当前程序的实例句柄
             句柄是一些表的索引也就是指向指针的指针,句柄和指针都是地址,句柄是Windows编程的一个关键性的概念,
             编写Windows应用程序总是要和各种句柄打交道。所谓句柄,就是一个唯一的数,用以标识许多不同的对象类型,
             如窗口、菜单、内存、画笔、画刷等。在Win32里,句柄是指向一个“无类型对象”(void*)的指针,也就是一个4字节长的数据。
             无论它的本质是什么,句柄并不是一个真正意义上的指针。从构造上看,句柄是一个指针,
             尽管它没有指向用于存储某个对象的内存位置。事实上,句柄指向一个包含了对该对象进行的引用的位置。
             句柄的声明是这样的:typedef void *HANDLE
             由于Windows是一个多任务操作系统,它可以同时运行多个程序或一个程序的多个副本。这些运行的程序称为一个实例。
             为了对同一程序的多个副本进行管理,Windows引入了实例句柄。Windows为每个应用程序建立一张表,
             实例句柄就好象是这张表的一个索引。
             不同在于:
             1、句柄所指的可以是一个很复杂的结构,并且很有可以是与系统有关的,比如说上面所说的线程的句柄,
             它指向的就是一个类或者结构,他和系统有很密切的关系,当一个线程由于不可预料的原因,
             而终止时在系统就可以回它所占用的资料,如CPU,内存等等,反过来想可以知道,这个句柄中的某一些项,
             是与系统进行交互的。由于Windows系统,是一个多任务的系统,它随时都可能要分配内存,回收内存,重组内存。
             2、指针它也可以指向一个复杂的结构,但是通常是用户定义的,所以的必需的工作都要用户完成,特别是在删除的时候。
             但在VC++6.0中也有一些指针,它们都是处理一些小问题才用的,如最常见的字符的指针,
             它也是要用户处理的如果你动态分配了内存;但是Cstring就不要用户处理了,它其实是VC++中的一个类,
             所以的操作都由成员函数完成,产生(分配)由构造函数,删除(回收)由析构函数完成。 
HINSTANCE hPrevInstance   前一个程序的实例句柄,win32程序中此值都为NULL,为了兼容win16程序留的。win32程序不再通过它来控制唯一运行实例。
             前一个程序是指同一个程序文件多次运行时的不同内存版本。
LPSTR lpCmdLine      命令行字符。如果要让win32程序支持console方式,那么就用它。
int nShowCmd       窗口显示方式。大概某些win32api启动程序时可以指定此参数,否则就只留给系统用了。*/
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
 if(!InitWindows(hInstance, nShowCmd))
 {
  return -1;
 }
 else
 {
  MSG msgTemp;//系统自有结构体,用于保存message信息。
  /*下面的while循环就是消息获取、消息转换、消息响应。
  本程序通过GetMessage从本线程的消息队列获取消息,由MSG*作为信息载体,详细参数含义参考MSDN。
  GetMessage获得WM_QUIT消息时会返回0,促使while消息循环终止。
  这类win32的api代码似乎只能看到它的asm代码和接口定义,
  但在当年泄露出来的win2k部分源代码中可以找到这个函数的实现的代码,呵呵。*/
  while(GetMessage(&msgTemp, NULL, 0, 0))
  {
   TranslateMessage(&msgTemp);//将virtual-key消息翻译为character消息。如果要响应键盘,可能需要它。
   DispatchMessage(&msgTemp);//分配消息。
  }
  return msgTemp.wParam;//为什么是返回它?不明。随便返回一个int都可以。
 }
} /*InitWindows是为了简化WinMain函数,自定义的函数。参数也是因为要使用才设置的。*/
BOOL InitWindows(HINSTANCE hIns, int iCmdShow)
{
 /*为什么需要WNDCLASS?因为win32程序在交由用户编码之前,已经定义了绝大部分属性和行为,
   剩下一些交由用户自定义,然后系统获得完备信息后才能处理,也就是说用户是在和系统合作办一件事情,而且是由系统说了算。
   系统需要用户建立一个窗口,建立之前需要注册它,WNDCLASS就是用来注册窗口的。*/
 WNDCLASS wc; //窗口类
 wc.style = CS_VREDRAW | CS_HREDRAW; //窗口类风格
 wc.lpfnWndProc = (WNDPROC)WinProcTest; //窗口消息处理函数
 wc.cbClsExtra = 0; //窗口类存储的额外字节数,不明白
 wc.cbWndExtra = 0; //窗口存储的额外字节数,不明白
 wc.hInstance = hIns; //当前程序实例句柄,CreateWindow也用到这个参数。
 wc.hIcon = LoadIcon(hIns, IDI_APPLICATION ); //图标
 wc.hCursor = LoadCursor(NULL, IDC_ARROW ); //光标
 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //画刷
 wc.lpszMenuName = NULL; //窗口菜单的名字,如果有应该在资源文件中定义
 wc.lpszClassName = "hello world";//窗口名称,注意CreateWindow函数的第一个参数与此处一致。
 if(!RegisterClass(&wc))//注册窗口类。wc扔进去后以后连个响都没有,应该是系统在管理,没办法。
 {//注册失败时调用这个MessageBox函数,由win2k源代码来看,是新建了一个线程干了些事情。
  MessageBox(NULL, "Can not register window class", "Error", MB_OK | MB_ICONINFORMATION);
  return FALSE;
 }
 /*前面忙了半天就是为了CreateWindow创建窗口,它返回的是窗口句柄,实际上此返回值在系统内部有个备份,每次都会传给消息处理函数。*/
 HWND hwnd = CreateWindow("hello world",//窗口类名称
        "simple win32 program",//窗口标题
        WS_OVERLAPPEDWINDOW,//窗口风格
        100,//窗口位置的x坐标,坐标轴从左到右
        100,//窗口位置的y坐标,坐标轴从上到下
        600,//窗口的宽度,单位是像素
        400,//窗口的高度
        NULL,//父窗口句柄,产生窗口与窗口的关系!
        NULL,//菜单句柄
        hIns,//应用程序实例句柄
        NULL//窗口创建数据指针
        );
 if(!hwnd) return FALSE;
 ShowWindow(hwnd, iCmdShow); //显示窗口,将显示这种行为分离出来,可以获得一种灵活
 UpdateWindow(hwnd); //更新窗口,同理把刷新行为分离出来,也是为了某种灵活,当然这种分离也让俺们初学者感到麻烦,呵呵
 return TRUE;
} /*消息处理函数,用户自定义,利用WNDCLASS.lpfnWndProc注册到系统,系统管理消息,如果有就会把消息丢给这个函数处理。
返回值似乎是给系统用的,不知道有何讲究,似乎是没有。
HWND hWnd  窗口句柄
UINT uiMsg 主消息值
WPARAM wPar 副消息值
LPARAM lPar 副消息值
*/
LRESULT CALLBACK WinProcTest(HWND hWnd, UINT uiMsg, WPARAM wPar, LPARAM lPar)
{
 switch(uiMsg)//通过主副消息值配合判断用户行为(数值映射而已)
 {
 case WM_KEYDOWN://键盘按下消息
  {
   switch(wPar)
   {
    case VK_SPACE://空格键
     {
      MessageBox(hWnd, "space", "key", MB_OK | MB_ICONINFORMATION);
      break;
     }
    default:
     break;
   }
   break;
  }
 case WM_RBUTTONDOWN://鼠标右键按下,这些都需要系统采用统一的消息映射,才能协作
  {
   MessageBox(hWnd, "right button down", "mouse", MB_OK | MB_ICONINFORMATION);
   break;
  }
 case WM_PAINT://重绘消息
  {
   /*以下是在窗口绘图区输出文字,操作方法与窗口程序启动不同,但是编码风格还是c的那种:
   系统通过某种标识(窗口句柄)维护程序信息,用户通过BeginPaint和窗口句柄获得某种句柄(标识),
   通过句柄通知系统做些事情。*/
   char cTemp[] = "hello";
   HDC hdc;
   PAINTSTRUCT psT;
   hdc = BeginPaint(hWnd, &psT);//取得设备环境句柄
   SetTextColor(hdc, RGB(0,0,0));//设置文字颜 {MOD}
   TextOut(hdc, 160, 0, cTemp, strlen(cTemp));//输出文字
   EndPaint(hWnd, &psT);//释放资源
   break;
  }
 case WM_DESTROY://关闭程序的消息
  {
   PostQuitMessage(0);//发送WM_QUIT消息,促使GetMessage返回false,从而结束while消息循环。
   break;
  }
 default:
  return DefWindowProc(hWnd, uiMsg, wPar, lPar);//系统默认的消息处理函数
 }
 return 0;