当前位置: 首页 > news >正文

海北藏族自治州网站建设_网站建设公司_腾讯云_seo优化

太原网站建设杰迅,网站设计的基本步骤,做旅游网站的工作流程图,在线培训课程上一篇中我给各位说了一般人认为C中较为难的东西——指针。其实对于C#xff0c;难点当然不局限在指针这玩意儿上#xff0c;还有一些有趣的概念#xff0c;如模板类、虚基类、纯虚函数等#xff0c;这些都是概念性的东西#xff0c;几乎每一本C书上都会介绍#xff0c;而…  上一篇中我给各位说了一般人认为C中较为难的东西——指针。其实对于C难点当然不局限在指针这玩意儿上还有一些有趣的概念如模板类、虚基类、纯虚函数等这些都是概念性的东西几乎每一本C书上都会介绍而平时我们除了会接触到纯虚函数外其他的不多用。纯虚函数你可以认为与C#中的抽象方法或接口中的方法类似即只定义不实现。好处就是多态发何处理由派生类来决定。 在开始吹牛之前我先推荐一套视频教程孙鑫老师的C教程共20课我不是帮他老人家打广告而是因为孙老师讲的课是我听过的最好的课我都看过4次了我完全可以用他的视频教程来复习C的。   好了F话说完了下面我就扯一下编写一个Win32应用程序的大致流程不管你的程序有多么复杂多么变态其基本思路和流程是不变的。这就好比你写书法的时候特别是写楷书我不管你用的是欧体、颜体还是柳体你都得遵守“永字八法”基本规则。 那么我们要编写一个Win32应用程序要经过哪几个步骤呢 你不妨想一想你有一家工厂是生产女性服装的如果你要生产一批新式服装例如某种冬装你会有哪些流程 首先如果我们确定要做这么一款服式我们要请设计师来把服装设计好然后打版打版就是生成基本样本以后工人就按照这个样本做就行了。 其次注册产品向上级主管申报登记后就转入车间或下游加工企业开工。 再次为了展示你的新产品的特色你要举办一场服装表演。 接着、持续更新发现产品存在的问题不断改进修正。 最后推向市场。 我们开发Win32应用程序也是遵守这样的规范。不过我想现在很少人用Win32在实际开发中毕竟它的开发效率是相当地低下所以曾被某些人误认为只适用于开发木马程序。其实也不一定的不要太邪恶了。 MFC对Win API函数的封装后来出现了托管C你可以用于写WinForm程序这样可以提高开发效率。 如果你有足够的时间如果你还在学习编程如果你是刚进入大学的年轻有为者你不用急因为你会有更多的时间磨炼你应当考虑多学一点C类语言C的学习你会发现你能学到很多其他语言中学不到的知识特别是接触到不少原理性的东西能加深你对编程哲学的认知。   一、WinMain入口点 我们在学习标准C的时候都知道每个应用程序运行时都会先进入入口点函数main而当从main函数跳出时程序就结束了。在Windows编程里面也是一样的只是我们的入口点函数不叫main叫WinMain这个函数不同于main我们不能乱来它的定义必须与声明保持一致。 我建议各位安装VS的时候都顺便更新帮助文档到本地硬盘这样我们可以方便查找。有一点要注意目前DestTop Develop的文档基本上是英文的做好心理准备。 WinMain函数怎么写呢不用记的到MSDN文档一搜直接复制就行了。 [cpp] view plaincopyprint? int CALLBACK WinMain(      _In_  HINSTANCE hInstance,      _In_  HINSTANCE hPrevInstance,      _In_  LPSTR lpCmdLine,      _In_  int nCmdShow    );   这个函数带了一个CALLBACK说明它是一个回调函数那么这个CALLBACK是啥呢。我们先不管我们先动写一个Windows让大家有一个更直观的认识。 1、启动你的开发工具版本任意。 2、从菜单栏中依次【文件】【新建】【项目】在新建项目窗口中选择Win32-Win32应用程序。   2、点击确定后会弹出一个向导单击【下一步】。项目类型选择Windows应用程序附加选项选择空项目我们要自己编写实现代码。   3、单击完成项目创建成功。打开【解决方案资源管理器】在“源文件”文件夹上右击从菜单中找到【添加】【新建项】注意是源文件不要搞到头文件去了。 在新建项窗口中选C代码文件.cpp后缀的不要选错了选成头文件不然无法编译因为头文件是不参与编译的。文件名随便。   包含Windows.h头文件这个是最基本的。 [cpp] view plaincopyprint? #include Windows.h   然后是入口点这个我们直接把MSDN的声明Ctrl C然后Ctrl V上去就行了。 [cpp] view plaincopyprint? int CALLBACK WinMain(      _In_  HINSTANCE hInstance,      _In_  HINSTANCE hPrevInstance,      _In_  LPSTR lpCmdLine,      _In_  int nCmdShow    )  {        return 0;  }   WinMain返回整型返回0就行了其实是进程的退出码一定要0不要写其他因为0表示正常退出其他值表示非正常退出。 刚才我们提到这个函数带了CALLBACK那么它是什么很简单你回到IDE在CALLBACK上右击选【转到定义】看看吧。 我们看到它其实是一个宏原型如下 [cpp] view plaincopyprint? #define CALLBACK    __stdcall   这时候我们发现了它其实就是__stdcall那么这个__stdcall是什么呢它是和__cdecl关键字对应的这些资料你网上搜一下就有了如果你觉得不好理解你不妨这样认为__stdcall是专门用来调用Win API 的反正MSDN上也是这样说的它其实是遵循Pascal的语法调用标准相对应地__cdecl是C语言的调用风格这个也是编译器选项。打开项目属性找到节点C/C\高级然后查看一下调用约定我们看到默认是选择C风格调用的所以WIN API 函数才用上关键字__stdcall如果你实在不懂也没关系这个东西一般不影响我们写代码但属性窗口中的编译器选项不要乱改改掉了可能会导致一些问题。   那么CALLBACK有什么特别呢一句话函数不是我们调用的但函数只定义了模型没有具体处理而代码处理权在被调用者手里。怎么说呢我们完全把它理解为.NET中的委托我想这样就好理解了委托只声明了方法的参数和返回值并没有具体处理代码。 WinMain是由系统调用的而WinMain中的代码如何写那操作系统就不管了。就好像我告诉你明天有聚会一起去爬山反正我是通知你了至于去不去那是你决定了。 接下来看看入口点函数的参数。 注意我们平时看到很多如HANDLEHINSTANCEHBRUSHWPARAM。LPARAMHICONHWND等一大串数据类型也许我们会说怎么Windows开发有那么多数据类型。其实你错了人总是被眼睛所看到的东西欺骗Win API 中根本没有什么新的数据类型全都是标准C中的类型说白了这些东西全是数字来的。如果你不信自己可以研究一下。 它定义这些名字只是方便使用罢了比如下面这样 [cpp] view plaincopyprint? int hWindow;  int hIcon;  int theAppInstance;   第一个变量指的是窗口的句柄第二个指的是一个图标的句柄第三个是当前应用程序的实例句柄你看看如果我们所有的句柄都是int我们就无法判断那些类型是专门用来表示光标资源不知道哪些类型是专用来表示位图的句柄了但是如果我们这样 [cpp] view plaincopyprint? #defin HBRUSH  int64   这样就很直观我一看这名就知道是Brush Handlers哦我就明白它是专门用来管理内存中的画刷资源的看这就很明了所以通常这些新定义的类型或者宏都是取有意义的名字。比如消息它也是一个数字如果我说115代表叫你去滚但光是一个115谁知道你什么意思但是如果我们为它定义一个宏 [cpp] view plaincopyprint? #define WM_GET_OUT    115   这样只要我SendMessage(hwnd,  WM_GET_OUT, NULL, NULL)你就会收到一条消息滚到一边去。   WinMain的第一个参数是当前应用程序的实例句柄第二个参数是前一个实例比如我把kill.exe运行了两个实例进程列表中会有两个kill.exe这时候第一次运行的实例号假设为0001就传递第一个参数hInstance第二次运行的假设实例号为0002就传给了hPrevInstance参数。 lpCmdLine参数从名字上就猜到了就是命令行参数那LPSTR是啥呢它其实就是一个字符串你可以跟入定义就知道了它其实就是char*指向char的指针记得我上一篇文章中说的指针有创建数组的功能吗对其实这里传入的命令行参数应该是char[ ]这就是我在第一篇文章中要说指针的原因。 这里告诉大家一个技巧我们怎么知道哪些参数是指针类型呢因为不是所有参数都有 * 标识。技巧还是在命名上以后只要我们看到P开头的或者LP开头的都是指针类型。 比如LPWSTRLPCTSTRLPRECT等等。 最后一个参数nCmdShow是主窗口的显示方式。它定义了以下宏。   ValueMeaningSW_HIDE0 Hides the window and activates another window. SW_MAXIMIZE3 Maximizes the specified window. SW_MINIMIZE6 Minimizes the specified window and activates the next top-level window in the Z order. SW_RESTORE9 Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window. SW_SHOW5 Activates the window and displays it in its current size and position. SW_SHOWMAXIMIZED3 Activates the window and displays it as a maximized window. SW_SHOWMINIMIZED2 Activates the window and displays it as a minimized window. SW_SHOWMINNOACTIVE7 Displays the window as a minimized window. This value is similar toSW_SHOWMINIMIZED, except the window is not activated. SW_SHOWNA8 Displays the window in its current size and position. This value is similar toSW_SHOW, except the window is not activated. SW_SHOWNOACTIVATE4 Displays a window in its most recent size and position. This value is similar toSW_SHOWNORMAL, except the window is not activated. SW_SHOWNORMAL1 Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.   这个参数是操作系统传入的我们无法修改它。那么应用程序在运行时是如何决定这个参数的呢看看这个不用我介绍了吧你一定很熟悉。 我们写了WinMain但我们还要在WinMain前面预先定义一个WindowProc函数。C与C#Java这些语言不同你只需记住C编译器的解析是从左到右从上到下的如果某函数要放到代码后面来实现但在此之前要使用那么你必须先声明一下不然编译时会找不到。这里因为我们通常会把WindowProc实现放在WinMain之后但是在WinMain中设计窗口类时要用到它的指针这时候我们必须在WinMain之前声明WindowProc。 同样地WindowProc的定义我们不用记到MSDN直接抄就行了。 [cpp] view plaincopyprint? #include Windows.h  // 必须要进行前导声明  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  );    int CALLBACK WinMain(      _In_  HINSTANCE hInstance,      _In_  HINSTANCE hPrevInstance,      _In_  LPSTR lpCmdLine,      _In_  int nCmdShow    )  {      return 0;  }  // 在WinMain后实现  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  )  {      return DefWindowProc(hwnd, uMsg, wParam, lParam);  }   前导声明与后面实现的函数的签名必须一致编译才会认为它们是同一个函数。在WindowProc中返回DefWindowProc是把我们不感兴趣或者没有处理的消息交回给操作系统来处理。也许你会问函数的名字一定要叫WindowProc吗当然不是了你可以改为其他名字如MyProc但前提是返回值和参数的类型以及个数必须一致。 [cpp] view plaincopyprint? LRESULT CALLBACK MyProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  )   这个函数带了CALLBACK说明不是我们调用的也是由操作系统调用的我们在这个函数里面对需要处理的消息进行响应。至于为什么可以改函数的名字而系统为什么能找到这个函数呢后面你就知道了。   二、设计与注册窗口类 设计窗口类其实就是设计我们程序的主窗口如有没有标题栏背景什么颜色有没有边框可不可以调整大小等。要设计窗口类我们用到一个结构—— [cpp] view plaincopyprint? typedef struct tagWNDCLASS {    UINT      style;    WNDPROC   lpfnWndProc;    int       cbClsExtra;    int       cbWndExtra;    HINSTANCE hInstance;    HICON     hIcon;    HCURSOR   hCursor;    HBRUSH    hbrBackground;    LPCTSTR   lpszMenuName;    LPCTSTR   lpszClassName;  } WNDCLASS, *PWNDCLASS;   通常情况下我们用WNDCLASS就可以了当然还有一个WNDCLASSEX的扩展结构在API里面凡是看到EX结尾的都是扩展的意思比如CreateWindowEx就是CreateWindow的扩展函数。 第一个成员是窗口的类样式注意不要和窗口样式WS_xxxxx混淆了这里指的是这个窗口类的特征不是窗口的外观特征这两个style是不一样的。 它的值可以参考MSDN通常我们只需要两个就可以了——CS_HREDRAW | CS_VREDRAW从名字就看出来了就是同时具备水平重画和垂直重画。因为当我们的窗口显示的时候被其他窗口挡住后重新显示或者大小调整后窗口都要发生绘制就像我们在纸上涂鸦一样每次窗口的变化都会“粉刷”一遍并发送WM_PAINT消息。 lpfnWndProc参数就是用来设置你用哪个WindowProc来处理消息前面我说过我们只要不更改回调函数的返回值和参数的类型和顺序就可以随意设置函数的名字那为什么系统可以找到我们用的回调函数呢对的就是通过lpfnWndProc传进去的它是一个函数指针也就是它里面保存的是我们定义的WindowProc的入口地址使用很简单我们只需要把函数的名字传给它就可以了。 cbClsExtra和cbWndExtra通常不需要设为0就OK。hInstance是当前应用程序的实例句柄从WinMain的hInstance参数中可以得到。hIcon和hCursor就不用我说了看名字就知道了。 hbrBackground是窗口的背景色你也可以不设置但在处理WM_PAINT消息时必须绘制窗口背景。也可以直接用系统定义的颜色MSDN为我们列出这些值大家不用记直接到MSDN拿来用就行了这些都比较好理解看名字就知道了。 COLOR_ACTIVEBORDERCOLOR_ACTIVECAPTIONCOLOR_APPWORKSPACECOLOR_BACKGROUNDCOLOR_BTNFACECOLOR_BTNSHADOWCOLOR_BTNTEXTCOLOR_CAPTIONTEXTCOLOR_GRAYTEXTCOLOR_HIGHLIGHTCOLOR_HIGHLIGHTTEXTCOLOR_INACTIVEBORDERCOLOR_INACTIVECAPTIONCOLOR_MENUCOLOR_MENUTEXTCOLOR_SCROLLBARCOLOR_WINDOW                                         /*  这个就是窗口的默认背景色  */COLOR_WINDOWFRAMECOLOR_WINDOWTEXTlpszMenuName指的是菜单的ID没有菜单就NULLlpszClassName就是我们要向系统注册的类名字符不能与系统已存在的类名冲突如“BUTTON”类。 所以在WinMain中设计窗口类。 [cpp] view plaincopyprint? // 类名  WCHAR* cls_Name  LMy Class;  // 设计窗口类  WNDCLASS wc;  wc.hbrBackground  (HBRUSH)COLOR_WINDOW;  wc.lpfnWndProc  WindowProc;  wc.lpszClassName  cls_Name;  wc.hInstance  hInstance;   窗口类设计完成后不要忘了向系统注册这样系统才能知道有这个窗口类的存在。向操作系统注册窗口类使用RegisterClass函数它的参数就是一个指向WNDCLASS结构体的指针所以我们传递的时候要加上符号。 [cpp] view plaincopyprint? // 注册窗口类  RegisterClass(wc);     三、创建和显示窗口 窗口类注册完成后就应该创建窗口然后显示窗口调用CreateWindow创建窗口如果成功会返回一个窗口的句柄我们对这个窗口的操作都要用到这个句柄。什么是句柄呢其实它就是一串数字只是一个标识而已内存中会存在各种资源如图标、文本等为了可以有效标识这些资源每一个资源都有其唯一的标识符这样通过查找标识符就可以知道某个资源存在于内存中哪一块地址中就好比你出身的时候长辈都要为你取个名字你说名字用来干吗名字就是用来标识你的不然你见到A叫小明遇到B又叫小明那谁知道哪个才是小明啊就好像你上大学去报到号会为你分配一个可以在本校学生中唯一标识你的学号所有学生的学号都是不同的这样只要通过索引学号就可以找到你的资料。 CreateWindow函数返回一个HWND类型它就是窗口类的句柄。 [cpp] view plaincopyprint? // 创建窗口  HWND hwnd  CreateWindow(      cls_Name,           //类名要和刚才注册的一致      L我的应用程序,          //窗口标题文字      WS_OVERLAPPEDWINDOW,        //窗口外观样式      38,             //窗口相对于父级的X坐标      20,             //窗口相对于父级的Y坐标      480,                //窗口的宽度      250,                //窗口的高度      NULL,               //没有父窗口为NULL      NULL,               //没有菜单为NULL      hInstance,          //当前应用程序的实例句柄      NULL);              //没有附加数据为NULL  if(hwnd  NULL)                //检查窗口是否创建成功      return 0;   窗外观的样式都是WS_打头的是Window Style的缩写这个我就不说了MSDN上全有了。 窗口创建后就要显示它就像我们的产品做了要向客户展示。显示窗口调用ShowWindow函数。 [cpp] view plaincopyprint? // 显示窗口  ShowWindow(hwnd, SW_SHOW);   既然要显示窗口了那么ShowWindow的第一个参数就是刚才创建的窗口的句柄第二个参数控制窗口如何显示你可以从SW_XXXX中选一个也可以用WinMain传进来的参数还记得WinMain的最后一个参数吗   四、更新窗口可选 为什么更新窗口这一步可有可无呢因为只要程序在运行着只要不是最小化只要窗口是可见的那么我们的应用程序会不断接收到WM_PAINT通知。这里先不说后面你会明白的。好了更新窗口当然是调用UpdateWindow函数。 [cpp] view plaincopyprint? // 更新窗口  UpdateWindow(hwnd);     五、消息循环 Windows操作系统是基于消息控制机制的用户与系统之间的交互程序与系统之间的交互都是通过发送和接收消息来完成的。就好像军队一样命令一旦传达就要执行当然我们的应用程序和军队不一样我们收到指令不一要执行我们是可以选择性地执行。 我们知道代码是不断往前执行的像我们刚才写的WinMain函数一样如果你现在运行程序你会发现什么都没有是不是程序不能运行呢不是其实程序是运行了只是它马上结束了只要程序执行跳出了WinMain的右大括号程序就会结束了。那么要如何让程序不结束了可能大家注意到我们在C程序中可以用一个getchar()函数来等到用户输入这样程序就人停在那里直到用户输入内容。但我们的窗口应用不能这样做因为用户有可能进行其他操作如最小化窗口移动窗口改变窗口大小或者点击窗口上的按钮等。因此我们不能简地弄一个getchar在那里这样就无法响应用户的其他操作了。 可以让程序留在某处不结束的另一个方法就是使用循环而且是死循环这样程序才会永久地停在某个地方但这个死循环必须具有跳出的条件不然你的程序会永久执行直达停电或者把电脑砸了。 这样消息循环就出现了只要有与用户交互系统人不断地向应用程序发送消息通知因为这些消息是不定时不断发送的必须有一个绶冲区来存放就好像你去银行办理手续要排队一样我们从最前端取出一条一条消息处理后面新发送的消息会一直在排队直到把所有消息处理完这就是消息队列。要取出一条消息调用GetMessage函数。函数会传入一个MSG结构体的指针当收到消息会填充MSG结构体中的成员变量这样我们就知道我们的应用程序收到什么消息了直到GetMessage函数取不到消息条件不成立循环跳出这时应用程序就退出。MSG的定义如下 [cpp] view plaincopyprint? typedef struct tagMSG {    HWND   hwnd;    UINT   message;    WPARAM wParam;    LPARAM lParam;    DWORD  time;    POINT  pt;  } MSG, *PMSG, *LPMSG;   hwnd不用说了就是窗口句柄哪个窗口的句柄还记得WindowProc回调函数吗你把这个函数交给了谁来处理hwnd就是谁的句柄比如我们上面的代码我们是把WindowProc赋给了新注册的窗口类并创建了主窗口返回一个表示主窗口的句柄所以这里MSG中的hwnd指的就是我们的主窗口。 message就是我们接收到的消息看到它是一个数字无符号整型所以我们操作的所有消息都是数字来的。wParam和lParam是消息的附加参数其实也是数值来的。通常lParam指示消息的处理结果不同消息的结果返回值不同具体可参阅MSDN。 有了一个整型的值来表示消息我们为什么还需要附加参数呢你不妨想一下如果接收一条WM_LBUTTONDOWN消息即鼠标左键按下时发送的通知消息那么我们不仅知道左键按下这件事我们更感趣的是鼠标在屏幕上的哪个坐标处按下左键按了几下这时候你公凭一条WM_LBUTTONDOWN消息是无法传递这么多消息的。可能我们需要把按下左键时的坐标放入wParam参数中最典型的就是WM_COMMAND消息因为只要你使用菜单点击按钮都会发送这样一条消息那么我怎么知道用户点了哪个按钮呢如果窗口中只有一个按钮那好办用户肯定单击了它但是如果窗口上有10个按钮呢而每一个按钮被单击都会发送WM_COMMAND消息你能知道用户点击了哪个按钮吗所以我们要把用户点击了的那个按钮的句柄存到lParam参数中这样一来我们就可以判断出用户到底点击了哪个按钮了。 GetMessage函数声明如下 [cpp] view plaincopyprint? BOOL WINAPI GetMessage(    _Out_     LPMSG lpMsg,    _In_opt_  HWND hWnd,    _In_      UINT wMsgFilterMin,    _In_      UINT wMsgFilterMax  );   这个函数在定义时带了一个WINAPI现在按照前面我说的方法你应该猜到它就是一个宏而真实的值是__stdcall前文中说过了。 第一个参数是以LP开头还记得吗我说过的你应该想到它就是 MSG* 一个指向MSG结构的指针。第二个参数是句柄通常我们用NULL因为我们会捕捉整个应用程序的消息。后面两个参数是用来过滤消息的指定哪个范围内的消息我接收在此范围之外的消息我拒收如果不过滤就全设为0.。返回值就不说了自己看。 [cpp] view plaincopyprint? // 消息循环  MSG msg;  while(GetMessage(msg, NULL, 0, 0))  {      TranslateMessage(msg);      DispatchMessage(msg);  }   TranslateMessage是用于转换按键信息的因为键盘按下和弹起会发送WM_KEYDOWN和WM_KEYUP消息但如果我们只想知道用户输了哪些字符这个函数可以把这些消息转换为WM_CHAR消息它表示的就是键盘按下的那个键的字符如“A”这样我们处理起来就更方便了。 DispatchMessage函数是必须调用的它的功能就相当于一根传送带每收到一条消息DispatchMessage函数负责把消息传到WindowProc让我们的代码来处理如果不调用这个函数我们定义的WindowProc就永远接收不到消息你就不能做消息响应了你的程序就只能从运行就开始死掉了没有响应。   六、消息响应 其实现在我们的应用程序是可以运行了因为在WindowProc中我们调用了DefWindowProc函数消息我们不作任何处理又把控制权路由回到操作系统来默认处理所以整个过程中我们现在的消息循环是成立的只不过我们不做任何响应罢了。 好的现在我把完整的代码贴一下方便你把前面我们说的内容串联起来。 [cpp] view plaincopyprint? #include Windows.h  // 必须要进行前导声明  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  );    // 程序入口点  int CALLBACK WinMain(      _In_  HINSTANCE hInstance,      _In_  HINSTANCE hPrevInstance,      _In_  LPSTR lpCmdLine,      _In_  int nCmdShow    )  {      // 类名      WCHAR* cls_Name  LMy Class;      // 设计窗口类      WNDCLASS wc;      wc.hbrBackground  (HBRUSH)COLOR_WINDOW;      wc.lpfnWndProc  WindowProc;      wc.lpszClassName  cls_Name;      wc.hInstance  hInstance;      // 注册窗口类      RegisterClass(wc);        // 创建窗口      HWND hwnd  CreateWindow(          cls_Name,           //类名要和刚才注册的一致          L我的应用程序,  //窗口标题文字          WS_OVERLAPPEDWINDOW, //窗口外观样式          38,             //窗口相对于父级的X坐标          20,             //窗口相对于父级的Y坐标          480,                //窗口的宽度          250,                //窗口的高度          NULL,               //没有父窗口为NULL          NULL,               //没有菜单为NULL          hInstance,          //当前应用程序的实例句柄          NULL);              //没有附加数据为NULL      if(hwnd  NULL) //检查窗口是否创建成功          return 0;        // 显示窗口      ShowWindow(hwnd, SW_SHOW);        // 更新窗口      UpdateWindow(hwnd);        // 消息循环      MSG msg;      while(GetMessage(msg, NULL, 0, 0))      {          TranslateMessage(msg);          DispatchMessage(msg);      }      return 0;  }  // 在WinMain后实现  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  )  {      return DefWindowProc(hwnd, uMsg, wParam, lParam);  }   所有代码看上去貌似很正常也遵守了流程设计窗口类注册窗口类创建窗口显示窗口更新窗口消息循环。是吧这段代码看上去毫无破绽运行应该没问题吧。好如果你如此自信那就试试吧。 按下F5试试运行。哈哈结果会让很多人失望很多初学者就是这样一切看起来好像正常于是有人开始骂VC是垃圾是编译器有bug也有人开始想放弃了妈的这么难不学了。人啊总是这样老指责别人的问题从不在自己身上找问题是真的VC的bug吗 我前面说了这段代码貌似很正常呵呵你看到问题在哪吗给你两分钟来找错。我提示一下这个程序没有运行是因为主窗口根本就没有创建因为我在代码里面做了判断如果窗口顺柄hwnd为NULL就退出现在程序一运行就退出了明显是窗口创建失败。 ………… 好了不用找了很多人找不出来尤其是许多初学者不少人找了一遍又一遍都说没有错误至少代码提示没说有错编译运行也没报错所以不少人自信地说代码没错。 其实你是对的代码确实没有错而问题就出在WNDCLASS结构上认真看一下MSDN上有关RegisterClass函数说明中的一句话这句话很多人没注意到但它很关键。 You must fill the structure with the appropriate class attributes before passing it to the function. 现在你明白了吧还不清楚没关系看看我把代码这样改一下你就知道了。 [cpp] view plaincopyprint? // 设计窗口类  WNDCLASS wc;  wc.cbClsExtra  0;  wc.cbWndExtra  0;  wc.hCursor  LoadCursor(hInstance, IDC_ARROW);;  wc.hIcon  LoadIcon(hInstance, IDI_APPLICATION);;  wc.lpszMenuName  NULL;  wc.style  CS_HREDRAW | CS_VREDRAW;  wc.hbrBackground  (HBRUSH)COLOR_WINDOW;  wc.lpfnWndProc  WindowProc;  wc.lpszClassName  cls_Name;  wc.hInstance  hInstance;   现在你运行一下你一定能看到窗口。 但现在你对窗口无法进行操作因为后续的代码还没完成。 为什么现在又可以了呢MSDN那句话的意思就是说我们在注册窗口类之前必须填充WNDCLASS结构体何为填充就是要为结构的所有成员赋值就算不需要你也要为它赋一个NULL或0因为结构在创建时没有对成员进行初始化这就导致变量无法正确的分配内存最后注册失败。 那么如果一个结构体成员很多而我只需要用到其中三个其他的也要初始化是不是很麻烦是的除了为每个成员赋值还有一种较简单的方法就是在声明变量时给它赋一对大括号里面放置结构体的应该分配内存的大小如 [cpp] view plaincopyprint? // 设计窗口类  WNDCLASS wc  { sizeof(WNDCLASS) };  wc.hbrBackground  (HBRUSH)COLOR_WINDOW;  wc.lpfnWndProc  WindowProc;  wc.lpszClassName  cls_Name;  wc.hInstance  hInstance;   这样一来我们也发现窗口也可以成功创建。 我们还可以更简单直接把sizeof也去掉在声明变量时直接赋一对空的大括号就行了就如这样。 [cpp] view plaincopyprint? WNDCLASS wc  { };   这样写更简单窗口类同样可以正常注册。大括号代表的是代码块这样结构体有了一个初值因此它会按照结构体的大小分配了相应的内存。   为什么会这样呢这里涉及到一个关于结构体的一个很有趣的赋值方式。我们先放下我们这个例子下面我写一个简单的例子你就明白了。 [cpp] view plaincopyprint? #include stdio.h   typedef struct rectStruct   {       int x;       int y;       int width;       int height;   } RECT, *PRECT;     void main()   {       RECT rect  { 0, 0, 20, 30 };       printf(矩形的坐标是%d, %d\n矩形的大小%d , %d, rect.x, rect.y, rect.width, rect.height);       getchar();   }   在本例中我们定义了一个表示矩形的结构体 RECT 它有四个成员分别横坐标纵坐标宽度高度但是我们在声明和赋值中我们只用了一对大括号把每个成员的值按照定义的顺序依次写到大括号中即{ 0, 0, 20, 30 }x的值为0y的值为0width为20height的值为30。 也就是说我们可以通过这种简单的方法向结构变量赋值注意值的顺序要和成员变量定义的顺序相同。 现在回到我们的Windows程序来我们明白了这种赋值方式对于 WNDCLASS wc {  } 就不难理解了这样虽然大括号里面是空的其实它已经把变量初始化了都赋了默认值这样一来就可以正确分配内存了。   七、为什么不能退出 通常情况下当我们的主窗口关闭后应用程序应该退出木马程序除外但是我们刚才运行后发现为什么我的窗口关了但程序不退出呢前面我说了要退出程序就要先跳出消息循环和关闭哪个窗口无关。因此我们要解决两个问题 1、如果跳出消息循环 2、什么时候退出程序。 其实两个问题是可以合并到一起解决。 首先要知道当窗口被关闭为窗口所分配的内存会被销毁同时我们会收到一条WM_DESTROY消息因而我们只要在收到这条消息时调用PostQuitMessage函数这个函数提交一条WM_QUIT消息而在消息循环中WM_QUIT消息使GetMessage函数返回0这样一来GetMessage返回FALSE就可以跳出消息循环了这样应用程序就可以退出了。 所以我们要做的就是捕捉WM_DESTROY消息然后PostQuitMessage. [cpp] view plaincopyprint? // 在WinMain后实现  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  )  {      switch(uMsg)      {      case WM_DESTROY:          {              PostQuitMessage(0);              return 0;          }      }      return DefWindowProc(hwnd, uMsg, wParam, lParam);  }   我们会收到很多消息所以用switch判断一下是不是WM_DESTROY消息如果是退出应用程序。 好了这样我们一个完整的Windows应用程序就做好了。 下面是完整的代码清单。 [cpp] view plaincopyprint? #include Windows.h  // 必须要进行前导声明  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  );    // 程序入口点  int CALLBACK WinMain(      _In_  HINSTANCE hInstance,      _In_  HINSTANCE hPrevInstance,      _In_  LPSTR lpCmdLine,      _In_  int nCmdShow    )  {      // 类名      WCHAR* cls_Name  LMy Class;      // 设计窗口类      WNDCLASS wc  { };      wc.hbrBackground  (HBRUSH)COLOR_WINDOW;      wc.lpfnWndProc  WindowProc;      wc.lpszClassName  cls_Name;      wc.hInstance  hInstance;      // 注册窗口类      RegisterClass(wc);        // 创建窗口      HWND hwnd  CreateWindow(          cls_Name,           //类名要和刚才注册的一致          L我的应用程序,  //窗口标题文字          WS_OVERLAPPEDWINDOW, //窗口外观样式          38,                 //窗口相对于父级的X坐标          20,                 //窗口相对于父级的Y坐标          480,                //窗口的宽度          250,                //窗口的高度          NULL,               //没有父窗口为NULL          NULL,               //没有菜单为NULL          hInstance,          //当前应用程序的实例句柄          NULL);              //没有附加数据为NULL      if(hwnd  NULL) //检查窗口是否创建成功          return 0;        // 显示窗口      ShowWindow(hwnd, SW_SHOW);        // 更新窗口      UpdateWindow(hwnd);        // 消息循环      MSG msg;      while(GetMessage(msg, NULL, 0, 0))      {          TranslateMessage(msg);          DispatchMessage(msg);      }      return 0;  }  // 在WinMain后实现  LRESULT CALLBACK WindowProc(      _In_  HWND hwnd,      _In_  UINT uMsg,      _In_  WPARAM wParam,      _In_  LPARAM lParam  )  {      switch(uMsg)      {      case WM_DESTROY:          {              PostQuitMessage(0);              return 0;          }      }      return DefWindowProc(hwnd, uMsg, wParam, lParam);  }   转载于:https://www.cnblogs.com/lvdongjie/p/4502532.html
http://www.ihoyoo.com/news/133515.html

相关文章:

  • 公司建了网站怎么做分录广东室内设计学校
  • 各类设计型网站上海注册公司一网通办
  • 韩国大型门户网站潼南县大潼建设有限公司网站
  • 上海 企矩 网站建设广州有名的传媒公司有哪些
  • 品牌网站建设相关问题oppo手机网站建设策划方案
  • 网站建设招聘网asp net网站建设
  • 垂直门户网站建设备案用网站建设方案
  • 做网站为什么要用源码学做网站要什么学历
  • 培训网站源码学视频剪辑去哪里学比较好
  • 知名的家居行业网站开发怎么评价网站的好坏
  • 淘宝网首页登录网页版凯里seo排名优化
  • 淄博网站制作公司推广开电商网站需要多少钱
  • 那个外贸网站做的好qifeiye做的网站如何
  • 企业网站建设销售前景汽车 营销 网站建设
  • 辽宁省档案网站建设菏泽做公司简介网站
  • 网站建设合同】哪一家好
  • 取消网站的通知网站开发工程师职位要求
  • 做外贸英语网站山东优质校建设专题网站
  • 能发锚文本的网站彩票站自己做网站
  • 做网站编程语言网站更新迭代
  • 上海网站建设广告语网站备案系统登陆不上
  • 长沙口碑好网站建设公司老外做中文网站
  • 衡阳市做淘宝网站建设软件开发和大数据哪个前景好
  • 品牌网站推广软件网站备案被注销
  • 云空间网站网红营销推广
  • 大连城乡建设网站广东深圳旅游必去十大景点
  • 织梦网站栏目字体怎么调洗发水营销推广软文800字
  • 中石建基础设施建设有限公司网站购买网站空间大小
  • 临沂企业做网站wordpress管理员页面
  • 做rom网站辽宁省住房和城乡建设厅网站换了