最近遇到了一个非常奇怪的问题,反复思量就是想不通,请大家集思广益,给点思路
  我做的视频控件(mfc的),在测试中需要进行创建窗口(cwnd->create)  和 销毁敞口(cwnd->destroywindow)操作。
  由于视频格式要求,我要创建的窗口背景为黑色,我重载了CWnd::PreCreateWindow 在里边调用了AfxRegisterWndClass
   AfxRegisterWndClass( 
CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, 
        ::LoadCursor( NULL, IDC_ARROW ),
        (HBRUSH) ::GetStockObject(BLACK_BRUSH),
NULL
       );
  利用第三个参数,在注册窗口的时候将背景弄黑,这样不会有问题。
  但是如果我第三个参数用了 m_hBrush = CreateSolidBrush( VIDEOWND_COLOUR )这样创建的画刷对象,反复创建,销毁窗口100次左右,create创建窗口函数便返回0,即创建窗口失败。我每次都销毁的时候也会 DeleteObject(m_hBrush);因此资源泄漏可以排除,资源管理器的内存基本没涨。
  那问题是用GetStockObject获得的GDI对象,放在AfxRegisterWndClass的第三个参数处做参数就不会出上述问题(如代码中所示),CreateSolidBrush创建的就会,而且我试着每次创建窗口之后调用CreateSolidBrush函数,但不使用他返回的参数在AfxRegisterWndClass的第三个参数处,便也不会出错,这说明这样创建画刷没有错,但将返回的画刷句柄做参数就有问题了。
  GetStockObject在msdn上说不用进行任何释放,为什么?StockObject是什么样的对象?什么样的叫stock“对象”。这样返回的画刷对象和我每次创建画刷再销毁画刷的做法有什么本质上的区别吗?为什么前者可以通过反复创建窗口测试,后者却不能?
   
   

解决方案 »

  1.   

    估计是注册类之后的HBRUSH无法删除,你可以调试看一下DeleteObject是不是返回失败。
    AfxRegisterWndClass只要调用一次即可,不需要每次创建窗口都调用。
    另外,可以用UnregisterClass取消已注册的类,然后再DeleteObject。
      

  2.   

    Stock对象是系统内置的对象,无论用不用它都存在,所以不需要释放。至于你说的问题是很奇怪,不过更奇怪的是为何要执行那么多次的AfxRegisterWndClass,一种窗口类型只需要注册一次即可,以后可以用类名创建多个窗口实例。另外,注册窗口类时创建的画刷是不需要手工删除的,当窗口类取消注册时会自动释放画刷对象。
      

  3.   

    把AfxRegisterWndClass的内容放到构造函数里面比较好 不要放在PreCreateWindow里面
    以后直接用类名就行了。
      

  4.   

    DeleteObject有执行,且显示成功删除,再说用于函数参数的对象不都是在栈中吗?应该也会自动释放才对。
      

  5.   

    至于AfxRegisterWndClass,我没次调用create一个窗口,然后detroywindow,下次再create的时候不用调用AfxRegisterWndClass他也会为黑背景?
      

  6.   

    栈中分配的参数和变量在函数返回后会自动释放,但CreateSolidBrush是创建一个GDI对象,分配在栈中的只是一个句柄值,句柄变量被释放并不会关闭句柄,所以GDI资源没有销毁。
    AfxRegisterWndClass是注册一个WNDCLASS,注册之后这个WNDCLASS就是一直存在的,直到取消注册为止,所以只需要注册一次,CreateWindow可以无限次使用该WNDCLASS。
      

  7.   

    但是我要如何理解,那么句柄变量被释放并不会关闭句柄,我要怎么理解呢,能给我说说吗?那要怎么关闭句柄  也就是说要怎么改造一下CreateSolidBrush这种就可以用,用closehandle关闭吗?
      

  8.   

    强行销毁:DeleteObject(m_hBrush);
      

  9.   

    GDI 中的 Stock 对象,你可以理解为是随系统的生命周期而存在的,不需要你自己管理,但是可以拿来就用,所以叫“库存”对象。到底哪些是库存对象,看 MSDN 中的介绍就够了,画刷、画笔、字体等各类对象都有那么几个。对库存对象可以调用 DeleteObject,只不过不会有任何效果,但也没有副作用,因为系统本身可以识别出来。具体的问题所在我还没有推测到,但是你的做法是有一定问题的。因为窗口类的背景画刷应该与窗口类自己具有一样的生命周期而不是与某个窗口一样。
      

  10.   

    GDI 对象只创建,不删除,会造成资源泄露。本来程序退出时,系统会检查所有你创建但未删除的 GDI 对象并删除之的。估计你的程序有多个窗口,主窗口线程在这么多次创建中未关闭。
    Stock Object 是由系统创建和维护的,因此不会出现资源泄露的问题。注意因为大家谁都可以使用,不应该用 DeleteObject 等函数删除。
      

  11.   

    举个不恰当的例子:CreateSolidBrush相当于到银行开户,银行给你一个存折相当于句柄,用存折可以对帐户进行管理,如果你不需要这个帐户的时候,要把存折交给银行消户相当于DeleteObject,如果你没有消户,而把存折扔了,就相当于栈中句柄变量的释放,而实际上帐户还是存在的,还需要占用银行的资源来保存,这就相当于资源泄露。
      

  12.   

    我有调用DeleteObject啊,我每次销毁窗口时候会调用这个啊
      

  13.   

    程序如下:
    CImport::CImport(){  
     m_hBrush = CreateSolidBrush( VIDEOWND_COLOUR );}CImport::~CImport()
    {
    // this->DestroyWindow();
     DeleteObject(m_hBrush);
    }
    /*
    //视频接收及显示线程开始static CWnd* WndRecvVideoScreen;
    CImport* pVideoImport;
    bool bVideoRecv = false;int CImport::MutiVideoRecvStart(CString ip,HWND hWnd,int port)
    {
    if(true == bVideoRecv)
    return -1;
    pVideoImport = new CImport();
    //    WndRecvVideoScreen->Attach(hWnd);
        WndRecvVideoScreen = (CWnd*) CWnd::FromHandle(hWnd);

    CRect rectWnd;
    if(NULL == pVideoImport)
    return -1; if ( !pVideoImport->Create(
    NULL,
    NULL,
    WS_CHILD|WS_EX_NOPARENTNOTIFY ,
    CRect( 0, 0, 0, 0 ),
    WndRecvVideoScreen,//this,
    0,
    NULL
    ) )
    {  // ShowErrMsg() ;
    AfxMessageBox("error");
    return 0;
    }   // pVideoImport->SelectThisPane(TRUE);

    WndRecvVideoScreen->GetWindowRect(&rectWnd);
    pVideoImport->SetWindowPos NULL,
                    0,
                    0, rectWnd.Width()-2,
    rectWnd.Height()-2,
    SWP_SHOWWINDOW
    );
    // pVideoImport->ShowWindow(SW_SHOW);

    bVideoRecv = true;
    return 1;
    }
    //视频接收及显示线程结束
    int CImport::MutiVideoRecvStop()
    {
    if(false == bVideoRecv)
    return -1;
    if(NULL != pVideoImport)
    {
    // pVideoImport->VideoRecvStop();
    }
    //::PostMessage(pVideoImport->GetSafeHwnd(),WM_QUIT,0,0);
      
    //pVideoImport->
    :: DestroyWindow( pVideoImport->GetSafeHwnd() );
    //    pVideoImport->Detach();
    // pVideoImport->DestroyWindow();
    // WndRecvVideoScreen->DestroyWindow();
    // SAFE_DELETE(WndRecvVideoScreen);
    pVideoImport->ReleaseDC(pVideoImport->GetDC());
    if(NULL!=pVideoImport){
    delete  pVideoImport;
        pVideoImport=NULL; }
         WndRecvVideoScreen->DeleteTempMap();
    bVideoRecv = false;
    return 1;
    }BOOL CImport::PreCreateWindow(CREATESTRUCT &cs)
    {
    if ( !CWnd::PreCreateWindow( cs ) )
        {
    return FALSE;
    }
        cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
        cs.style &= ~WS_BORDER;

        cs.lpszClass = AfxRegisterWndClass( 
                      CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, ::LoadCursor( NULL, IDC_ARROW ),
     (HBRUSH) ::GetStockObject(BLACK_BRUSH),
    NULL
    );
        return TRUE;
    }