• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

Windows编程之从控制台到SDK窗口

VC/MFC/Windows 弦苦 2149次浏览 0个评论

1.典型C/C++程序

/*HelloC.c*/ #include<stdio.h> int main(int argc, char *argv[]) { printf(“Hello C!/n”); return 0; } // HelloCPP1.cpp #include<iostream> int main(int argc, char *argv[]) { std::cout<<“Hello CPP!/n”; return 0; } // HelloCPP2.cpp #include <iostream> using namespace std; class X { public: X() { cout<<“X constructor”<<endl; } ~X() { cout<<“X deconstructor”<<endl; } }; X x; int main() { cout<<“end main”<<endl; return 0; }

2.Windows 编程的第一种方式是传统的SDK方式,Windows操作系统是由C语言编写的,故可采用C语言直接调用windowsAPI函数进行编程

以下为Win SDK应用程序窗口程序框架示例及框架解析。

(1)建立Win32应用程序项目Win32SDK

Visual Studio 2005à文件à新建à项目àVisual C++àwin32àwin32项目àwin32应用程序à空项目(默认使用Unicode字符集)。如果运行时提示找不到mfc80d.dllmsvcr80d.dll文件,在“项目属性à配置属性à清单工具à常规”中的“使用FAT32解决办法”处选择“是”,再重新生成解决方案。

(2)添加源文件Win32SDK.cpp

 

/*以下为典型Windows窗口应用程序框架*/ // Win32 大多数API,包含在windows.h头文件中 #include <windows.h> // 窗口函数声明 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // 应用程序入口函数,参数由系统传入 int WINAPI WinMain( HINSTANCE hInstance, // 程序实例句柄(线索) HINSTANCE hPrevInstance, // 为保持与Win16兼容的句柄 LPSTR lpCmdLine, // 命令行参数 int nCmdShow // 初始化窗口显示方式 ) { LPCTSTR lpszProviderClass = __TEXT(“MyWndClass”); WNDCLASS WndClass; // 窗口类,可为WNDCLASS MSG Msg; // Windows 消息 HWND hWnd; // 窗口句柄 // 窗口类的定义 WndClass.style = CS_HREDRAW|CS_VREDRAW; // 窗口类型 WndClass.lpfnWndProc = (WNDPROC)WndProc; // 指定窗口处理函数 WndClass.cbClsExtra = NULL; // 窗口类无扩展 WndClass.cbWndExtra = NULL; // 窗口实例无扩展 WndClass.hInstance = hInstance; // 窗口类所属实例 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);// 窗口最小化图标,为默认图标 WndClass.hCursor = LoadCursor(NULL,IDC_ARROW); // 用箭头作为鼠标图标 WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 以白色作为窗口颜色 WndClass.lpszMenuName = NULL; // 窗口无菜单 WndClass.lpszClassName = lpszProviderClass; // 窗口类名 // 注册窗口类 if(!RegisterClass(&WndClass)) { MessageBeep(0); // 如注册失败则发出警告 return FALSE; } // 创建窗口 hWnd = CreateWindow( lpszProviderClass, // 注册的窗口类名 __TEXT(“Hello Windows!”), // 窗口标题名 WS_OVERLAPPEDWINDOW, // 窗口的风格 CW_USEDEFAULT, // 窗口左上角的X坐标,取默认值 CW_USEDEFAULT, // 窗口左上角的Y坐标,取默认值 CW_USEDEFAULT, // 窗口右下角的X坐标,取默认值 CW_USEDEFAULT, // 窗口右下角的Y坐标,取默认值 NULL, // 此窗口无父窗口 NULL, // 菜单句柄(此处无) hInstance, // 程序实例句柄 NULL // 指向一个传递给窗口的指针型参数,此处设置为空 ); if(!hWnd) // 创建窗口失败,返回FALSE { return FALSE; } ShowWindow(hWnd, nCmdShow); // 显示窗口 UpdateWindow(hWnd); // 刷新窗口 // 消息循环 while(GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); // 翻译消息 DispatchMessage(&Msg); // 将消息传递给处理函数 } return Msg.wParam; // 返回消息的附加参数 } // 消息处理函数,每当有与本窗口相关的用户交互时,系统调用此回调函数进行处理 LRESULT CALLBACK WndProc( HWND hWnd, // 窗口句柄 UINT nMessage, // 接受到的待处理的消息ID WPARAM wParam, // 消息附参数 LPARAM lParam // 消息附参数 ) { switch(nMessage) { // 处理用户感兴趣的消息 // 响应WM_CREATE消息 case WM_CREATE: MessageBox(hWnd, __TEXT(“收到WM_CREATE消息!”), __TEXT(“通知”), MB_OK); break; // 响应WM_LBUTTONDOWN消息 case WM_LBUTTONDOWN: MessageBox(hWnd, __TEXT(“收到WM_LBUTTONDOWN消息!”), __TEXT(“通知”), MB_OK); break; // 响应WM_CHAR消息 case WM_CHAR: TCHAR buffer[30]; wsprintf(buffer, __TEXT(“你刚才按下了%c键!”), wParam); MessageBox(hWnd, buffer, __TEXT(“通知”), MB_OK); break; // 响应WM_DESTROY消息 case WM_DESTROY: PostQuitMessage(0); // 发出WM_QUIT消息退出 break; // 调用系统默认处理函数DefWindowProc() default: return DefWindowProc(hWnd,nMessage,wParam,lParam); } return FALSE; // 用户处理后返回,将控制权交由系统。 }

Windows编程之从控制台到SDK窗口

 

3.Windows下的C/C++应用程序运行机制

1subsystem选项

VC6

Project SettingsàC/C++àPreprocessoràPreprocessor Definitions_CONSOLE_WINDOWS

Project SettingsàC/C++àProject Options/D “_CONSOLE”/D “_WINDOWS “

Project SettingsàLinkà Project Options/subsystem:console/subsystem:windows

VC2005

项目属性àC/C++à预处理器à预处理器定义:_CONSOLE_WINDOWS

项目属性àC/C++à命令行:/D “_CONSOLE”/D “_WINDOWS “

项目属性à链接器à命令行:/SUBSYSTEM:CONSOLE/SUBSYSTEM:WINDOWS

项目属性à链接器à 系统à 子系统:控制台(/SUBSYSTEM:CONSOLE)Windows (/SUBSYSTEM:WINDOWS)

2)可执行文件的Entry Point

可执行文件都有一个Entry Point(起始地址),LINK时可以用/entry指定。

缺省情况下,如果subsystem“console”Entry Point mainCRTStartup(ANSI)wmainCRTStartuup(UNICODE),即:

/subsystem:”console” /entry:”mainCRTStartup” (ANSI)

/subsystem:”console” /entry:”wmainCRTStartuup” (UNICODE)

mainCRTStartup wmainCRTStartuup 会调用用户编写的mainwmain在进入可执行文件的代码前,系统将会创建一个控制台窗口。

值得一提的是,在进入应用程序的Entry Point前,Windows的装载器已经做过C变量的初始化,有初值的全局变量拥有了它们的初值,没有初值的变量被设为0

如果subsystem“windows”Entry PointWinMain(ANSI)wWinMain(UINCODE),即:

/subsystem:”windows” /entry:”WinMainCRTStartup” (ANSI)

/sbusystem:”windows” /entry:”wWinMainCRTStartup” (UINCODE)

WinMainCRTStartup wWinMainCRTStartup 会调用 用户编写的WinMain wWinMain窗口由用户调用CreateWindow(Ex)创建

VC2005中,项目属性à链接器à高级处有入口点选项,在编写Windows Mobile ANSI控制台程序时需指定入口点为mainWCRTStartupWindowsCE (/SUBSYSTEM:WINDOWSCE))。

VC2005中,建一个简单的Console程序(main为入口函数),编译后F10将进入crtexe.c,查看调用堆栈如下:

àint main(int argc, char* argv[])

àint __tmainCRTStartup(void)

àint mainCRTStartup(void)

    <1>mainCRTStartup函数只是简单的调用__tmainCRTStartup函数,其代码如下:

#ifdef _WINMAIN_

int WinMainCRTStartup(void) // UNICODE版本:int wWinMainCRTStartup(void)

#else

int mainCRTStartup(void) // UNICODE版本:int wmainCRTStartup(void)

{

        __security_init_cookie();

 

        return __tmainCRTStartup();

}

    <2>__tmainCRTStartup函数调用main/WinMain函数,其代码如下:

int __tmainCRTStartup(void)

{

//……

#ifdef _WINMAIN_

GetStartupInfo( &StartupInfo );

……

mainret = WinMain(…) // UNICODE版本:int wWinMain(…)

#else  /* _WINMAIN_ */

mainret = main(); // UNICODE版本:int wmain(…)

    //……

    /*

    * do C++ constructors (initializers) specific to this EXE

*/

if (__native_startup_state == __initializing)

    {

          _initterm( __xc_a, __xc_z );

          __native_startup_state = __initialized;

}

    //……

 

    if ( !managedapp )

exit(mainret);

    //……

return mainret;

}

    可以推测上述_WINMAIN_宏是与subsystem相关的一个VC编译器内部宏。

(3)应用程序的启动

    程序是一连串静态的指令,而进程是一个容器,它包含了一系列运行在这个程序实例上下文中的线程使用的资源。

当我们用鼠标点击磁盘上的可执行文件App.exe后,App.exe被装载至内存后就形成了进程,因此进程是一个正在运行的程序的实例。一般我们可以同时启动多个App.exe的实例,即创建同名的多个不同ID的进程。

进程内核对象不是进程本身,仅仅是一个系统用来管理这个进程的一个小的数据结构(PCBProcess Control Block)。进程是不活泼的,程序代码的执行是线程的工作,线程是进程内执行代码的独立实体。一个进程要完成任何的事情,它必须有一个运行在其地址空间的线程。Windows操作系统调用CreateProcess函数创建一个新的进程和该进程的主线程,返回 LPPROCESS_INFORMATION信息。主线程通过执行C/C++运行期启动代码初始化C/C++运行期库,C/C++运行期启动代码又会调用main函数。主线程在运行期间,可以调用CreateThread创建辅助线程,即所谓的多线程。

App.exe进程的主线程入口函数main/WinMain返回后,启动函数mainCRTStartup__tmainCRTStartup)调用C/C++运行期退出函数exit(参数为main/WinMain返回值)

全局变量(包括内置类型和类类型)存储在全局区。exit函数会销毁所有全局的或静态的C++对象,全局C++对象的构造函数在进入应用程序的Entry Point之后,调用用户编写的main/WinMain之前调用。编译器调用atexit记录全局对象的析构函数(dynamic atexit destructor for ‘x’多个函数形成多播链),在exit退出时(main/WinMain返回之后)调用。

exitF11进入crt0dat.c中的doexit,其中析构函数的调用代码片段如下:

/*

* do _onexit/atexit() terminators(if there are any)

* These terminators MUST be executed in reverse order (LIFO)!

*/

_PVFV * onexitbegin = (_PVFV *)_decode_pointer(__onexitbegin);

_PVFV * onexitend   = (_PVFV *)_decode_pointer(__onexitend);

// 依次调用atexit注册的函数(oneixtbeginàonexitend

HelloCPP2.cpp程序在VC6中,没输出“X deconstructor”,在VC2005中输出“X deconstructor”

最后(/* return to OS or to caller */)调用ExitProcessdoexità__crtExitProcess)促使操作系统终止应用程序。

 

参考:

在VC中编译、运行程序的小知识点

如何屏蔽控制台应用程序的窗口?

《深入浅出MFC》第一章àWin32程序基本概念à进程与线程à一个进程的诞生与死亡

Windows核心编程 第五版》4.1编写第一个Windows应用程序。


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明Windows编程之从控制台到SDK窗口
喜欢 (0)

您必须 登录 才能发表评论!

加载中……