文章类型: VC&C++
关键词: VC,MFC,OCX,DLL,资源模块,句柄,切换,GIF,动画,图片,定时器,对话框,DoModal,弹不出
内容摘要: VC基于MFC的OCX中使用对话框显示GIF动画图片和使用时间定时器

VC基于MFC的OCX中使用对话框显示GIF动画图片和使用时间定时器

2018/9/7 16:13:04    来源:apple    阅读:

最近对VC的项目使用OCX进行封装时(使用VC2015的环境进行的开发),由于需要在一个对话框中使用GIF图片进行动画进度效果显示,同时需要使用定时器来显示当前流失的时间,基于这样的一个需求,描述一下实现的过程:

建立了一个对话框,添加了关联该对话框的类PdfCreateWaitDlg,系统默认该类的基类为“CDialogEx”,(由于走了一些弯路,不在啰嗦描述)至此,编译ocx项目,出现了一堆错误,简述如下:譬如,出现“ActiveDoc.h”文件中语句“enum { WM_IDLEUPDATECMDUI = 0x0363 };”引发了一系列莫名其妙的错误[error C2059: 语法错误:“常量”]、[error C2143: 语法错误: 缺少“;”(在“}”的前面)]等等;
    原因分析:在文件“StdAfx.h”中自动添加了语句“#include <afxcontrolbars.h>”而造成的这些错误,估计是由于OCX封装控件的机制问题,直接通过窗体添加类时出现上面的那些错误;
解决办法:文件“StdAfx.h”中自动添加了语句“#include <afxcontrolbars.h>”进行删除,同时添加
对话框基类“CDialogEx”所包含的头文件[#include "afxdialogex.h"]。

其实上面的错误也可以使用该对话框关联类的基类为“CDialog”,这样就不会出现上面的这种错误!

开发上面的这个需求时,在对话框中使用系统的定时器来完成定时功能,对话框的显示方式必须使用模式对话框,即“DoModal()”函数来调用,由于在弹出对话框时还需要执行程序的其他代码,所以弹出对话框必须新建线程来完成对话框的显示和执行,

在开发时,如果使用开辟的线程直接使用对话框的对象来调用函数“DoModal()”来显示,结果执行该函数时出现了错误,查找了一些资料,说需要在对话框弹出前添加下面的代码执行以下:

//!!!!MFC初始化
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
    ??_Module.LogEvent(_T("MFC初始化错误!"), EVENTLOG_ERROR_TYPE );
        return;
}

添加上面的代码后,执行“DoModal()”函数时虽然不出现错误了,但是发现根本不会弹出对话框,又查找资料分析问题,最终使用下面的代码替代上面的代码得到了解决,能够弹出对话框了,

AFX_MANAGE_STATE(AfxGetStaticModuleState());
        CoInitialize(NULL);
    AfxEnableControlContainer();

下面直接上关键代码来说明上面的需求实现过程,

void CUIREVPrintSet::OnCheckPrintparmPrint() 
{
    unsigned int nDummy;
    m_hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadAnimation, this,
        CREATE_SUSPENDED, &nDummy);
    if (!m_hThread)
    {
        TRACE(_T("Draw: Couldn't start a GIF animation thread\n"));
        return;
    }
    else
    {
        bPdfCreateUploadThreadEnd = false;
        bThreadClose = false;
        ResumeThread(m_hThread);
    }

    CUICtrView *pView = (CUICtrView *)((CFrameWnd*) AfxGetApp()->m_pMainWnd)->GetActiveView();
    Update();
//    BOOL IsFilter
    //目的:修改回顾中的打印功能,将原来调用系统的打印更新为下面的自定义的PDF打印。
    pView->SwitchViewREV(12, TRUE);

    //目的:将上面的“生成pdf”按钮和下面的“上传”按钮的功能进行合并。
    //下面为“上传功能”功能的代码
    CHolterDoc    *pDoc = (CHolterDoc *)((CFrameWnd*)AfxGetApp()->m_pMainWnd)->GetActiveDocument();
    //创建XML文件
    pDoc->CreatXML();
    //Sleep(2000);
    //更新创建的PDF文件名称
    pDoc->ChangePdfName();

    bPdfCreateUploadThreadEnd = true;

    //目的:设置并传递容器类CSuperHolterCtrl的对象指针,用于向容器CSuperHolterCtrl发送消息。
    //目的:患者报告生成完成时通知调用者。
    pOcxControl->PostMessage(OCX_EVENT_FINISHREPORT, 0, 0);//发送给父窗口

    WaitForSingleObject(m_hThread, INFINITE);
    OnOK();
}

void CUIREVPrintSet::ThreadAnimation()
{
    ////!!!!MFC初始化
    //if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    //{
    //    //??_Module.LogEvent(_T("MFC初始化错误!"), EVENTLOG_ERROR_TYPE );
    //    return;
    //}
    //目的:不能用上面的语句,虽然能够避免出现文件“afxwin1.inl”中的第24行语句“ASSERT(afxCurrentResourceHandle != NULL);”发生错误,但是弹不出下面的PdfCreateWaitDlg的对话框。
    //目的:使用下面的语句,能够解决文件“afxwin1.inl”中的第24行语句“ASSERT(afxCurrentResourceHandle != NULL);”发生错误,同时也可以弹出PdfCreateWaitDlg的对话框。
    //原因在于资源
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
        CoInitialize(NULL);
    AfxEnableControlContainer();

    PdfCreateWaitDlg dlg;
    dlg.DoModal();
}

UINT WINAPI CUIREVPrintSet::_ThreadAnimation(LPVOID pParam)
{
    ASSERT(pParam);
    CUIREVPrintSet *pREVPrintSet = reinterpret_cast<CUIREVPrintSet *> (pParam);
    pREVPrintSet->ThreadAnimation();

    // this thread has finished its work so we close the handle
    CloseHandle(pREVPrintSet->m_hThread);
    // and init the handle to zero (so that Stop() doesn't Wait on it)
    pREVPrintSet->m_hThread = 0;
    return 0;
}

头文件定义的关键代码如下:

protected:
    afx_msg void OnCheckPrintparmPrint();
    void ThreadAnimation();
    HANDLE m_hThread;
    static UINT WINAPI _ThreadAnimation(LPVOID pParam);

对话框窗体的头文件全部代码如下:

#if !defined(AFX_PdfCreateWaitDlg_H__D1B85304_83A9_4E00_BC5F_181FFCE51271__INCLUDED_)
#define AFX_PdfCreateWaitDlg_H__D1B85304_83A9_4E00_BC5F_181FFCE51271__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "PictureEx.h"
// PdfCreateWaitDlg.h : header file
//
// PdfCreateWaitDlg 对话框
/*
目的:添加对话框“IDD_DLG_PDF_CREATE_WAIT”的关联类。
注意事项:由于OCX封装控件的机制问题,直接通过窗体添加类时出现下面的错误,
编辑OCX项目时出现“ActiveDoc.h”文件中语句“enum { WM_IDLEUPDATECMDUI = 0x0363 };”引
发了一系列莫名其妙的错误[error C2059: 语法错误:“常量”]、[error C2143: 语法错误: 缺少“;”(在“}”的前面)]等等;
原因分析:在文件“StdAfx.h”中自动添加了语句“#include <afxcontrolbars.h>”而造成的这些错误。
解决办法:文件“StdAfx.h”中自动添加了语句“#include <afxcontrolbars.h>”进行删除,同时添加
对话框基类“CDialogEx”所包含的头文件[#include "afxdialogex.h"]。
*/
class PdfCreateWaitDlg : public CDialog
{
// Construction
public:
    PdfCreateWaitDlg(CWnd* pParent = NULL);   // 标准构造函数
    virtual ~PdfCreateWaitDlg();

    // Dialog Data对话框数据
    //{{AFX_DATA(PdfCreateWaitDlg)
    enum { IDD = IDD_DLG_PDF_CREATE_WAIT};
    //}}AFX_DATA
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

protected:
    // Generated message map functions
    //{{AFX_MSG(CQTEdit)
    virtual BOOL OnInitDialog();
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
protected:
    CPictureEx m_Gif;
    CStatic m_passTime;
    int passTimeCount;
    CString sPassTimeCount;
    CFont dlgStaticFont;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_PdfCreateWaitDlg_H__D1B85304_83A9_4E00_BC5F_181FFCE51271__INCLUDED_)

对话框窗体的cpp文件全部代码如下:

// PdfCreateWaitDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "SuperHolter.h"
#include "PdfCreateWaitDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// PdfCreateWaitDlg 对话框
extern bool bPdfCreateUploadThreadEnd;
PdfCreateWaitDlg::PdfCreateWaitDlg(CWnd* pParent /*=NULL*/)
    : CDialog(PdfCreateWaitDlg::IDD, pParent)
{

}

PdfCreateWaitDlg::~PdfCreateWaitDlg()
{
}

void PdfCreateWaitDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //IDC_STATIC_GIF是一个CStatic控件的ID
    DDX_Control(pDX, IDC_STATIC_GIF, m_Gif);
    DDX_Control(pDX, IDC_STATIC_TIME, m_passTime);
}


BEGIN_MESSAGE_MAP(PdfCreateWaitDlg, CDialog)
    ON_WM_TIMER()
END_MESSAGE_MAP()


// PdfCreateWaitDlg 消息处理程序


BOOL PdfCreateWaitDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // TODO:  在此添加额外的初始化
    dlgStaticFont.CreatePointFont(200, "Times New Roman");
    m_passTime.SetFont(&dlgStaticFont);
    passTimeCount = 0;
    sPassTimeCount.Format(_T("%d"), passTimeCount);
    m_passTime.SetWindowText(sPassTimeCount);
    UpdateData(FALSE);
    SetTimer(1, 1000, NULL);
    if (m_Gif.Load("E:\\Holter_OCX\\SupHolterOCX_Xml\\res\\progressbar.gif"))
    {
        m_Gif.Draw();
    }

    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
}

//多个定时器的实现与应用
void PdfCreateWaitDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    switch (nIDEvent)
    {
    case 1:
        if (!bPdfCreateUploadThreadEnd)
        {
            passTimeCount++;
            sPassTimeCount.Format(_T("%d"), passTimeCount);
            m_passTime.SetWindowText(sPassTimeCount);
            UpdateData(FALSE);
        }
        else
        {
            PdfCreateWaitDlg::OnOK();
        }
        break;
    case 2:
        if (!bPdfCreateUploadThreadEnd)
        {
            switch (dotCount)
            {
            case 1:
                sTipDot = ">";
                break;
            case 2:
                sTipDot = ">>";
                break;
            case 3:
                sTipDot = ">>>";
                break;
            case 4:
                sTipDot = ">>>>";
                break;
            case 5:
                sTipDot = ">>>>>";
                break;
            case 6:
                sTipDot = ">>>>>>";
                dotCount = 0;
                break;
            default:
                break;
            }

            m_Tip2.SetWindowText(sTipDot);
            UpdateData(FALSE);
            dotCount++;
        }
        else
        {
            PdfCreateWaitDlg::OnOK();
        }
        break;
    default:
        break;
    }

    CDialog::OnTimer(nIDEvent);
}

对话框中使用了第三方扩展类“PictureEx”,可以百度搜索下载使用,

同时附PictureEx文件下载:PictureEx类下载

实现的效果如下图所示:

图片.png

开辟线程来显示生成文件的已用时间

开发时查找了如下资料:


MFC DLL 资源模块句柄切换

以前写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上 AFX_MANAGE_STATE(AfxGetStaticModuleState())。一直不明白这样做的含义,也一直没有这样做,而且代码也工作 得好好的,所以感觉这好像一句废话。

最近的项目中,需要在DLL里使用MFC生成界面,这才发现一旦资源放在不同的动态库里,而且还和多 线程搅和在一起的时候,事情就变得异常的复杂,以前对MFC的一知半解已经不足与应付了。程序莫名的崩溃,莫名的ASSERT,资源怎样也装载不起来,为 什么呢?每次,总是尝试着,在每一个线程的开始,把AFX_MANAGE_STATE(AfxGetStaticModuleState())添加上去, 或者在某些地方用AfxSetResourceHandler()一把,然后问题就解决了,但是不是很明白到底是怎么回事,总感觉这种解决办法让人很不安 心,仿佛在下一秒问题又会突然冒出来。

前天,这个问题终于发挥到了极致,任我花费了好几个小时,怎样的尝试都不能成功,在项目的关键时候发生这种事情,让我暗暗发誓以后再也不用MFC了。正像很多的电影情节一样,事情最后还是得到了解决,这次我决定不能再这么算了,一定要把这个事情理解得明明白白。

在这里,我遇到的问题就是,如何让DLL里的界面代码使用该DLL的资源(Resource),如何在工作线程里加载有IE控件的对话框?

我问同事,他们是如何实现DLL资源切换的?AFX_MANAGE_STATE(AfxGetStaticModuleState())这就是他们的答案,一如微软的推荐,原来就是这么简单啊!让我们来看看,这句代码到底做了什么?

#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p);

AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState)
{
    m_pThreadState = _afxThreadState;
    m_pPrevModuleState = m_pThreadState->m_pModuleState;
    m_pThreadState->m_pModuleState = pNewState;
}

_AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2()
{  m_pThreadState->m_pModuleState = m_pPrevModuleState; }

原来,就是定义一个局部的对象,利用其构造和析构函数在函数的入口和函数的出口进行State状态的切换,我猜AfxGetStaticModuleState()一定是获取当前代码所在DLL的State。

果然,请看

static _AFX_DLL_MODULE_STATE afxModuleState;

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()
{
    AFX_MODULE_STATE* pModuleState = &afxModuleState;
    return pModuleState;
}


class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE


// AFX_MODULE_STATE (global data for a module)
class AFX_MODULE_STATE : public CNoTrackObject
{
...
    CWinApp* m_pCurrentWinApp;
    HINSTANCE m_hCurrentInstanceHandle;
    HINSTANCE m_hCurrentResourceHandle;
    LPCTSTR m_lpszCurrentAppName;
    BYTE m_bDLL;    // TRUE if module is a DLL, FALSE if it is an EXE

...
    COccManager* m_pOccManager;
...

这里不得不说,MFC把很多的数据都堆放在这里,搞得很复杂,结构性非常的差。
}

afxModuleState是dll的静态成员,自然可以被同样的dll里的代码所访问,但是何时初始化的?


extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
...

        AfxWinInit(hInstance, NULL, _T(""), 0);
...
}

BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine, int nCmdShow)
{
    ASSERT(hPrevInstance == NULL);

    // handle critical errors and avoid Windows message boxes
    SetErrorMode(SetErrorMode(0) |
        SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);

    // set resource handles
    AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    pModuleState->m_hCurrentInstanceHandle = hInstance;
    pModuleState->m_hCurrentResourceHandle = hInstance;

...

}

原来在DLL的入口函数,用该DLL的hInstance初始化了该结构。


到这时候,我们还是不明白,为什么要进行资源切换?前面开始的_afxThreadState到底是什么?好像跟Thread有关系,到底是什么呢?

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

#define THREAD_LOCAL(class_name, ident_name) /
    AFX_DATADEF CThreadLocal<class_name> ident_name;

template<class TYPE>
class CThreadLocal : public CThreadLocalObject

再 往下跟踪,发现其实代码越发生涩难懂,但是基本的功能就是访问当前此行代码的线程的私有数据。所谓线程的私有数据,就是说,不同的线程执行同样的一段代 码,得到的数据可能是不同的。这才想起来,MFC的很多句柄啦,都是保存在全局的Map里的,而且放在线程的私有数据区里,所以跨线程传递MFC对象是很 不安全的。但是,MFC为什么要这么做呢?这个问题,到目前为止,我还是搞不明白。

还是回到开始的代码,资源切换到底是如何进行的?


int CDialog::DoModal()
{
...

    HINSTANCE hInst = AfxGetResourceHandle();
    if (m_lpszTemplateName != NULL)
    {
        hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
        HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
        hDialogTemplate = LoadResource(hInst, hResource);
...
}


_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()
    { ASSERT(afxCurrentResourceHandle != NULL);
        return afxCurrentResourceHandle; }

#define afxCurrentResourceHandle    AfxGetModuleState()->m_hCurrentResourceHandle

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
    _AFX_THREAD_STATE* pState = _afxThreadState;
    AFX_MODULE_STATE* pResult;
    if (pState->m_pModuleState != NULL)
    {
        // thread state's module state serves as override
        pResult = pState->m_pModuleState;
    }
    else
    {
        // otherwise, use global app state
        pResult = _afxBaseModuleState.GetData();
    }
    ASSERT(pResult != NULL);
    return pResult;
}

原 来MFC的对话框装载资源是通过获取当前线程对应的ModuleState保存的ResourceHandler来装载资源的。所以,DLL里的代码,需 要在函数的入口,首先把当前执行线程的ModuleState换成该Dll的State,这样才能装载该dll的资源!这时候,我突然明白过来,为什么需 要要依赖线程的私有数据来保存ModuleState,其实确切的说是传递!--这其实是因为CDialog是存放在另一个DLL里的,比如 MFC40.dll,如果以共享模式连接MFC库的话。而用户自己编写的CDialog的子类并不放在CDialog同样的Dll里,他们如何来传递这个 资源句柄呢?两种解决办法:1,利用参数传递。2,存放在一个公共的地方。前者需要增加参数,显得很麻烦,Win32的API好像就是这样实现的吧?后 者,需要确定这个公共地方在何处?这让人想起来,建立一个公共的动态库?由主程序的提供?再多说一句,J2EE里有一个容器的概念(COM+好像也有,不 知道.NET是如何的),组件都是生存在容器里,这时候我们就可以设想把该数据存放在容器里。不管怎样,MFC的实现就是放在线程的私有数据区,不需要公 共的动态库,也不需要麻烦主程序,它自己就搞定了!它自以为很好的解决方式,很完美,却引发了我们的一系列的问题,特别是不明白就里的人。

关 于资源装载,问题似乎已经解决了,但是还有一点点小麻烦就是,我实现的dll不是以普通的输出函数进行输出的,而是输出类,我可不想在每一个类的成员函数 里添加AFX_MANAGE_STATE(AfxGetStaticModuleState())。怎么办呢?既然已经知道了资源切换的原理,我们添加两 个输出函数,分别对应AFX_MAINTAIN_STATE2的构造和析构函数,在类的使用前后调用,就可以了。或者,分别放在类的构造和析构函数里。又 或者,就声明为成员变量。无论怎样,需要保证的一点就是资源的切换要正确嵌套,不可交叉--这种情况在不同的DLL之间交叉调用的时候会发生。

好 了,现在DLL里的资源可以正确调用了,但是在当Dialog上包含有IE控件的时候,我们还是失败了,为什么呢?我知道对于ActiveX控 件,Dialog需要做一些特殊的处理,AfxEnableControlContainer(),我也知道,要使用COM,需要 CoInitialize(),但是我一直没有想过需要两个一起用才能把IE弄出来,但是最后就是这样的。奇怪的是,如果不是在工作线程里,根本不需要 CoInitialize(),就能装载IE控件的,这个暂时就先不管了。

PROCESS_LOCAL(COccManager, _afxOccManager)

void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager)
{
    if (pOccManager == NULL)
        afxOccManager = _afxOccManager.GetData();
    else
        afxOccManager = pOccManager;
}

#define afxOccManager   AfxGetModuleState()->m_pOccManager

这 样看来,这个_afxOccManager应该是属于整个进程的,整个进程只有一个,就在那个定义它的dll里。但是,你需要把该对象(或者创建一个自定 义的)传给ModuleState(请注意前面的AFX_MODULE_STATE里就包含了该属性),也就是要 AfxEnableControlContainer()一下,这样特定的ModuleState就有了OccManager的信息!但是,请注意,一定 要在目标dll里,正确切换了资源之后,才能进行,如下:

AFX_MANAGE_STATE(AfxGetStaticModuleState());
CoInitialize(NULL);
AfxEnableControlContainer();

至此,这个困扰我很久的问题,终于脉络清晰起来了。


线程创建后为什么要调用CloseHandle

很多程序在创建线程都这样写的: 
............ 
ThreadHandle = CreateThread(NULL,0,.....); 
CloseHandel(ThreadHandle ); 
。。。。。 
这不是刚好创建又关闭了吗?线程怎么运行呢?

================================================

Closing a thread handle does not terminate the associated thread. To remove a thread object, you must terminate the thread, then close all handles to the thread.

================================================

1,线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的.....(说不清楚了),线程句柄是一个内核对象。我们可以通过句柄来操作线程,但是线程的生命周期和线程句柄的生命周期不一样的。线程的生命周期就是线程函数从开始执行到return,线程句柄的生命周期是从CreateThread返回到你CloseHandle()。

2,所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了。

3,如果你CreateThread以后需要对这个线程做一些操作,比如改变优先级,被其他线程等待,强制TermateThread等,就要保存这个句柄,使用完了在CloseHandle。如果你开了一个线程,而不需要对它进行如何干预,CreateThread后直接CloseHandle就行了。

所以 
CloseHandel(ThreadHandle ); 
只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对应的线程做任何干预了。并没有结束线程。

如果你觉得多了一个变量,也可以写为: 
CloseHandel(CreateThread(NULL,0,.....));

================================================================

《windows核心编程》上说调用closehandle(HANDLE)表示创建者放弃对该内核对象的操作。如果该对象的引用对象记数为0就撤消该对象。

====================================================================

在线程创建后马上调用CloseHandle()是个良好的做法,这里不会影响线程的执行,就是因为即使你close了这个handle,它的内部记数也不为零.   但如果你不关,在线程结束后,那个线程对象将滞留于内存中,也就是说你有handle   leak. 
返回这个handle给你,是让你有机会对这个线程实施外部动作,诸如waitforsingleobject之类.

==================================================================

CloseHandle的功能是关闭一个打开的对象句柄,该对象句柄可以是线程句柄,也可以是进程、信号量等其他内核对象的句柄,而ExitThread的功能是终止一个线程,它所接受的参数是一个线程的退出码。 
通过调用CloseHandle可以告知系统,已经完成了对某一内核对象的操作,该函数首先检查调用进程的句柄表,来确认进程是否对该句柄所指向的对象有访问权,如果句柄无效则返回FALSE,如果有效,系统将得到该内核对象的数据结构的地址,把结构中的使用计数成员减1,如果计数变为0,则将从内核中释放该内核对象。 
如果计数还未到0,就意味着还有其他的进程在使用这个内核对象,那么它就不会被释放。 
ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除工作。 
如果需要进一步的信息,您可以参看: 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/handobj_289x.asp 
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/prothred_531g.asp 
等有关的MSDN信息。

==================================================================

线程作为一种资源创建后不只被创建线程引用,我想系统自身为了管理线程也会有一个引用,所以用户线程释放线程句柄后,引用计数也不会是零。引用计数是资源自我管理的一种机制,资源本身以引用计数为零来得知别人不再需要自己,从而把自己kill掉。

=================================================================

CreateThread后那个线程的引用计数不是1,调用CloseHandle只是说自己对这个线程没有兴趣了,线程还是正常运行的

=================================================================

CreateThread后那个线程的引用计数不是1,而是2。 
creating   a   new   process   causes   the   system   to   create   a   process   kernel   object    
and   a   thread   kernel   object.   At   creation   time,   the   system   gives   each   object    
an   initial   usage   count   of   1.   Then,   just   before   CreateProcess   returns,   the    
function   opens   the   process   object   and   the   thread   object   and   places   the    
process-relative   handles   for   each   in   the   hProcess   and   hThread   members   of    
the   PROCESS_INFORMATION   structure.   When   CreateProcess   opens   these   objects    
internally,   the   usage   count   for   each   becomes   2.

=================================================================

创建新的进程后,记数初始化为1,而函数需要返回进程内核对象的句柄,相当于打开一次新创建的类核对象,记数再加1

------------------------------------

CloseHandle函数

BOOL CloseHandle(

HANDLE hObject

);

参数

hObject :代表一个已打开对象handle。

返回值

TRUE:执行成功;

FALSE:执行失败,可以调用GetLastError()获知失败原因。

函数说明:

关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。

若在线程执行完之后,没有调用CloseHandle,在进程执行期间,将会造成内核对象的泄露,相当于句柄泄露,但不同于内存泄露,这势必会对系统的效率带来一定程度上的负面影响。但当进程结束退出后,系统会自动清理这些资源。


VC中关于 0xcccccccc、0xcdcdcdcd和 0xfeeefeee 异常值说明

VC在调试时,可能会报“写入位置0xcccccccc 时发生访问冲突”,或者“写入位置0xcdcdcdcd 时发生访问冲突”,这些问题可能是由于使用了未初始化的指针引起的。

        在 Debug 模式下,VC 会把未初始化的栈内存上的指针全部填成 0xcccccccc ,当字符串看就是 “烫烫烫烫……”,会把未初始化的堆内存上的指针全部填成 0xcdcdcdcd,当字符串看就是 “屯屯屯屯……”。那么调试器为什么要这么做呢?

        VC的DEBUG版会把未初始化的指针自动初始化为0xcccccccc或0xcdcdcdcd,而不是就让取随机值,那是为了方便我们调试程序,如果野指针的初值不确定,那么每次调试同一个程序就可能出现不一样的结果,比如这次程序崩掉,下次却能正常运行,这样显然对我们解bug是非常不利的,所以自动初始化的目的是为了让我们一眼就能确定我们使用了未初始化的野指针了。


        常见默认值说明:

        0xcdcdcdcd - Created but not initialised

        0xdddddddd - Deleted

        0xfeeefeee - Freed memory set by NT's heap manager

        0xcccccccc - Uninitialized locals in VC6 when you compile w/ /GZ

        0xabababab - Memory following a block allocated by LocalAlloc()

↑ 上一篇文章:注册dll与反注册dll,注册表编辑的批处理文件bat的写法 关键词:注册,dll,反注册,注册表,编辑,批处理,bat 发布日期:2018/9/5 10:27:52
↓ 下一篇文章:System.ArgumentException: “0”的值对于“emSize”无效。“emSize”应该大于 0 且小 关键词:net,C#,ArgumentException,emS.. 发布日期:2018/9/12 9:44:58
相关文章:
VC++ MFC DLL动态链接库编写详解 关键词:VC++,MFC,DLL,动态链接库,编写,详解 发布日期:2016-08-19 10:45
MFC OCX 控件事件的添加和处理 关键词:VC,C++,MFC,OCX,控件,事件,添加,处理 发布日期:2018-05-29 15:23
VC在Release出现fatal error C1189:Building MFC application with /MD[d] (CRT dll version) 关键词:VC2015,Release,#error,Building,MFC,application,w.. 发布日期:2018-10-10 14:08
相关目录:.NETVC&C++软件开发
我要评论
正在加载评论信息......