欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

【C#.net】使用 miniblink 在 Winform 程序中嵌入瀏覽器

admin
2025年1月8日 15:7 本文熱度 125

最近公司產(chǎn)品中自定義瀏覽器比較老,打開一些支持h5 的站莫名報(bào)錯(cuò),而且經(jīng)常彈框。已經(jīng)到了令人無法忍受的地步了,于是我想到了將內(nèi)核由之前的IE 升級(jí)到Chromium。之前想到的是使用cef來做,而且網(wǎng)上的資源和教程也很多,后來在自己嘗試的過程中發(fā)現(xiàn)使用cef時(shí)程序會(huì)莫名其妙的崩潰,特別是在關(guān)閉對(duì)話框的時(shí)候。我在網(wǎng)上找了一堆資料,嘗試了各種版本未果,這個(gè)方案也就放棄了。后來又搜到了wke和miniblink,對(duì)比二者官方的文檔和demo,我決定使用miniblink,畢竟我直接搜索wke browser 出來的都是miniblink,只有搜索wke github 才會(huì)有真正的wke,而且wke似乎沒有api文檔,最后miniblink是國(guó)人寫的,文檔都是中文而且又有專門的qq交流群,有問題可以咨詢一下。

什么是miniblink

miniblink 是由國(guó)內(nèi)大神 龍泉寺掃地僧 針對(duì)chromium內(nèi)核進(jìn)行裁剪去掉了所有多余的部件,只保留最基本的排版引擎blink,而產(chǎn)生的一款號(hào)稱全球小巧的瀏覽器內(nèi)核項(xiàng)目,目前miniblink 保持了10M左右的極簡(jiǎn)大小,相比CEF 動(dòng)輒幾百M(fèi)的大小確實(shí)小巧許多。而且能很好的支持H5等一些列新標(biāo)準(zhǔn),同時(shí)它內(nèi)嵌了Nodejs 支持electron。而且也支持各種語(yǔ)言調(diào)用。

官方的地址如下為 https://weolar.github.io/miniblink/index.html

使用miniblink

說了這么多那么該怎么用呢?從官方的介紹來看,我們可以使用VS的向?qū)С绦蛏梢粋€(gè)普通的win32 窗口程序,然后生成的這些代碼中將函數(shù)InitInstance 中的代碼全部刪除加上這么5句話

wkeSetWkeDllPath(L"E:\\mycode\\miniblink49\\trunk\\out\\Release_vc6\\node.dll");

wkeInitialize();

wkeWebView window = wkeCreateWebWindow(WKE_WINDOW_TYPE_POPUP, NULL, 0, 0, 1080, 680);

wkeLoadURL(window, "qq.com");

wkeShowWindow(window, TRUE);

當(dāng)然,使用這些函數(shù)需要下載它的SDK開發(fā)包,然后在對(duì)應(yīng)位置包含wke.h。
這些代碼會(huì)生成一個(gè)窗口程序,具體的請(qǐng)敢興趣的朋友自己去實(shí)踐看看效果。或者編譯運(yùn)行一下它的demo程序。

在對(duì)話框中使用

現(xiàn)在我想在對(duì)話框中使用,那么該怎么辦呢。

首先也是先用MFC的向?qū)梢粋€(gè)對(duì)話框并編輯資源文件。最后我的對(duì)話框大概長(zhǎng)成這樣

我會(huì)將按鈕下面部分全部作為瀏覽器頁(yè)面。

我們?cè)诔绦駻PP類的InitInstance函數(shù) 中初始化miniblink庫(kù),并在對(duì)話框被關(guān)閉后直接卸載miniblink的相關(guān)資源

wkeSetWkeDllPath(L"node.dll");


wkeInitialize();

CWebBrowserDlg dlg = CWebBrowserDlg();

m_pMainWnd = dlg;

INT_PTR nResponse = dlg.DoModal();


if (nResponse == IDOK)

{

  // TODO: 在此放置處理何時(shí)用

  //  “確定”來關(guān)閉對(duì)話框的代碼

}

else if (nResponse == IDCANCEL)

{

  // TODO: 在此放置處理何時(shí)用

  //  “取消”來關(guān)閉對(duì)話框的代碼

}


// 由于對(duì)話框已關(guān)閉,所以將返回 FALSE 以便退出應(yīng)用程序,

//  而不是啟動(dòng)應(yīng)用程序的消息泵。

wkeFinalize();

然后在主對(duì)話框類中新增一個(gè)成員變量用來保存miniblink的web視圖的句柄

wkeWebView m_web;

我們?cè)趯?duì)話框的OnInitDialog函數(shù)中創(chuàng)建這么一個(gè)視圖,用來加載百度的首頁(yè)面

GetClientRect(&rtClient);

rtClient.top += 24;

m_web = wkeCreateWebWindow(WKE_WINDOW_TYPE_CONTROL, *this, rtClient.left, rtClient.top, rtClient.right - rtClient.left, rtClient.bottom - rtClient.top);

wkeLoadURL(m_web, "https://www.baidu.com");

wkeShowWindow(m_web, TRUE);

至此我們已經(jīng)能夠生成一個(gè)簡(jiǎn)單的瀏覽器程序

似乎到這已經(jīng)差不多該結(jié)束了,但是現(xiàn)在我遇到了在整個(gè)程序完成期間最大的問題,那就是web頁(yè)面無法響應(yīng)鍵盤消息,我嘗試過改成窗口程序,發(fā)現(xiàn)改了之后能正常運(yùn)行,但是我要的是對(duì)話框啊。這么改只能證明這個(gè)庫(kù)是沒問題的。

后來我在群里面發(fā)出了這樣的疑問,有朋友告訴我說應(yīng)該是wkeWebView沒有接受到鍵盤消息,于是我打算處理主對(duì)話框的WM_KEYDOWN 和WM_KEYUP 以及WM_CHAR消息,根據(jù)官方的文檔,應(yīng)該是只需要攔截對(duì)話框的這三個(gè)消息,然后使用函數(shù)wkeFireKeyUpEvent、wkeFireKeyDownEvent、wkeFireKeyPressEvent函數(shù)分別向wkeWebView發(fā)送鍵盤消息就可以了.于是我在對(duì)應(yīng)的處理函數(shù)中添加了相關(guān)代碼

//OnChar

unsigned int flags = 0;

if (nFlags & KF_REPEAT)

  flags |= WKE_REPEAT;

if (nFlags & KF_EXTENDED)

  flags |= WKE_EXTENDED;

wkeFireKeyPressEvent(m_web, nChar, flags, false);


//OnKeyUp

unsigned int flags = 0;

if (nFlags & KF_REPEAT)

  flags |= WKE_REPEAT;

if (nFlags & KF_EXTENDED)

  flags |= WKE_EXTENDED;

wkeFireKeyUpEvent(m_web, virtualKeyCode, flags, false);


//OnKeyDown

unsigned int flags = 0;

if (nFlags & KF_REPEAT)

  flags |= WKE_REPEAT;

if (nFlags & KF_EXTENDED)

  flags |= WKE_EXTENDED;

wkeFireKeyDownEvent(m_web, virtualKeyCode, flags, false);

但是這么干,我通過調(diào)試發(fā)現(xiàn)它好像并沒有進(jìn)入到這些函數(shù)里面來,也就是說鍵盤消息不是由主對(duì)話框來處理的。那么現(xiàn)在只能在wkeWebView 對(duì)應(yīng)的窗口中來處理了。那么怎么捕獲這個(gè)窗口的消息呢,miniblink提供了函數(shù)wkeGetHostHWND 來根據(jù)視圖的句柄獲取對(duì)應(yīng)窗口的句柄,那么現(xiàn)在的思路就是這樣的:首先獲取對(duì)應(yīng)的窗口句柄然后通過SetWindowLong來修改窗口的窗口過程,然后在窗口過程中處理這些消息就行了。根據(jù)這個(gè)思路整理一下代碼

//在創(chuàng)建wkeWebView 之后來hook窗口過程

HWND hWnd = wkeGetHostHWND(m_web);

g_OldProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

//為了能夠在全局函數(shù)中使用對(duì)話框類的東西,我們?yōu)榇翱诮壎ㄒ粋€(gè)對(duì)話框類的指針

SetWindowLong(hWnd, GWL_USERDATA, this);

接著在MyWndProc中處理對(duì)應(yīng)的消息事件

LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    CWebBrowserDlg* pDlg = (CWebBrowserDlg*)GetWindowLong(hWnd, GWL_USERDATA);

    if (NULL == pDlg)

    {

        return CallWindowProc(g_OldProc, hWnd, uMsg, wParam, lParam);

    }


    switch (uMsg)

    {

        case WM_KEYUP:

        {

          unsigned int virtualKeyCode = wParam;

          unsigned int flags = 0;

          if (HIWORD(lParam) & KF_REPEAT)

          flags |= WKE_REPEAT;

          if (HIWORD(lParam) & KF_EXTENDED)

          flags |= WKE_EXTENDED;


          wkeFireKeyDownEvent(pDlg->m_web, virtualKeyCode, flags, false);

        }

        break;

        case WM_KEYDOWN:

        {

          unsigned int virtualKeyCode = wParam;

          unsigned int flags = 0;

          if (HIWORD(lParam) & KF_REPEAT)

          flags |= WKE_REPEAT;

          if (HIWORD(lParam) & KF_EXTENDED)

          flags |= WKE_EXTENDED;


          wkeFireKeyUpEvent(pDlg->m_web, virtualKeyCode, flags, false);

        }

        break;

        case WM_CHAR:

        {

          unsigned int charCode = wParam;

          unsigned int flags = 0;

          if (HIWORD(lParam) & KF_REPEAT)

          flags |= WKE_REPEAT;

          if (HIWORD(lParam) & KF_EXTENDED)

          flags |= WKE_EXTENDED;


          wkeFireKeyPressEvent(pDlg->m_web, charCode, flags, false);

        }

        break;


        default:

          return CallWindowProc(g_OldProc, hWnd, uMsg, wParam, lParam);

    }


    return 0;

}

這樣做之后我發(fā)現(xiàn)它雖然能夠截取到這些消息并執(zhí)行它,但是在調(diào)用wkeFireKeyPressEvent等函數(shù)之后仍然無法響應(yīng)鍵盤消息。難道是wkeCreateWebWindow 創(chuàng)建出來的窗口不能做子窗口?帶著這個(gè)疑問我根據(jù)官方文檔嘗試了一下使用wkeCreateWebView ,然后將它綁定到對(duì)應(yīng)的窗口上,然后這個(gè)整體作為子窗口的方式。

代碼太長(zhǎng)了,我就不放出來了,有興趣的可以翻到本文尾部,我將這個(gè)demo項(xiàng)目放到的GitHub上。

結(jié)果還是不行。這些函數(shù)仍然進(jìn)不來。

真的郁悶,難道要換方案?我這個(gè)時(shí)候已經(jīng)開始準(zhǔn)備換方案了,在編譯wke 的時(shí)候心情極度煩躁,我在之前的程序上不停的敲擊鍵盤,就聽見“等等~”。我靠!這不是想從模態(tài)對(duì)話框上切換回主頁(yè)面時(shí)的那個(gè)聲音嗎?會(huì)不會(huì)是因?yàn)槟B(tài)對(duì)話框的關(guān)系?

這個(gè)時(shí)候我瞬間來了靈感。那就換吧,主要改一下APP類中相關(guān)代碼,吧模態(tài)改成非模態(tài)的就行

CWebBrowserDlg *dlg = new CWebBrowserDlg();

dlg->Create(IDD_WEBBROWSER_DIALOG);

m_pMainWnd = dlg;

INT_PTR nResponse = dlg->ShowWindow(SW_SHOW);


if (nResponse == IDOK)

{

  // TODO: 在此放置處理何時(shí)用

  //  “確定”來關(guān)閉對(duì)話框的代碼

}

else if (nResponse == IDCANCEL)

{

  // TODO: 在此放置處理何時(shí)用

  //  “取消”來關(guān)閉對(duì)話框的代碼

}


// 由于對(duì)話框已關(guān)閉,所以將返回 FALSE 以便退出應(yīng)用程序,

//  而不是啟動(dòng)應(yīng)用程序的消息泵。


//由于是非模態(tài)對(duì)話框,所以這里需要自己寫消息環(huán)

MSG msg = { 0 };

while (GetMessage(&msg, NULL, 0, 0))

{

  TranslateMessage(&msg);

  DispatchMessageW(&msg);

}


delete dlg;

臥槽,居然成功了,能正常相應(yīng)了!為什么模態(tài)就不行呢,后來我在復(fù)盤的時(shí)候想到,應(yīng)該是wkeWebView的窗口并沒有做成那種嚴(yán)格意義上的子窗口,它是一個(gè)獨(dú)立的,所以模態(tài)對(duì)話框把消息給攔截了不讓傳到其他的窗口導(dǎo)致的這個(gè)問題。
這個(gè)也算是成功了。

這個(gè)時(shí)候問題又來了,程序關(guān)不掉了,雖然說窗口是關(guān)了,但是程序并沒有退出,后來調(diào)試發(fā)現(xiàn),消息環(huán)沒有退出。這個(gè)時(shí)候我想到應(yīng)該是關(guān)閉時(shí)調(diào)用的是EndDialog。但是此時(shí)已經(jīng)改成非模態(tài)了,需要最后調(diào)用DestroyWindow,那么這個(gè)地方就得去對(duì)話框的OnClose消息中改。

void CWebBrowserDlg::OnClose()

{

// TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值

DestroyWindow();

//CDialog::OnClose();

}

好了,這個(gè)時(shí)候基本已經(jīng)完成了。就剩下一些按鈕事件處理了。

按鈕事件的處理

這里直接貼代碼吧,基本只有幾行,很容易看懂的

void CWebBrowserDlg::OnBnClickedBtnBack()

{

// TODO: 在此添加控件通知處理程序代碼

if (wkeCanGoBack(m_web))

{

wkeGoBack(m_web);

}

}


void CWebBrowserDlg::OnBnClickedBtnForward()

{

// TODO: 在此添加控件通知處理程序代碼

if (wkeCanGoForward(m_web))

{

wkeGoForward(m_web);

}

}


void CWebBrowserDlg::OnBnClickedBtnStop()

{

// TODO: 在此添加控件通知處理程序代碼

wkeStopLoading(m_web);

}


void CWebBrowserDlg::OnBnClickedBtnRefresh()

{

// TODO: 在此添加控件通知處理程序代碼

wkeReload(m_web);

}


void CWebBrowserDlg::OnBnClickedBtnGo()

{

// TODO: 在此添加控件通知處理程序代碼

CString csurl;

GetDlgItem(IDC_EDIT_URL)->GetWindowText(csurl);

wkeLoadURLW(m_web, csurl);

}


//設(shè)置代理

void CWebBrowserDlg::OnBnClickedBtnProxy()

{

CDlgProxySet dlgProxySet;

dlgProxySet.DoModal();

wkeProxy proxy;

proxy.type = WKE_PROXY_HTTP;

USES_CONVERSION;

strcpy_s(proxy.hostname, sizeof(proxy.hostname), T2A(dlgProxySet.csIP));


proxy.port = dlgProxySet.m_port;

wkeSetProxy(&proxy);

// TODO: 在此添加控件通知處理程序代碼

}

wkeView 的回調(diào)函數(shù)

現(xiàn)在主體功能已經(jīng)完成了,要跟瀏覽器類似,需要處理這樣幾個(gè)東西。第一個(gè)是url欄中的內(nèi)容會(huì)根據(jù)當(dāng)前主頁(yè)面的url做調(diào)整,特別是針對(duì)302、301 跳轉(zhuǎn)的情況。第二個(gè)是窗口的標(biāo)題應(yīng)該改為頁(yè)面的標(biāo)題;第三個(gè)是在某些頁(yè)面中超鏈接用的是_blank,時(shí)應(yīng)該能正常打開新窗口。

為了實(shí)現(xiàn)這些目標(biāo),我們需要處理一些wkeView的事件,我們創(chuàng)建了wkeWebView 之后直接綁定這些事件

wkeOnTitleChanged(m_web, wkeOnTitleChangedCallBack, this); //最后一個(gè)參數(shù)是傳遞用戶數(shù)據(jù),這里我們傳遞this指針進(jìn)去

wkeOnURLChanged(m_web, wkeOnURLChangedCallBack, this);

wkeOnNavigation(m_web, wkeOnNavigationCallBack, this);

wkeOnCreateView(m_web, onBrowserCreateView, this);

// 頁(yè)面標(biāo)題更改時(shí)調(diào)用此回調(diào)

void _cdecl wkeOnTitleChangedCallBack(wkeWebView webView, void* param, const wkeString title)

{

CWebBrowserDlg *pDlg = (CWebBrowserDlg*)param;

if (NULL != pDlg)

{

pDlg->SetWindowText(wkeGetStringW(title));

}

}


//url變更時(shí)調(diào)用此回調(diào)

void _cdecl wkeOnURLChangedCallBack(wkeWebView webView, void* param, const wkeString url)

{

CWebBrowserDlg *pDlg = (CWebBrowserDlg*)param;

if (NULL != pDlg)

{

pDlg->GetDlgItem(IDC_EDIT_URL)->SetWindowTextW(wkeGetStringW(url));

}

}


//網(wǎng)頁(yè)開始瀏覽將觸發(fā)回調(diào), 這里主要是為了它能打開一些本地的程序

bool _cdecl wkeOnNavigationCallBack(wkeWebView webView, void* param, wkeNavigationType navigationType, const wkeString url)

{

const wchar_t* urlStr = wkeGetStringW(url);

if (wcsstr(urlStr, L"exec://") == urlStr) {

PROCESS_INFORMATION processInfo = { 0 };

STARTUPINFOW startupInfo = { 0 };

startupInfo.cb = sizeof(startupInfo);

BOOL succeeded = CreateProcessW(NULL, (LPWSTR)urlStr + 7, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);

if (succeeded) {

CloseHandle(processInfo.hProcess);

CloseHandle(processInfo.hThread);

}

return false;

}


return true;

}


//網(wǎng)頁(yè)點(diǎn)擊a標(biāo)簽創(chuàng)建新窗口時(shí)將觸發(fā)回調(diào)

wkeWebView _cdecl onBrowserCreateView(wkeWebView webView, void* param, wkeNavigationType navType, const wkeString urlStr, const wkeWindowFeatures* features)

{

const wchar_t* url = wkeGetStringW(urlStr);


wkeWebView newWindow = wkeCreateWebWindow(WKE_WINDOW_TYPE_POPUP, NULL, features->x, features->y, features->width, features->height);

wkeShowWindow(newWindow, true);

return newWindow;

}

至此這個(gè)瀏覽器的demo就完成了。最后貼上對(duì)應(yīng)的demo項(xiàng)目地址: https://github.com/aMonst/WebBrowser
PS:最近有一位朋友發(fā)郵件告訴我說,wkeWebView 不能響應(yīng)鍵盤消息與對(duì)話框是模態(tài)還是非模態(tài)無關(guān),主要是要處理wkeWebView的WM_GETDLGCODE 消息,那位朋友給出的代碼如下:

switch(uMsg)

{

case WM_GETDLGCODE:

return DLGC_WANTARROWS | DLGC_WANTALLKEYS | DLGC_WANTCHARS;

}

CPP復(fù)制全屏

我試了一下,發(fā)現(xiàn)確實(shí)是這樣,相比較我上面提出的改為非模態(tài)的方式來說,還是用模態(tài)對(duì)話框方便、畢竟MFC對(duì)話框程序本來就是非模態(tài)的。所以這里我將代碼做了一下修改。并同步到了GitHub上。最后再次感謝那位發(fā)郵件告訴的朋友。。。。。

參考資料


閱讀原文:https://www.cnblogs.com/lanuage/p/18541298


該文章在 2025/1/8 15:20:35 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved