MFC中的InitInstance()函数应该怎样理解

能够理解为主函数吗?还是主函数的入口函数?
2024-11-13 04:36:57
推荐回答(4个)
回答(1):

不是,是实例的初始化函数;比如分配内存空间啊什么的;
MFC的WinMain

使用MFC编程的程序员刚开始都会提出这样一个问题:我的程序是从哪儿开始执行的?回答是:从WinMain()开始执行的。提出这样的问题是由于在他们所编写的MFC应用中看不到WinMain()函数。这个函数是隐藏在MFC框架中,MFC的设计者将它作得很通用(这主要得益于Window的消息驱动的编程机制,使得作一个通用的WinMain()很容易),因此在一般情况下,无需更改WinMain()的代码,MFC的设计者也不提倡程序员修改WinMain()的代码。在MFC中,实际实现WinMain()的代码是AfxWinMain()函数(根据其前缀Afx就知道这是一个全局的MFC函数)。

一个Win32应用程序(或进程)是由一个或多个并发的线程组成的,其中第一个启动的线程称为主线程,在Window下,一般将线程分成两大类,界面线程和工作线程,工作线程就是一般的线程,它没有窗口,没有消息队列等,界面线程拥有一个或多个窗口,拥有一个消息队列和其他专属于界面线程的元素。在讨论AfxWinMain()之前,首先要简略提一下MFC中的两个重要的类,CWinThread和CWinApp,CWinThread是用来封装界面线程的类,CWinApp是从CWinThread派生而来的。在CWinThread中,有两个很重要的虚拟函数InitInstance()和ExitInistance(),MFC的程序员应该对这两个函数应该很熟悉。在CWinApp中,增加了另外一个虚拟函数InitApplication(),讨论AfxWinMain()的主要目的是看这些函数是如何被调用的。

AfxWinMain()的代码如下:

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

ASSERT(hPrevInstance == NULL); file://在win32下,hPrevInstance始终为NULL

int nReturnCode = -1;

CWinThread* pThread = AfxGetThread();

CWinApp* pApp = AfxGetApp();

// AFX internal initialization

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

goto InitFailure;

// App global initializations (rare)

if (pApp != NULL && !pApp->InitApplication())

goto InitFailure;

// Perform specific initializations

if (!pThread->InitInstance())

{

if (pThread->m_pMainWnd != NULL)

{

TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");

pThread->m_pMainWnd->DestroyWindow();

}

nReturnCode = pThread->ExitInstance();

goto InitFailure;

}

nReturnCode = pThread->Run();

InitFailure:

AfxWinTerm();

return nReturnCode;

}

在上面的代码中,AfxGetThread()返回的是当前界面线程对象的指针,AfxGetApp()返回的是应用程序对象的指针,如果该应用程序(或进程)只有一个界面线程在运行,那么这两者返回的都是一个全局的应用程序对象指针,这个全局的应用程序对象就是MFC应用框架所默认的theApp对象(每次使用AppWizard生成一个SDI或MDI应用程序时,AppWizard都会添加CYourApp theApp这条语句,AfxGetApp()返回的就是这个theApp的地址)。

CWinApp::InitApplication(), CWinThread::InitInstance(), CWinThread::ExitInstance()是如何被调用的,从上面的代码一看就知,我不再赘述。下面我们把焦点放在CWinThread::Run()上。

MFC的控制中心――CWinThread::Run()

说CWinThread::Run()是MFC的控制中心,一点也没有夸大。在MFC中,所有来自于消息队列的消息的分派都是在CWinThread::Run()函数中完成的,同AfxWinMain()一样,这个函数也是对程序员是不可见的,其道理同AfxWinMain()的一样。

首先要提的一点是,对每条从消息队列取出来的消息,MFC根据消息的类型,按照某个特定的模式进行分发处理,这个分发模式是MFC自己定义的。固定的消息分发流程和在这个流程中的可动态改变其行为的虚拟函数就构成了MFC的消息分发模式。应用程序可以通过重载这些虚拟函数,来局部定制自己的的消息分发模式。正是通过这些虚拟函数,MFC为应用程序提供了足够的灵活性。下面讨论的所有代码都来自于MFC源代码中的threadcore.cpp文件,它们都是CWinThread的成员。

CWinThread::Run()的结构

CWinThread::Run()的代码如下:

int CWinThread::Run()

{

ASSERT_VALID(this);

// for tracking the idle time state

BOOL bIdle = TRUE;

LONG lIdleCount = 0;

// acquire and dispatch messages until a WM_QUIT message is received.

for (;;)

{

// phase1: check to see if we can do idle work

while (bIdle &&

!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))

{

// call OnIdle while in bIdle state

if (!OnIdle(lIdleCount++))

bIdle = FALSE; // assume "no idle" state

}

// phase2: pump messages while available

do{

// pump message, but quit on WM_QUIT

if (!PumpMessage()) return ExitInstance();

// reset "no idle" state after pumping "normal" message

if (IsIdleMessage(&m_msgCur))

{

bIdle = TRUE;

lIdleCount = 0;

}

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

}

ASSERT(FALSE); // not reachable

}

CWinThread::Run()的处理过程如下:

先根据空闲标志以及消息队列是否为空这两个条件判断当前线程是否处于空闲状态(这个“空闲”的含义同操作系统的含义不同,是MFC自己所谓的“空闲”),如果是,就调用CWinThread::OnIdle(),这也是我们比较熟悉的一个虚拟函数。

如果不是,从消息队列中取出消息,进行处理,直到消息队列为空。

在这里,我们发现,MFC不是调用GetMessage()从线程消息队列中取消息,而是调用PeekMessage()。其原因在于,GetMessage()是一个具有同步行为的函数,如果消息队列中没有消息,GetMessage()会一直阻塞,使得线程处于睡眠状态,直到消息队列中有一条或多条消息,操作系统才会唤醒该线程,GetMessage()才会返回,如果线程处于睡眠状态了,就不会使线程具有MFC所谓的“空闲”状态了;而PeekMessage()则是一个具有异步行为的函数,如果消息队列中没有消息,它马上返回0,不会导致线程处于睡眠状态。

在上面的代码中,有两个函数值得探讨,一个是空闲处理函数OnIdle(),另外一个是消息分发处理函数PumpMessage()。不要忽视CWinThread的OnIdle()函数,它作了很多有意义的事情。下面讨论PumpMessage(),OnIdle()将在后面的章节里讨论。

CWinThread::Run()的核心――CWinThread::PumpMessage()

标题强调了PumpMessage()的重要性,Run()是MFC的控制中心,而PumpMessage()又是Run()的核心,所以从MFC的真正控制中心是PumpMessage()。PumpMessage()的代码极其简单:

BOOL CWinThread::PumpMessage()

{

ASSERT_VALID(this);

if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))

return FALSE;

// process this message

if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))

{

::TranslateMessage(&m_msgCur);

::DispatchMessage(&m_msgCur);

}

return TRUE;

}

首先,PumpMessage()调用GetMessage()从消息队列中取一条消息,由于PumpMessage()是在消息队列中有消息的时候才被调用的,所以GetMessage()会马上返回,根据其返回值,判断当前取出的消息是不是WM_QUIT消息(这个消息一般对是通过调用PostQuitMessage()放入线程消息队列的),如果是,就返回FALSE,CWinThread::Run()该退出了,CWinThread::Run()直接调用CWinThread::ExitInstance()退出应用程序。在GetMessage()的后面是我们所熟悉的TranslateMessage()和DispatchMessage()函数。

可以看出,是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。

就我个人观点而言,正是有了这个PreTranslateMessage(),才使得MFC能够灵活的控制消息的分发模式,可以说,PreTranslateMessage()就是MFC的消息分发模式。

<三>MFC的特色――PreTranslateMessage()

经过层层扒皮,终于找到了CWinThread::Run()最具特色的地方,这就是PreTranslateMessage()函数。同前面使用SDK编写的显示”Hello, world!”程序的消息循环不同的地方在于,MFC多了这个PreTranslateMessage(),PreTranslateMessage()最先获得了应用程序的消息处理权!下面我们对PreTranslateMessage()进行剥皮式分析。同前面一样,首先看看实际的PreTranslateMessage()的代码:

BOOL CWinThread::PreTranslateMessage(MSG* pMsg)

{

ASSERT_VALID(this);

// if this is a thread-message, short-circuit this function

if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg)) return TRUE;

// walk from target to main window

CWnd* pMainWnd = AfxGetMainWnd();

if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg)) return TRUE;

// in case of modeless dialogs, last chance route through main

// window's accelerator table

if (pMainWnd != NULL)

{

CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);

if (pWnd->GetTopLevelParent() != pMainWnd)

return pMainWnd->PreTranslateMessage(pMsg);

}

return FALSE; // no special processing

}

PreTranslateMessage()的处理过程如下:

首先判断该消息是否是一个线程消息(消息的窗口句柄为空的消息),如果是,交给DispatchThreadMessageEx()处理。我们暂时不管DispatchThreadMessageEx(),它不是我们讨论的重点。

调用CWnd::WalkPreTranslateTree()对该消息进行处理,注意该函数的一个参数是线程主窗口的句柄,这是PreTranslateMessage()的核心代码,在后面会对这个函数进行详细的分析。

对于非模式对话框,这特别的、额外的处理。

下面详细讨论一下CWnd::WalkPreTranslateTree()函数,它的代码很简单:

BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)

{

ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));

ASSERT(pMsg != NULL);

// walk from the target window up to the hWndStop window checking

// if any window wants to translate this message

for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))

{

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

if (pWnd != NULL)

{

// target window is a C++ window

if (pWnd->PreTranslateMessage(pMsg))

return TRUE; // trapped by target window (eg: accelerators)

}

// got to hWndStop window without interest

if (hWnd == hWndStop)

break;

}

return FALSE; // no special processing

}

CWnd::WalkPreTranslateTree()的所使用的策略很简单,拥有该消息的窗口最先获得该消息的处理权,如果它不想对该消息进行处理(该窗口对象的PreTranslateMessage()函数返回FALSE),就将处理权交给它的父亲窗口,如此向树的根部遍历,直到遇到hWndStop(在CWinThread::PreTranslateMessage()中,hWndStop表示的是线程主窗口的句柄)。记住这个消息处理权的传递方向,是由树的某个一般节点或叶子节点向树的根部传递!

小结:

下面对这一章作一个小结。

MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来,在下面的一章会对MFC的实现作详细介绍。

只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在

传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理,在某些情况下,要仔细处理,以免漏掉消息。

回答(2):

MFC 中InitInstance()是应用初始化函数。
1.函数原型:
CWinApp::InitInstance
virtual BOOL InitInstance( );
2.返回值:

如果初始化成功,则返回非零值;否则返回0。
InitInstance函数的返回值为BOOL类型,所谓的BOOL(布尔)类型,在C语言中就是int,它实际就是一个宏定义:
typedef int BOOL
通常我们使用BOOL类型来决定“是”以及“否”,“是”就是TRUE,“否”就是FALSE。TRUE和FALSE的值就是1和0,它们也是通过宏来直接定义的常量。
3.深入理解:

MFC对WindowsAPI进行了封装。在用向导编译成的二进时代码,MFC编译器链接器把源文件编译成PE文件格式存储在磁盘上。程序执行的时候,从PE文件头开始执行,在进入Winmain函数之前,进行一系列的必备的初始化。 MFC对这一系列的过程进行了封装。提供给编程人员的第一个裸露程序入口就是CWinApp的InitInstance(),其实程序的入口依然是WinMain()函数。大家都知道,每个程序都有拥有一个进程,每个进程至少有一个线程就是主线程。CWinThread类是MFC用来封装线程的,这个主线程就是在WinMain函数中创建的,包括UI线程和工作者线程。因此每个MFC程序至少使用一个CWinThread派生类。被MFC程序员熟知的CWinApp应用类就从这里派生。
InitInstance是CWinThread的一个虚函数,InitInstance就是“初始化实例”的意思,可见,它是在实例创建时首先被调用的。应用程序总要重载这个虚函数,进行系统设置,创建运行环境。例如,主窗口一定要在InitInstance()中创建,因为该函数退出后就进入该线程的消息循环。
MFC执行流程: _tWinMain(WinMain的别名,用define替换的)->AfxWinMain->初始化线程,调用InitInstance初始化窗口,调用Run函数进入消息循环。

回答(3):

看看MSDN的解释:实际上windows允许一个程序同时运行多次,每一次都称为一个“实例”(instance)。在每一个实例开始的时候系统都会调用initinstance()这个函数进行初始化。

The Windows operating system allows you to run more than one copy, or "instance," of the same application. WinMain calls InitInstance every time a new instance of the application starts.

The standard InitInstance implementation created by the MFC Application Wizard performs the following tasks:

As its central action, creates the document templates that in turn create documents, views, and frame windows. For a description of this process, see Document Template Creation.

Loads standard file options from an .ini file or the Windows registry, including the names of the most recently used files.

Registers one or more document templates.

For an MDI application, creates a main frame window.

Processes the command line to open a document specified on the command line or to open a new, empty document.

You can add your own initialization code or modify the code written by the wizard.

Note
MFC applications must be initialized as single threaded apartment (STA). If you call CoInitializeEx in your InitInstance override, specify COINIT_APARTMENTTHREADED (rather than COINIT_MULTITHREADED). For more information, see PRB: MFC Application Stops Responding When You Initialize the Application as a Multithreaded Apartment (828643) at http://support.microsoft.com/default.aspx?scid=kb;en-us;828643.

回答(4):

我的理解是主函数的入口函数