<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    so true

    心懷未來(lái),開(kāi)創(chuàng)未來(lái)!
    隨筆 - 160, 文章 - 0, 評(píng)論 - 40, 引用 - 0
    數(shù)據(jù)加載中……

    MFC程序框架剖析

    即便是基于MFC的應(yīng)用程序,建立窗口類也是會(huì)遵循如下的過(guò)程:
    設(shè)計(jì)窗口類->注冊(cè)窗口類->生成窗口->顯示窗口->更新窗口->消息循環(huán)->消息路由到窗口過(guò)程函數(shù)處理。下面就剖析一下在MFC中是如何完成上述過(guò)程的。

    (1)每個(gè)應(yīng)用程序都有且僅有一個(gè)應(yīng)用類的全局變量theApp,全局變量先于WinMain函數(shù)進(jìn)行處理。
    (2)WinMain函數(shù)體在APPMODUL.CPP文件中,定義如下:
    extern "C" int WINAPI
    _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
     LPTSTR lpCmdLine, int nCmdShow)
    {
     // call shared/exported WinMain
     return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
    }
    其中#define _tWinMain   WinMain
    (3)AfxWinMain函數(shù)體在WINMAIN.CPP文件中,里面有如下兩句話:
    CWinThread* pThread = AfxGetThread();
    CWinApp* pApp = AfxGetApp();
    其實(shí)這里得到的這兩個(gè)指針都是指向全局的對(duì)象theApp的;
    接下來(lái)有函數(shù)調(diào)用pThread->InitInstance(),根據(jù)多態(tài)性,會(huì)調(diào)用CXXXApp類中的InitInstance()函數(shù)。該函數(shù)很重要,在對(duì)該函數(shù)的調(diào)用中就會(huì)完成:設(shè)計(jì)窗口類->注冊(cè)窗口類->生成窗口->顯示窗口->更新窗口。
    接下來(lái),該函數(shù)中會(huì)繼續(xù)調(diào)用pThread->Run(),這就完成了:消息循環(huán)->消息路由到窗口過(guò)程函數(shù)處理。
    (4)進(jìn)入CXXXApp::InitInstance()函數(shù)體中,對(duì)于單文檔應(yīng)用程序,調(diào)用ProcessShellCommand(cmdInfo),通過(guò)調(diào)用該函數(shù)就會(huì)完成:設(shè)計(jì)窗口類->注冊(cè)窗口類->生成窗口。
    再接下來(lái)就會(huì)調(diào)用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();這就完成了:顯示窗口->更新窗口。
    (5)在函數(shù)CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中會(huì)進(jìn)入到如下的case分支:case CCommandLineInfo::FileNew:
      if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
    (6)進(jìn)入函數(shù)CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
     AFX_CMDHANDLERINFO* pHandlerInfo),調(diào)用_AfxDispatchCmdMsg(this, nID, nCode,
        lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
    (7)進(jìn)入函數(shù)AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
     AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),調(diào)用
    case AfxSig_vv:
      // normal command or control notification
      ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED
      ASSERT(pExtra == NULL);
      (pTarget->*mmf.pfn_COMMAND)();
    (8)進(jìn)入CWinApp::OnFileNew(),調(diào)用m_pDocManager->OnFileNew();這個(gè)函數(shù)很特殊,它本身是個(gè)消息響應(yīng)函數(shù),當(dāng)我們點(diǎn)擊ID為ID_FILE_NEW的菜單時(shí),會(huì)產(chǎn)生一個(gè)命令消息,由于命令消息可以被CCmdTarget類及其派生類來(lái)捕獲,而CWinApp是從CCmdTarget派生出來(lái)的,因此可以捕獲這個(gè)消息。當(dāng)應(yīng)用程序創(chuàng)建完成并成功顯示后,當(dāng)我們點(diǎn)擊文件菜單下的新建菜單項(xiàng)時(shí),就會(huì)首先進(jìn)入這個(gè)函數(shù),然后再依次執(zhí)行下去,最后就會(huì)執(zhí)行到pDocument->OnNewDocument()中,往往我們會(huì)對(duì)這個(gè)函數(shù)不解,不知道它為什么會(huì)響應(yīng)ID_FILE_NEW的命令消息,至此真相大白了。順便說(shuō)一句,為什么程序在剛啟動(dòng)的時(shí)候,我們并沒(méi)有點(diǎn)擊菜單項(xiàng),為什么會(huì)自動(dòng)的產(chǎn)生這個(gè)消息呢?這是因?yàn)樵贑XXXXApp::InitInstance()函數(shù)中有“CCommandLineInfo cmdInfo;”這個(gè)類的構(gòu)造函數(shù)是這樣的:CCommandLineInfo::CCommandLineInfo()
    {
     m_bShowSplash = TRUE;
     m_bRunEmbedded = FALSE;
     m_bRunAutomated = FALSE;
     m_nShellCommand = FileNew;
    },因此就會(huì)在第(5)步驟的時(shí)候進(jìn)入到“case CCommandLineInfo::FileNew:”這個(gè)分支中,就相當(dāng)于產(chǎn)生了這樣一個(gè)FileNew的消息。同理對(duì)于ID為ID_FILE_OPEN(在CWinApp::OnFileOpen()中響應(yīng))、ID_FILE_SAVE(在CDocument::OnFileSave()中響應(yīng))等等在MFC向?qū)槲覀兩傻膯挝臋n類中找不到消息響應(yīng)的入口時(shí),其實(shí)都是在基類CWinApp或者CDocument類中進(jìn)行了響應(yīng)。對(duì)于CXXXXDoc::Serialize(CArchive& ar)函數(shù)也是通過(guò)ID_FILE_SAVE和ID_FILE_OPEN產(chǎn)生命令消息后就行響應(yīng)從而才調(diào)用該函數(shù)的。
    (9)進(jìn)入CDocManager::OnFileNew(),CDocManager類有一個(gè)成員變量是CPtrList m_templateList;該變量保存了一個(gè)文檔模版鏈表指針,在CDocManager::OnFileNew()函數(shù)體中會(huì)調(diào)用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到鏈表中的頭,也就是第一個(gè)文檔模版,后面就會(huì)用得到的這個(gè)指針去調(diào)用pTemplate->OpenDocumentFile(NULL);緊接著就會(huì)有一個(gè)判斷,用來(lái)確定該鏈表中是否只有一項(xiàng),如果鏈表中保存了多個(gè)文檔模版,則會(huì)彈出一個(gè)對(duì)話框,來(lái)讓我們選擇到底是使用哪一套文檔模版來(lái)構(gòu)建應(yīng)用程序,相信大家也都見(jiàn)到過(guò)這種情況吧。對(duì)了,還有一點(diǎn)要說(shuō)明的是:pTemplate是一個(gè)CDocTemplate的指針,但接下來(lái)程序?yàn)槭裁磿?huì)進(jìn)入到CSingleDocTemplate::OpenDocumentFile的函數(shù)體內(nèi)呢,這是因?yàn)镃DocTemplate類中的OpenDocumentFile函數(shù)被定義為純虛函數(shù),而CSingleDocTemplate類又是從CDocTemplate類派生出來(lái)的,并且實(shí)現(xiàn)了該函數(shù),因此就會(huì)進(jìn)入到子類的函數(shù)體中了。
    (10)進(jìn)入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
     BOOL bMakeVisible),先調(diào)用CreateNewDocument()創(chuàng)建文檔類,再調(diào)用pFrame = CreateNewFrame(pDocument, NULL);創(chuàng)建框架類和視圖類,從這里也可以看出MFC體系結(jié)構(gòu)中文檔、框架、視圖“三位一體”的模式,在這一個(gè)函數(shù)中同時(shí)創(chuàng)建三個(gè)類;再會(huì)調(diào)用pDocument->OnNewDocument();因此就會(huì)進(jìn)入到子類的文檔類中的pDocument->OnNewDocument()中了。
    (11)進(jìn)入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),調(diào)用if (!pFrame->LoadFrame(m_nIDResource,
       WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles
       NULL, &context))
    (12)進(jìn)入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
     CWnd* pParentWnd, CCreateContext* pContext),調(diào)用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
    (13)進(jìn)入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),該函數(shù)內(nèi)部就完成了:設(shè)計(jì)窗口類->注冊(cè)窗口類。MFC通過(guò)給我們提供好一些已經(jīng)訂制好的窗口類,我們不需要自己再設(shè)計(jì)窗口類,只需要到那些訂制好的窗口類“倉(cāng)庫(kù)”中尋找一種適合我們需要的窗口類就可以了,然后通過(guò)AfxRegisterClass函數(shù)注冊(cè)窗口類。還需要說(shuō)明的是,再后續(xù)的跟蹤過(guò)程中,我們會(huì)發(fā)現(xiàn)還會(huì)進(jìn)入到AfxEndDeferRegisterClass函數(shù)中進(jìn)行設(shè)計(jì)和注冊(cè)窗口類,這主要是因?yàn)閱挝臋n應(yīng)用程序比較特殊,它提前通過(guò)這樣的一種途徑進(jìn)行了窗口類的設(shè)計(jì)和注冊(cè)步驟,其實(shí)是應(yīng)該在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函數(shù)的調(diào)用中完成窗口類的設(shè)計(jì)和注冊(cè)的,這一點(diǎn)我們要清楚,也就是說(shuō)設(shè)計(jì)和注冊(cè)窗口類的正宗發(fā)源地應(yīng)該是PreCreateWindow(CREATESTRUCT& cs)。此外,我們還會(huì)注意到在該函數(shù)體的前部分有一語(yǔ)句為“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口類的窗口過(guò)程函數(shù)都是DefWindowProc,這一點(diǎn)在后面的跟蹤中可以看到,每次生成窗口之后都會(huì)調(diào)用幾次DefWindowProc函數(shù)。也就是說(shuō)MFC都是讓我們采用默認(rèn)的窗口過(guò)程函數(shù),這并不是說(shuō)我們因此就不能使用自己的窗口過(guò)程函數(shù)實(shí)現(xiàn)個(gè)性化的消息處理了,MFC采用了一種基于消息映射的機(jī)制完成了消息個(gè)性化處理。
    (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
     CWnd* pParentWnd, CCreateContext* pContext)中,調(diào)用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
    (15)進(jìn)入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),調(diào)用PreCreateWindow(cs);
    (16)進(jìn)入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),調(diào)用CFrameWnd::PreCreateWindow(cs)
    (17)進(jìn)入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),調(diào)用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次設(shè)計(jì)和注冊(cè)窗口類
    (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
     CWnd* pParentWnd, CCreateContext* pContext)中,調(diào)用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
    (19)進(jìn)入BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
     LPCTSTR lpszWindowName,
     DWORD dwStyle,
     const RECT& rect,
     CWnd* pParentWnd,
     LPCTSTR lpszMenuName,
     DWORD dwExStyle,
     CCreateContext* pContext),調(diào)用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
      pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
    (20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
     LPCTSTR lpszWindowName, DWORD dwStyle,
     int x, int y, int nWidth, int nHeight,
     HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),調(diào)用if (!PreCreateWindow(cs))
    ,接下來(lái)調(diào)用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
       cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
       cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,終于讓我們找到生成窗口的地方了——函數(shù)::CreateWindowEx!
    (21)進(jìn)入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),調(diào)用if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
    (22)進(jìn)入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),調(diào)用return OnCreateHelper(lpcs, pContext);
    (23)進(jìn)入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),調(diào)用if (CWnd::OnCreate(lpcs) == -1)
    (24)進(jìn)入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),調(diào)用return (int)Default();
    (25)進(jìn)入LRESULT CWnd::Default(),調(diào)用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);
    (26)進(jìn)入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),調(diào)用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
    (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),調(diào)用if (!OnCreateClient(lpcs, pContext))
    (28)進(jìn)入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),調(diào)用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
    (29)進(jìn)入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),調(diào)用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext))
    (30)進(jìn)入BOOL CWnd::Create(LPCTSTR lpszClassName,
     LPCTSTR lpszWindowName, DWORD dwStyle,
     const RECT& rect,
     CWnd* pParentWnd, UINT nID,
     CCreateContext* pContext),調(diào)用return CreateEx(0, lpszClassName, lpszWindowName,
      dwStyle | WS_CHILD,
      rect.left, rect.top,
      rect.right - rect.left, rect.bottom - rect.top,
      pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);
    (31)進(jìn)入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
     LPCTSTR lpszWindowName, DWORD dwStyle,
     int x, int y, int nWidth, int nHeight,
     HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重復(fù)生成框架類CMainFrame的過(guò)程來(lái)生成CXXXView,因?yàn)樗彩且粋€(gè)窗口類,因此也需要進(jìn)行那一系列過(guò)程才能最終顯示更新出來(lái)。
    調(diào)用的順序是這個(gè)樣子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->...
    寫(xiě)到這里,基本上就清楚了,中間的省略號(hào)表示的部分大多數(shù)都是在與窗口過(guò)程函數(shù)有關(guān)的,因?yàn)樵谏纱翱诘臅r(shí)候需要響應(yīng)一些消息,因此需要調(diào)用一些窗口過(guò)程函數(shù),每次在調(diào)用::CreateWindowEx(...)函數(shù)后都會(huì)調(diào)用一些窗口過(guò)程函數(shù),然后再去調(diào)用該窗口類對(duì)應(yīng)的OnCreate函數(shù),其實(shí)在調(diào)用OnCreate函數(shù)之前調(diào)用CreateWindowEx只是生成了一個(gè)窗口,至于這個(gè)窗口里面要放置些什么東西,以及該如何裝飾該窗口,則就需要由OnCreate來(lái)完成了,往往我們都會(huì)在OnCreate函數(shù)的后面(這樣做是為了不影響窗口本身應(yīng)該布置的格局)添加一些代碼,創(chuàng)建我們自己的東西,比如我們通常會(huì)在CMainFrame類的OnCreate函數(shù)后面放置一些Create代碼,來(lái)創(chuàng)建我們自己的可停靠的工具欄或者按鈕之類的東西,當(dāng)然我們也可以在CXXXView類的OnCreate函數(shù)的后面添加一些代碼,來(lái)創(chuàng)建我們需要的東西,比如按鈕之類的東西。在完成了從設(shè)計(jì)、注冊(cè)到生成窗口的過(guò)程之后,往往還需要顯示更新,有些時(shí)候,我們不必要每次都顯示的調(diào)用CWnd的ShowWindow和UpdateWindow兩個(gè)函數(shù),我們可以在創(chuàng)建的時(shí)候,給窗口風(fēng)格中添加WS_VISIBLE即可,因此有些時(shí)候會(huì)跟蹤不到ShowWindow和UpdateWindow兩個(gè)函數(shù)這兩個(gè)函數(shù),因?yàn)榇翱谠趧?chuàng)建的時(shí)候就可見(jiàn)了。
        總的來(lái)說(shuō),先初始化應(yīng)用類,然后注冊(cè)生成框架類,然后再注冊(cè)生成視圖類,然后注冊(cè)生成視圖類OnCreate函數(shù)后面用戶添加的、用Create來(lái)準(zhǔn)備創(chuàng)建的窗口,然后再注冊(cè)生成框架類的OnCreate函數(shù)后面需要生成的m_wndToolBar、m_wndStatusBar以及我們自己添加的要?jiǎng)?chuàng)建的窗口類,最后在回到應(yīng)用類的初始化的函數(shù)體中,調(diào)用框架類的顯示和更新函數(shù),然后再進(jìn)入由框架類定義的窗口的消息循環(huán)中。

    消息循環(huán)的過(guò)程是這個(gè)樣子的:
    (1)調(diào)用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
     LPTSTR lpCmdLine, int nCmdShow)函數(shù)中的pThread->Run()
    (2)進(jìn)入int CWinApp::Run(),調(diào)用return CWinThread::Run();
    (3)進(jìn)入int CWinThread::Run(),調(diào)用if (!PumpMessage())
    (4)進(jìn)入BOOL CWinThread::PumpMessage(),調(diào)用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
    (5)回到BOOL CWinThread::PumpMessage(),調(diào)用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur);
    (6)回到int CWinThread::Run(),調(diào)用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
    (7)再重復(fù)(4)-(6)的步驟
    下面給出int CWinThread::Run()中消息循環(huán)的部分代碼:
    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));
    這段代碼其實(shí)本質(zhì)上與我們基于Win32 SDK手寫(xiě)的代碼:
    //消息循環(huán)
     MSG msg;
     while(GetMessage(&msg,NULL,0,0))
     {
      //簡(jiǎn)單的說(shuō),函數(shù)TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻譯成WM_CHAR消息,沒(méi)有該函數(shù)就不能產(chǎn)生WM_CHAR消息。
      TranslateMessage(&msg);
      ::DispatchMessage(&msg);
     }
    是一致的。

    posted on 2008-02-23 19:15 so true 閱讀(2503) 評(píng)論(3)  編輯  收藏 所屬分類: C&C++

    評(píng)論

    # re: MFC程序框架剖析  回復(fù)  更多評(píng)論   

    非常感謝樓主
    2008-03-11 09:12 |

    # re: MFC程序框架剖析  回復(fù)  更多評(píng)論   

    非常感謝,能提供這么好的講解,再次說(shuō)聲謝謝
    2008-03-27 16:59 | BOYS

    # re: MFC程序框架剖析  回復(fù)  更多評(píng)論   

    多謝樓主,獲益良多!
    2009-12-05 20:30 | mfc
    主站蜘蛛池模板: 美丽的姑娘免费观看在线播放| 亚洲免费黄色网址| av免费不卡国产观看| 国产免费黄色大片| 亚洲av永久无码制服河南实里| 亚洲一本之道高清乱码| 一级毛片免费一级直接观看| 一级女人18毛片免费| 国产亚洲日韩一区二区三区| 亚洲av无码片区一区二区三区| 国产免费黄色无码视频| 曰批全过程免费视频在线观看| 中文字幕人成人乱码亚洲电影| 激情综合亚洲色婷婷五月APP| 精品国产免费人成网站| 毛片a级毛片免费播放下载 | 亚洲熟妇无码八AV在线播放| 亚洲精品午夜国产va久久| 免费看无码特级毛片| 国产免费观看黄AV片| 亚洲性69影院在线观看| 国产免费福利体检区久久| 成人啪精品视频免费网站| 亚洲av无码精品网站| 污视频网站免费观看| 亚洲中文无码永久免费| 国产A在亚洲线播放| 黄色网页免费观看| 91免费精品国自产拍在线不卡| 亚洲AV人无码综合在线观看| 边摸边脱吃奶边高潮视频免费| 在线视频观看免费视频18| 久久精品国产96精品亚洲 | 免费无码一区二区三区蜜桃 | 成人久久久观看免费毛片| 成年男女免费视频网站| 久久亚洲AV成人无码| 中文字幕手机在线免费看电影 | 91亚洲自偷在线观看国产馆| 中国国产高清免费av片| 一区二区三区亚洲视频|