日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

duilib菜单开发遇见“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突”

發布時間:2023/12/13 综合教程 23 生活家
生活随笔 收集整理的這篇文章主要介紹了 duilib菜单开发遇见“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突” 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我的程序是這樣一個邏輯。 首先創建用戶列表,點擊列表項彈出菜單,點擊菜單上“設備選項”,彈出設備列表,上面顯示這個用戶擁有的設備。

菜單的創建參考了這為博主的教程:http://www.cnblogs.com/Alberl/category/520438.html

如圖點擊列表項,彈出菜單中點擊“設備”,運行新的窗口 “設備列表”。

接下來問題出現了,上面操作重復兩遍,會在第二次關閉設備列表的時候 發生異常,程序崩潰。

這就讓我非常頭痛了。

我知道這種錯誤是內存訪問問題,一般都是指針操作不當造成的。

調試程序,中斷發生位置是notify函數(duilib響應函數)結束位置。總之不是發生錯誤的位置。

下面貼出菜單程序源代碼:

MenuWnd2.h:

#pragma once

#include <windows.h>
#include "my_duilib.h"
#include <iostream>

class CUserManageMenuWnd: public CXMLWnd {
public:
    explicit CUserManageMenuWnd(LPCTSTR pszXMLPath,int tag);

protected:
    virtual ~CUserManageMenuWnd();   // 私有化析構函數,這樣此對象只能通過new來生成,而不能直接定義變量。就保證了delete this不會出錯

public:
    void Init(HWND hWndParent, POINT ptPos);
    virtual void    OnFinalMessage(HWND hWnd);
    virtual LRESULT HandleMessage (UINT uMsg, WPARAM wParam, LPARAM lParam);
    virtual LRESULT OnKillFocus   (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
    virtual void Notify( TNotifyUI& msg );
private:
    int tag;
};

MenuWnd2.cpp:

#include "MenuWnd2.h"
#include "my_including.h"
#include "page_info.h"
#include "mysql_utils.h"
#include "user_dev_lst.h"

extern c_page_info page_info;
extern user_sel_ret* user_arr;

CUserManageMenuWnd::CUserManageMenuWnd( LPCTSTR pszXMLPath, int tag) 
: CXMLWnd(pszXMLPath){
    this->tag = tag;
}

CUserManageMenuWnd::~CUserManageMenuWnd(){
}

void CUserManageMenuWnd::Init( HWND hWndParent, POINT ptPos ){
    Create(hWndParent, _T("MenuWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    ::ClientToScreen(hWndParent, &ptPos);
    ::SetWindowPos(*this, NULL, ptPos.x, ptPos.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}

void CUserManageMenuWnd::OnFinalMessage( HWND /*hWnd*/ ) {
    delete this;
}

LRESULT CUserManageMenuWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam ) {
    LRESULT lRes = 0;
    BOOL bHandled = TRUE;

    switch( uMsg )
    {
    case WM_KILLFOCUS:    
        lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); 
        break; 
    default:
        bHandled = FALSE;
    }

    if(bHandled || m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) 
    {
        return lRes;
    }

    return __super::HandleMessage(uMsg, wParam, lParam);
}

void CUserManageMenuWnd::Notify( TNotifyUI& msg ) {
    int num;
    string user_id;
    int dev_num;
    dev_sel_ret* devs;
    if( msg.sType == _T("itemclick") )  {
        string click_menu_option = msg.pSender->GetName().ToString();
        if( !click_menu_option.compare(_T("check_devs")) ) {
             PostMessage(WM_KILLFOCUS);
             num = page_info.get_begin_index() + this->tag;
             user_id = user_arr[num].id;
             devs = MYSQL_INTERFACES::select_devs_of_user("", &dev_num, user_id);
             // 顯示該用戶設備列表
             create_usr_dev_lst_win(dev_num, devs);
        }
    __super::Notify(msg); 
}

LRESULT CUserManageMenuWnd::OnKillFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) {
    Close();
    bHandled = FALSE;
    return __super::OnKillFocus(uMsg, wParam, lParam, bHandled); 
}

創建菜單的代碼,在user列表的notify函數里,POINT用來記錄菜單生成的位置坐標:

void CUsrManageWnd::Notify( TNotifyUI& msg ) {
    if(msg.sType == _T("itemclick"))  {  
        int i_index = msg.pSender->GetTag();
        POINT pt = {msg.ptMouse.x, msg.ptMouse.y};
        CUserManageMenuWnd *p_menu = new CUserManageMenuWnd(_T("Menu/menu2.xml"), i_index);
        p_menu->Init(g_usr_manage_win_hwnd, pt);
        p_menu->ShowWindow(TRUE);
    }  
    __super::Notify(msg);
}

發生中斷的位置就是notify函數結束的位置,真是看的我一頭霧水啊,中斷位置跳到反匯編來看也看不出所以然。

試了一天,最后到了晚上才發現問題所在,那就是delete。

.h文件可知,該程序私有化析構函數,使得只能new來創建,這就需要在合適時機去delete。

程序原本將delete寫在OnFinalMessage函數里。但在實際調試過程中,發現在執行了OnFinalMessage函數的delete后,程序竟然又進入到notify函數里,隨后報錯。

我也不是很明白,為什么點擊一次菜單,會進入兩次notify函數,對于duilib的消息機制也不是那么精通。

最后我的解決方案,就加入一個計數的變量。進入notify創建一次設備列表,則計數變量+1。如果計數變量大于0,則不再創建設備列表。且只有計數變量大于0的時候,才執行delete。

如下,計數變量為new_win_num。

#pragma once

#include <windows.h>
#include "my_duilib.h"
#include <iostream>

class CUserManageMenuWnd: public CXMLWnd {
public:
    explicit CUserManageMenuWnd(LPCTSTR pszXMLPath,int tag);

protected:
    virtual ~CUserManageMenuWnd();   // 私有化析構函數,這樣此對象只能通過new來生成,而不能直接定義變量。就保證了delete this不會出錯

public:
    void Init(HWND hWndParent, POINT ptPos);
    virtual void    OnFinalMessage(HWND hWnd);
    virtual LRESULT HandleMessage (UINT uMsg, WPARAM wParam, LPARAM lParam);
    virtual LRESULT OnKillFocus   (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
    virtual void Notify( TNotifyUI& msg );
private:
    int tag;
    int new_win_num;
};
#include "MenuWnd2.h"
#include "my_including.h"
#include "page_info.h"
#include "mysql_utils.h"
#include "user_dev_lst.h"

extern c_page_info page_info;
extern user_sel_ret* user_arr;

CUserManageMenuWnd::CUserManageMenuWnd( LPCTSTR pszXMLPath, int tag) 
: CXMLWnd(pszXMLPath){
    this->tag = tag;
    this->new_win_num = 0;
}

CUserManageMenuWnd::~CUserManageMenuWnd(){
}

void CUserManageMenuWnd::Init( HWND hWndParent, POINT ptPos ){
    Create(hWndParent, _T("MenuWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    ::ClientToScreen(hWndParent, &ptPos);
    ::SetWindowPos(*this, NULL, ptPos.x, ptPos.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}

void CUserManageMenuWnd::OnFinalMessage( HWND /*hWnd*/ ) {
    if (new_win_num >0)
        delete this;
}

LRESULT CUserManageMenuWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam ) {
    LRESULT lRes = 0;
    BOOL bHandled = TRUE;

    switch( uMsg )
    {
    case WM_KILLFOCUS:    
        lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); 
        break; 


    default:
        bHandled = FALSE;
    }

    if(bHandled || m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) 
    {
        return lRes;
    }

    return __super::HandleMessage(uMsg, wParam, lParam);
}

void CUserManageMenuWnd::Notify( TNotifyUI& msg ) {
    int num;
    string user_id;
    int dev_num;
    dev_sel_ret* devs;
    if( msg.sType == _T("itemclick") )  {
        string click_menu_option = msg.pSender->GetName().ToString();if( !click_menu_option.compare(_T("check_devs")) ) {
            if (new_win_num == 0) {
                PostMessage(WM_KILLFOCUS);
                num = page_info.get_begin_index() + this->tag;
                user_id = user_arr[num].id;
                devs = MYSQL_INTERFACES::select_devs_of_user("", &dev_num, user_id);
                // 顯示該用戶設備列表
                create_usr_dev_lst_win(dev_num, devs);
            }
            new_win_num++; 
        }
    }
    __super::Notify(msg); 
}

LRESULT CUserManageMenuWnd::OnKillFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) {
    Close();
    bHandled = FALSE;
    return __super::OnKillFocus(uMsg, wParam, lParam, bHandled); 
}

轉載一下原作者對于duilib菜單的理解https://www.cnblogs.com/Alberl/p/3352461.html,覺得講的挺好的:

【菜單類小知識】
如果不用指針的方式,而直接用變量的方式顯示菜單 CDuiMenu menu(_T("Menu/menu.xml")),則不能用ShowWindow,否則會崩潰,因為出了作用域后窗口被銷毀了,所以此時可以將CDuiMenu 定義為成員變量、全局變量、或者靜態變量,但是做為一個局部使用的類,這些方法顯然不怎么好;
這時可以用ShowModal代替ShowWindow,于是就能看到窗口啦,但是卻產生了一個問題,那就是菜單窗口不會失去焦點,或者說點擊主窗口的其他區域,菜單不會消失,當然,小伙伴們可以自己捕獲鼠標,來判斷是否點擊了主窗口的其他區域,但顯然這種方法也不太好;
這個時候delete this就派上用場啦(用智能指針也會崩潰,因為出了作用域同樣會銷毀內存,所以只能用delete this啦~ 用delete this就是將作用域交給duilib了),據說COM里面就是用delete this來銷毀內存的。Alberl在duilib的Demo里面見到了大量的delete this,覺得這種自殺的方法很不靠譜,這不,前面教程就提到了ActiveX的一個bug,也是和delete this脫不了干系的~不過既然COM里面都用了delete this,那就說明如果用好這把雙刃劍,還是可以帶來很多好處的。
因為duilib提供了一個機制,就是窗口的最后一個函數一定是OnFinalMessage,之后不再調用窗口類的其他函數,這就為自殺提供了兩個必要條件;delete this而還有一個必要條件就是這個類必須是通過new來申請內存的(而非 "new[]",亦非placement的"new" ,一定要是最原始的 "new",當然malloc也行(需要用free,而不是delete)),所以就將析構函數設置成私有函數,就保證了只有通過new申請內存的方式才能編譯通過。 而duilib的Demo中大量使用delete this卻沒有保證這些必要條件,只要直接用變量的方式來聲明類,則關閉窗口時就會崩潰,作為Demo,如此不嚴謹,有待好好規范。 當然,沒有XX黨,就沒有新中國,沒有那些大神的Demo,也就輪不到Alberl唧唧歪歪啦,這里Alberl只是覺得Demo應該嚴謹和權威,畢竟是官方的,并沒有其他意思,請多多諒解~O(∩_∩)O~

最后要吸取教訓,如果遇到0xC0000005這種異常,一定要檢查對內存的操作。數組啊、指針一類的。

也有可能是,釋放了對象的對內存后繼續對對象進行操作引發的。

總結

以上是生活随笔為你收集整理的duilib菜单开发遇见“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突”的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。