2024-11-21 21:50:52 +08:00
|
|
|
|
// SimpleBrowser.cpp --- simple Win32 browser
|
2024-01-22 12:56:15 +08:00
|
|
|
|
// Copyright (C) 2019 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
|
|
|
|
// This file is public domain software.
|
|
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
|
// SimpleBrowser.cpp --- simple Win32 browser
|
|
|
|
|
// Copyright (C) 2019 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
|
|
|
|
|
// This file is public domain software.
|
|
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
2024-04-02 15:36:52 +08:00
|
|
|
|
#include "MWebBrowser.hpp"
|
2024-01-22 12:56:15 +08:00
|
|
|
|
|
2024-01-22 14:33:11 +08:00
|
|
|
|
BOOL GetIEVersion(LPWSTR pszVersion, DWORD cchVersionMax)
|
|
|
|
|
{
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CRegKey hKey;
|
|
|
|
|
if (ERROR_SUCCESS != hKey.Open(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Internet Explorer)", KEY_READ))
|
|
|
|
|
return FALSE;
|
|
|
|
|
DWORD cb = cchVersionMax * sizeof(WCHAR);
|
|
|
|
|
if ((ERROR_SUCCESS == hKey.QueryStringValue(L"svcVersion", pszVersion, &cb)) ||
|
|
|
|
|
(ERROR_SUCCESS == hKey.QueryStringValue(L"Version", pszVersion, &cb)))
|
|
|
|
|
return TRUE;
|
2024-01-22 14:33:11 +08:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2024-04-02 15:36:52 +08:00
|
|
|
|
static DWORD getemulation()
|
|
|
|
|
{
|
2024-01-22 14:33:11 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
2024-01-22 12:56:15 +08:00
|
|
|
|
BOOL DoSetBrowserEmulation(DWORD dwValue)
|
|
|
|
|
{
|
|
|
|
|
static const TCHAR s_szFeatureControl[] =
|
2024-12-29 19:32:24 +08:00
|
|
|
|
TEXT(R"(SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl)");
|
2024-01-22 12:56:15 +08:00
|
|
|
|
|
|
|
|
|
TCHAR szPath[MAX_PATH], *pchFileName;
|
|
|
|
|
GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath));
|
|
|
|
|
pchFileName = PathFindFileName(szPath);
|
|
|
|
|
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CRegKey hkeyControl;
|
|
|
|
|
if (ERROR_SUCCESS != hkeyControl.Open(HKEY_CURRENT_USER, s_szFeatureControl, KEY_ALL_ACCESS))
|
|
|
|
|
return FALSE;
|
2024-01-22 12:56:15 +08:00
|
|
|
|
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CRegKey hkeyEmulation;
|
|
|
|
|
if (ERROR_SUCCESS != hkeyEmulation.Create(hkeyControl, TEXT("FEATURE_BROWSER_EMULATION"), 0, 0, KEY_ALL_ACCESS, NULL, NULL))
|
|
|
|
|
return FALSE;
|
2024-01-22 12:56:15 +08:00
|
|
|
|
|
2024-12-29 19:32:24 +08:00
|
|
|
|
if (dwValue)
|
|
|
|
|
{
|
|
|
|
|
return ERROR_SUCCESS == hkeyEmulation.SetValue(dwValue, pchFileName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hkeyEmulation.DeleteValue(pchFileName);
|
|
|
|
|
return TRUE;
|
2024-01-22 12:56:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-21 21:50:52 +08:00
|
|
|
|
class MWebBrowserEx : public MWebBrowser
|
|
|
|
|
{
|
|
|
|
|
HWND hwndParent;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
std::vector<std::tuple<std::optional<std::wstring>, int>> menuitems;
|
2025-01-02 17:47:21 +08:00
|
|
|
|
std::map<int, void (*)(LPCWSTR)> menucallbacks;
|
|
|
|
|
UINT CommandBase = 10086;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
static MWebBrowserEx *Create(HWND _hwndParent);
|
|
|
|
|
// IDocHostUIHandler interface
|
|
|
|
|
STDMETHODIMP ShowContextMenu(
|
|
|
|
|
DWORD dwID,
|
|
|
|
|
POINT *ppt,
|
|
|
|
|
IUnknown *pcmdtReserved,
|
|
|
|
|
IDispatch *pdispReserved);
|
2025-01-02 17:47:21 +08:00
|
|
|
|
HRESULT getselectedtext(LPWSTR *selectedText)
|
|
|
|
|
{
|
|
|
|
|
CComPtr<IHTMLDocument2> pDocument;
|
|
|
|
|
CHECK_FAILURE(GetIHTMLDocument2(&pDocument));
|
|
|
|
|
CComPtr<IHTMLSelectionObject> pSelectionObj;
|
|
|
|
|
CHECK_FAILURE(pDocument->get_selection(&pSelectionObj));
|
|
|
|
|
CComPtr<IHTMLTxtRange> pTxtRange;
|
|
|
|
|
CHECK_FAILURE(pSelectionObj->createRange((IDispatch **)&pTxtRange));
|
|
|
|
|
CHECK_FAILURE(pTxtRange->get_text(selectedText));
|
|
|
|
|
if (!*selectedText)
|
|
|
|
|
return -1;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
2024-11-21 21:50:52 +08:00
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
MWebBrowserEx(HWND _hwndParent);
|
|
|
|
|
};
|
|
|
|
|
MWebBrowserEx::MWebBrowserEx(HWND _hwndParent) : MWebBrowser(_hwndParent), hwndParent(_hwndParent)
|
|
|
|
|
{
|
|
|
|
|
}
|
2025-01-02 17:47:21 +08:00
|
|
|
|
LRESULT CALLBACK Extra_Menu_Handle(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
|
|
|
{
|
|
|
|
|
auto proc = (WNDPROC)GetProp(hwnd, L"GWLP_WNDPROC");
|
|
|
|
|
auto thisptr = (MWebBrowserEx *)GetProp(hwnd, L"THIS_PTR");
|
|
|
|
|
if (msg == WM_COMMAND)
|
|
|
|
|
{
|
|
|
|
|
if (thisptr->menucallbacks.find((int)wp) != thisptr->menucallbacks.end())
|
|
|
|
|
{
|
|
|
|
|
[&]()
|
|
|
|
|
{
|
|
|
|
|
CComBSTR selectedText;
|
|
|
|
|
CHECK_FAILURE_NORET(thisptr->getselectedtext(&selectedText));
|
|
|
|
|
thisptr->menucallbacks[(int)wp](selectedText);
|
|
|
|
|
}();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return proc(hwnd, msg, wp, lp);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 21:50:52 +08:00
|
|
|
|
MWebBrowserEx *MWebBrowserEx::Create(HWND _hwndParent)
|
|
|
|
|
{
|
|
|
|
|
MWebBrowserEx *pBrowser = new MWebBrowserEx(_hwndParent);
|
|
|
|
|
if (!pBrowser->IsCreated())
|
|
|
|
|
{
|
|
|
|
|
pBrowser->Release();
|
|
|
|
|
pBrowser = NULL;
|
|
|
|
|
}
|
2025-01-02 17:47:21 +08:00
|
|
|
|
SetProp(_hwndParent, L"GWLP_WNDPROC", (HANDLE)GetWindowLongPtr(_hwndParent, GWLP_WNDPROC));
|
|
|
|
|
SetProp(_hwndParent, L"THIS_PTR", (HANDLE)pBrowser);
|
|
|
|
|
SetWindowLongPtr(_hwndParent, GWLP_WNDPROC, (ULONG_PTR)Extra_Menu_Handle);
|
2024-11-21 21:50:52 +08:00
|
|
|
|
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()
|
2024-05-31 16:53:16 +08:00
|
|
|
|
{
|
|
|
|
|
return getemulation();
|
|
|
|
|
}
|
2024-11-21 21:50:52 +08:00
|
|
|
|
DECLARE_API void *html_new(HWND parent)
|
2024-04-02 15:36:52 +08:00
|
|
|
|
{
|
|
|
|
|
DoSetBrowserEmulation(getemulation());
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto s_pWebBrowser = MWebBrowserEx::Create(parent);
|
2024-01-22 12:56:15 +08:00
|
|
|
|
if (!s_pWebBrowser)
|
|
|
|
|
return NULL;
|
2024-04-02 15:36:52 +08:00
|
|
|
|
|
|
|
|
|
s_pWebBrowser->put_Silent(VARIANT_TRUE);
|
|
|
|
|
|
|
|
|
|
s_pWebBrowser->AllowInsecure(TRUE);
|
|
|
|
|
|
|
|
|
|
return s_pWebBrowser;
|
2024-01-22 12:56:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 21:50:52 +08:00
|
|
|
|
DECLARE_API void html_navigate(void *web, wchar_t *path)
|
2024-04-02 15:36:52 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-04-02 15:36:52 +08:00
|
|
|
|
ww->Navigate2(path);
|
2024-01-22 12:56:15 +08:00
|
|
|
|
}
|
2024-11-21 21:50:52 +08:00
|
|
|
|
DECLARE_API void html_resize(void *web, int x, int y, int w, int h)
|
2024-04-02 15:36:52 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-04-02 15:36:52 +08:00
|
|
|
|
RECT r;
|
|
|
|
|
r.left = x;
|
|
|
|
|
r.top = y;
|
|
|
|
|
r.right = x + w;
|
|
|
|
|
r.bottom = y + h;
|
|
|
|
|
ww->MoveWindow(r);
|
2024-01-22 12:56:15 +08:00
|
|
|
|
}
|
2024-11-21 21:50:52 +08:00
|
|
|
|
DECLARE_API void html_release(void *web)
|
2024-04-02 15:36:52 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-01-22 12:56:15 +08:00
|
|
|
|
ww->Destroy();
|
2024-04-02 15:36:52 +08:00
|
|
|
|
// ww->Release(); Destroy减少引用计数,自动del
|
2024-04-29 22:21:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 19:32:24 +08:00
|
|
|
|
DECLARE_API void html_get_current_url(void *web, void (*cb)(LPCWSTR))
|
2024-04-29 22:21:00 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
2024-12-29 19:32:24 +08:00
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CComBSTR _u;
|
|
|
|
|
CHECK_FAILURE_NORET(ww->get_LocationURL(&_u));
|
|
|
|
|
cb(_u);
|
2024-04-29 22:21:00 +08:00
|
|
|
|
}
|
2024-05-18 17:53:33 +08:00
|
|
|
|
|
2024-11-21 21:50:52 +08:00
|
|
|
|
DECLARE_API void html_set_html(void *web, wchar_t *html)
|
2024-05-18 17:53:33 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-05-18 17:53:33 +08:00
|
|
|
|
ww->SetHtml(html);
|
|
|
|
|
}
|
2025-01-02 17:47:21 +08:00
|
|
|
|
DECLARE_API void html_add_menu(void *web, int index, const wchar_t *label, void (*callback)(const wchar_t *))
|
2024-11-21 21:50:52 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
|
|
|
|
std::optional<std::wstring> _label;
|
|
|
|
|
if (label)
|
|
|
|
|
_label = label;
|
|
|
|
|
auto ptr = ww->menuitems.begin() + index;
|
2025-01-02 17:47:21 +08:00
|
|
|
|
auto command = ww->CommandBase++;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
ww->menuitems.insert(ptr, {_label, command});
|
2025-01-02 17:47:21 +08:00
|
|
|
|
ww->menucallbacks[command] = callback;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
}
|
2024-12-29 19:32:24 +08:00
|
|
|
|
DECLARE_API void html_get_select_text(void *web, void (*cb)(LPCWSTR))
|
2024-11-21 21:50:52 +08:00
|
|
|
|
{
|
|
|
|
|
if (!web)
|
2024-12-29 19:32:24 +08:00
|
|
|
|
return;
|
2024-11-21 21:50:52 +08:00
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CComBSTR selectedText;
|
2025-01-02 17:47:21 +08:00
|
|
|
|
CHECK_FAILURE_NORET(ww->getselectedtext(&selectedText));
|
2024-12-29 19:32:24 +08:00
|
|
|
|
cb(selectedText);
|
2024-11-21 21:50:52 +08:00
|
|
|
|
}
|
2024-12-22 01:02:46 +08:00
|
|
|
|
|
|
|
|
|
DECLARE_API void html_bind_function(void *web, const wchar_t *name, void (*function)(wchar_t **, int))
|
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
|
|
|
|
ww->jsobj->bindfunction(name, function);
|
|
|
|
|
}
|
2024-12-29 15:00:43 +08:00
|
|
|
|
|
|
|
|
|
DECLARE_API HWND html_get_ie(void *web)
|
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return nullptr;
|
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
|
|
|
|
return ww->GetIEServerWindow();
|
|
|
|
|
}
|
2024-12-29 15:35:27 +08:00
|
|
|
|
|
|
|
|
|
DECLARE_API void html_eval(void *web, const wchar_t *js)
|
|
|
|
|
{
|
|
|
|
|
if (!web)
|
|
|
|
|
return;
|
|
|
|
|
auto ww = static_cast<MWebBrowserEx *>(web);
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CComPtr<IHTMLDocument2> pDocument;
|
|
|
|
|
CHECK_FAILURE_NORET(ww->GetIHTMLDocument2(&pDocument));
|
2024-12-29 15:35:27 +08:00
|
|
|
|
CComPtr<IDispatch> scriptDispatch;
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CHECK_FAILURE_NORET(pDocument->get_Script(&scriptDispatch));
|
2024-12-29 15:35:27 +08:00
|
|
|
|
DISPID dispid;
|
2024-12-29 17:33:33 +08:00
|
|
|
|
CComBSTR evalStr = L"eval";
|
2024-12-29 19:32:24 +08:00
|
|
|
|
CHECK_FAILURE_NORET(scriptDispatch->GetIDsOfNames(IID_NULL, &evalStr, 1,
|
|
|
|
|
LOCALE_SYSTEM_DEFAULT, &dispid) != S_OK);
|
2024-12-29 15:35:27 +08:00
|
|
|
|
|
|
|
|
|
DISPPARAMS params;
|
|
|
|
|
VARIANT arg;
|
|
|
|
|
VARIANT result;
|
|
|
|
|
EXCEPINFO excepInfo;
|
|
|
|
|
UINT nArgErr = (UINT)-1;
|
|
|
|
|
params.cArgs = 1;
|
|
|
|
|
params.cNamedArgs = 0;
|
|
|
|
|
params.rgvarg = &arg;
|
|
|
|
|
arg.vt = VT_BSTR;
|
|
|
|
|
static const wchar_t *prologue = L"(function(){";
|
|
|
|
|
static const wchar_t *epilogue = L";})();";
|
|
|
|
|
int n = wcslen(prologue) + wcslen(epilogue) + wcslen(js) + 1;
|
|
|
|
|
auto eval = std::make_unique<wchar_t[]>(n);
|
|
|
|
|
_snwprintf(eval.get(), n, L"%s%s%s", prologue, js, epilogue);
|
2024-12-29 17:33:33 +08:00
|
|
|
|
CComBSTR bstrVal = eval.get();
|
|
|
|
|
arg.bstrVal = bstrVal;
|
|
|
|
|
scriptDispatch->Invoke(
|
|
|
|
|
dispid, IID_NULL, 0, DISPATCH_METHOD,
|
|
|
|
|
¶ms, &result, &excepInfo, &nArgErr);
|
2024-12-29 15:35:27 +08:00
|
|
|
|
}
|