// SimpleBrowser.cpp --- simple Win32 browser // Copyright (C) 2019 Katayama Hirofumi MZ // This file is public domain software. #define _CRT_SECURE_NO_WARNINGS // SimpleBrowser.cpp --- simple Win32 browser // Copyright (C) 2019 Katayama Hirofumi MZ // This file is public domain software. #define _CRT_SECURE_NO_WARNINGS #include "MWebBrowser.hpp" BOOL GetIEVersion(LPWSTR pszVersion, DWORD cchVersionMax) { pszVersion[0] = 0; HKEY hKey = NULL; RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Internet Explorer", 0, KEY_READ, &hKey); if (hKey) { DWORD cb = cchVersionMax * sizeof(WCHAR); LONG ret = RegQueryValueExW(hKey, L"svcVersion", NULL, NULL, (LPBYTE)pszVersion, &cb); if (ret != ERROR_SUCCESS) { ret = RegQueryValueExW(hKey, L"Version", NULL, NULL, (LPBYTE)pszVersion, &cb); } RegCloseKey(hKey); return ret == ERROR_SUCCESS; } return FALSE; } static DWORD getemulation() { DWORD m_emulation = 0; WCHAR szVersion[32]; if (GetIEVersion(szVersion, ARRAYSIZE(szVersion))) { if (szVersion[1] == L'.') { switch (szVersion[0]) { case '7': m_emulation = 7000; break; case '8': m_emulation = 8888; break; case '9': m_emulation = 9999; break; } } else if (szVersion[2] == L'.') { if (szVersion[0] == L'1' && szVersion[1] == L'0') { m_emulation = 10001; } if (szVersion[0] == L'1' && szVersion[1] == L'1') { m_emulation = 11001; } } } return m_emulation; } BOOL DoSetBrowserEmulation(DWORD dwValue) { static const TCHAR s_szFeatureControl[] = TEXT("SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl"); TCHAR szPath[MAX_PATH], *pchFileName; GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)); pchFileName = PathFindFileName(szPath); BOOL bOK = FALSE; HKEY hkeyControl = NULL; RegOpenKeyEx(HKEY_CURRENT_USER, s_szFeatureControl, 0, KEY_ALL_ACCESS, &hkeyControl); if (hkeyControl) { HKEY hkeyEmulation = NULL; RegCreateKeyEx(hkeyControl, TEXT("FEATURE_BROWSER_EMULATION"), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkeyEmulation, NULL); if (hkeyEmulation) { if (dwValue) { DWORD value = dwValue, size = sizeof(value); LONG result = RegSetValueEx(hkeyEmulation, pchFileName, 0, REG_DWORD, (LPBYTE)&value, size); bOK = (result == ERROR_SUCCESS); } else { RegDeleteValue(hkeyEmulation, pchFileName); bOK = TRUE; } RegCloseKey(hkeyEmulation); } RegCloseKey(hkeyControl); } return bOK; } class MWebBrowserEx : public MWebBrowser { HWND hwndParent; public: std::vector, int>> menuitems; static MWebBrowserEx *Create(HWND _hwndParent); // IDocHostUIHandler interface STDMETHODIMP ShowContextMenu( DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved); protected: MWebBrowserEx(HWND _hwndParent); }; MWebBrowserEx::MWebBrowserEx(HWND _hwndParent) : MWebBrowser(_hwndParent), hwndParent(_hwndParent) { } MWebBrowserEx *MWebBrowserEx::Create(HWND _hwndParent) { MWebBrowserEx *pBrowser = new MWebBrowserEx(_hwndParent); if (!pBrowser->IsCreated()) { pBrowser->Release(); pBrowser = NULL; } return pBrowser; } STDMETHODIMP MWebBrowserEx::ShowContextMenu( DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved) { HMENU hMenu = NULL; switch (dwID) { case CONTEXT_MENU_TEXTSELECT: { if (!menuitems.size()) return S_FALSE; HMENU hMenu = CreatePopupMenu(); int idx = 0; for (auto &item : menuitems) { if (std::get<0>(item)) { AppendMenu(hMenu, MF_STRING, std::get<1>(item), std::get<0>(item).value().c_str()); } else AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); idx += 1; } TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, ppt->x, ppt->y, 0, hwndParent, nullptr); DestroyMenu(hMenu); return S_OK; } default: return S_FALSE; } } DECLARE_API DWORD html_version() { return getemulation(); } DECLARE_API void *html_new(HWND parent) { DoSetBrowserEmulation(getemulation()); auto s_pWebBrowser = MWebBrowserEx::Create(parent); if (!s_pWebBrowser) return NULL; s_pWebBrowser->put_Silent(VARIANT_TRUE); s_pWebBrowser->AllowInsecure(TRUE); return s_pWebBrowser; } DECLARE_API void html_navigate(void *web, wchar_t *path) { if (!web) return; auto ww = static_cast(web); ww->Navigate2(path); } DECLARE_API void html_resize(void *web, int x, int y, int w, int h) { if (!web) return; auto ww = static_cast(web); RECT r; r.left = x; r.top = y; r.right = x + w; r.bottom = y + h; ww->MoveWindow(r); } DECLARE_API void html_release(void *web) { if (!web) return; auto ww = static_cast(web); ww->Destroy(); // ww->Release(); Destroy减少引用计数,自动del } DECLARE_API const wchar_t *html_get_current_url(void *web) { if (!web) return L""; auto ww = static_cast(web); wchar_t *_u; ww->get_LocationURL(&_u); return _u; } DECLARE_API void html_set_html(void *web, wchar_t *html) { if (!web) return; auto ww = static_cast(web); ww->SetHtml(html); } DECLARE_API void html_add_menu(void *web, int index, int command, const wchar_t *label) { if (!web) return; auto ww = static_cast(web); std::optional _label; if (label) _label = label; auto ptr = ww->menuitems.begin() + index; ww->menuitems.insert(ptr, {_label, command}); } BSTR GetSelectedText(IHTMLDocument2 *pHTMLDoc2) { IHTMLSelectionObject *pSelectionObj = nullptr; HRESULT hr = pHTMLDoc2->get_selection(&pSelectionObj); if (FAILED(hr) || pSelectionObj == nullptr) { return nullptr; } IHTMLTxtRange *pTxtRange = nullptr; hr = pSelectionObj->createRange((IDispatch **)&pTxtRange); pSelectionObj->Release(); if (FAILED(hr) || pTxtRange == nullptr) { return nullptr; } BSTR selectedText = nullptr; hr = pTxtRange->get_text(&selectedText); pTxtRange->Release(); return selectedText; } DECLARE_API const wchar_t *html_get_select_text(void *web) { if (!web) return L""; auto ww = static_cast(web); if (IHTMLDocument2 *pDocument = ww->GetIHTMLDocument2()) { auto text = GetSelectedText(pDocument); pDocument->Release(); // 不需要free,free会崩溃 return text; } return L""; } DECLARE_API void html_bind_function(void *web, const wchar_t *name, void (*function)(wchar_t **, int)) { if (!web) return; auto ww = static_cast(web); ww->jsobj->bindfunction(name, function); }