・settings.multi_threaded_message_loop = false; にする
・CefRenderHandler をオーバーライドしCefClientのインスタンスに設定
・イベントはSendMouseClickEvent等で送信
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Debug\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Debug\\libcef_dll_wrapper.lib") #else #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib") #endif #include <wx/evtloop.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <include/cef_app.h> #include <string> wxBitmap FromRGBA(const unsigned char* rgba_buffer, int width, int height) { // wxImage用にRGBを抜き出す // 既存の生配列からstd::vectorへの変換例 std::vector<unsigned char> rgb_data(rgba_buffer, rgba_buffer + width * height * 3); for (int i = 0; i < width * height; ++i) { rgb_data[i * 3 + 0] = rgba_buffer[i * 4 + 2]; rgb_data[i * 3 + 1] = rgba_buffer[i * 4 + 1]; rgb_data[i * 3 + 2] = rgba_buffer[i * 4 + 0]; } wxImage image(width, height, rgb_data.data(), true); // wxBitmapへ変換 return wxBitmap(image); }
class MyRenderHandler : public CefRenderHandler { public: wxFrame* m_pFrame = nullptr; wxBitmap *m_pbitmap = nullptr; void setBitmap(wxBitmap *bmp) { m_pbitmap = bmp; } void setFrame(wxFrame* frame) { m_pFrame = frame; } // GetViewRect() をoverrideする // 必須:ビューポートのサイズを返す void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override { // ビューポートのサイズを返す rect = CefRect(0, 0, width_, height_); } // 必須:レンダリング結果がここに来る void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override { // ※ buffer は 32bit BGRA フォーマット *m_pbitmap = FromRGBA(static_cast<const unsigned char*>(buffer), width, height); if (m_pFrame) { m_pFrame->Refresh(); // ウィンドウを更新 } } // 例: サイズを外から設定 void SetSize(int w, int h) { width_ = w; height_ = h; } private: int width_ = 800; int height_ = 600; IMPLEMENT_REFCOUNTING(MyRenderHandler); };
class MyHandler : public CefClient, public CefLifeSpanHandler { // CefRefPtr<CefRenderHandler> renderHandler_; public: // MyHandler(CefRefPtr<CefRenderHandler> renderHandler) : renderHandler_(renderHandler) {} // CefRefPtr<CefRenderHandler> GetRenderHandler() override { return renderHandler_; } CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { m_Browser = browser; } void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { m_Browser = nullptr; // イベントから抜ける if (wxEventLoopBase::GetActive()) { wxEventLoopBase::GetActive()->Exit(); } } void CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } CefRefPtr<CefBrowser> GetBrowser() { return m_Browser; } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; };
class MyCustomEventLoop : public wxGUIEventLoop { public: MyCustomEventLoop() = default; virtual ~MyCustomEventLoop() = default; protected: // Yieldが必要な場合 virtual void DoYieldFor(long eventsToProcess) override { // デフォルトの実装 wxGUIEventLoop::DoYieldFor(eventsToProcess); } // メッセージループの各イテレーション開始時に呼ばれるフック virtual void OnNextIteration() override { // デフォルトの実装 wxGUIEventLoop::OnNextIteration(); } ////////////////////////////////////////////// // メッセージを処理する bool Dispatch() override { bool running = true; MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) running = false; else { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } CefDoMessageLoopWork(); ::Sleep(10); return true; // まだ継続 } ////////////////////////////////////////////// };
// メインフレーム class MyFrame : public wxFrame { CefRefPtr<MyHandler> g_handler; wxBitmap m_cefbmp; public: MyFrame() : wxFrame(nullptr, wxID_ANY, "CEF Offscreen Rendering") { Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); Bind(wxEVT_SIZE, &MyFrame::OnSize, this); Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLButtonDown, this); Bind(wxEVT_LEFT_UP, &MyFrame::OnLButtonUp, this); CallAfter(&MyFrame::PostCreate); } private:
void OnLButtonDown(wxMouseEvent& event) { CefMouseEvent mouse_event; mouse_event.x = event.GetX(); mouse_event.y = event.GetY(); mouse_event.modifiers = event.GetModifiers(); // マウスボタンDOWN g_handler->GetBrowser()->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, // isMouseUp == false (DOWN) 1 ); event.Skip(); // 必要なら他のハンドラにも渡す }
void OnLButtonUp(wxMouseEvent& event) { CefMouseEvent mouse_event; mouse_event.x = event.GetX(); mouse_event.y = event.GetY(); mouse_event.modifiers = event.GetModifiers(); // マウスボタンUP g_handler->GetBrowser()->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, // isMouseUp == true (UP) 1 ); event.Skip(); }
void PostCreate() { CefRefPtr<MyRenderHandler> renderHandler = new MyRenderHandler(); renderHandler->setBitmap(&m_cefbmp); renderHandler->setFrame(this); renderHandler->SetSize(400, 400); // ビューポートのサイズを設定 g_handler = CefRefPtr<MyHandler>(new MyHandler(renderHandler) ); CefBrowserSettings browser_settings; CefWindowInfo window_info; CefRect cefRect( 0, 0, 400, 400); HWND hwnd = (HWND)this->GetHandle(); //window_info.SetAsChild(hwnd, cefRect); // オフスクリーンレンダリング window_info.SetAsWindowless(nullptr); CefBrowserHost::CreateBrowser( window_info, g_handler, "https://www.google.com", browser_settings, nullptr, nullptr); this->Layout(); // レイアウトの更新 } void OnClose(wxCloseEvent& evt) { // CEFのブラウザを閉じる g_handler->CloseAllBrowsers(true); // ウィンドウを閉じない指示 evt.Veto(); } void OnSize(wxSizeEvent& evt) { if (g_handler) { HWND hBrowserWnd = g_handler->GetBrowser()->GetHost()->GetWindowHandle(); // ウィンドウサイズ変更時の処理 if (g_handler && g_handler->GetBrowser()) { RECT rect(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight()); SetWindowPos(hBrowserWnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); // ブラウザにサイズ変更を通知 g_handler->GetBrowser()->GetHost()->WasResized(); } } evt.Skip(); // イベントをスキップしてデフォルトの処理を行う } // OnPaint() void OnPaint(wxPaintEvent& evt) { // 描画処理をここに追加 wxPaintDC dc(this); if (m_cefbmp.IsOk()) { // CEFのビットマップを描画 dc.DrawBitmap(m_cefbmp, 0, 0, true); } } }; class MyApp : public wxApp { public: bool OnInit() override { HINSTANCE hInstance = ::GetModuleHandle(NULL); CefMainArgs main_args(hInstance); int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); if (exit_code >= 0) return false; // CEFの設定 CefSettings settings; settings.no_sandbox = true; // マルチスレッドメッセージループを無効にする // オフスクリーンレンダリングでは false必須 settings.multi_threaded_message_loop = false; CefInitialize(main_args, settings, nullptr, nullptr); /////////////////////////////////////////////////// auto frame = new MyFrame(); frame->Show(); return true; } int OnRun() override { // wxGUIEventLoop を使って自前ループを構築 MyCustomEventLoop* loop = new MyCustomEventLoop; wxEventLoopBase::SetActive(loop); // アクティブなループとして登録 int retval = loop->Run(); // ここでメッセージループ開始 delete loop; // ループ終了後に解放 // CEFのシャットダウン CefShutdown(); return retval; } virtual int OnExit() { return wxApp::OnExit(); } }; // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Debug\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Debug\\libcef_dll_wrapper.lib") #else #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib") #endif #include <wx/evtloop.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <include/cef_app.h> #include <string>
class MyHandler : public CefClient, public CefLifeSpanHandler { public: MyHandler() {} CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { m_Browser = browser; } void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { m_Browser = nullptr; // イベントから抜ける if (wxEventLoopBase::GetActive()) { wxEventLoopBase::GetActive()->Exit(); } } void CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } CefRefPtr<CefBrowser> GetBrowser() { return m_Browser; } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; };
class MyCustomEventLoop : public wxGUIEventLoop { public: MyCustomEventLoop() = default; virtual ~MyCustomEventLoop() = default; protected: // Yieldが必要な場合 virtual void DoYieldFor(long eventsToProcess) override { // デフォルトの実装 wxGUIEventLoop::DoYieldFor(eventsToProcess); } // メッセージループの各イテレーション開始時に呼ばれるフック virtual void OnNextIteration() override { // デフォルトの実装 wxGUIEventLoop::OnNextIteration(); } ////////////////////////////////////////////// // メッセージを処理する bool Dispatch() override { bool running = true; MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) running = false; else { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } CefDoMessageLoopWork(); ::Sleep(10); return true; // まだ継続 } ////////////////////////////////////////////// };
// メインフレーム class MyFrame : public wxFrame { CefRefPtr<MyHandler> g_handler; public: MyFrame() : wxFrame(nullptr, wxID_ANY, "メッセージループ") { Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); Bind(wxEVT_SIZE, &MyFrame::OnSize, this); CallAfter(&MyFrame::PostCreate); } private: void PostCreate() { g_handler = CefRefPtr<MyHandler>(new MyHandler); CefBrowserSettings browser_settings; CefWindowInfo window_info; CefRect cefRect( 0, 0, 400, 400); HWND hwnd = (HWND)this->GetHandle(); window_info.SetAsChild(hwnd, cefRect); CefBrowserHost::CreateBrowser( window_info, g_handler, "https://www.google.com", browser_settings, nullptr, nullptr); this->Layout(); // レイアウトの更新 } void OnClose(wxCloseEvent& evt) { // CEFのブラウザを閉じる g_handler->CloseAllBrowsers(true); // ウィンドウを閉じない指示 evt.Veto(); } void OnSize(wxSizeEvent& evt) { if (g_handler) { HWND hBrowserWnd = g_handler->GetBrowser()->GetHost()->GetWindowHandle(); // ウィンドウサイズ変更時の処理 if (g_handler && g_handler->GetBrowser()) { RECT rect(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight()); SetWindowPos(hBrowserWnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); // ブラウザにサイズ変更を通知 g_handler->GetBrowser()->GetHost()->WasResized(); } } evt.Skip(); // イベントをスキップしてデフォルトの処理を行う } }; class MyApp : public wxApp { public: bool OnInit() override { HINSTANCE hInstance = ::GetModuleHandle(NULL); CefMainArgs main_args(hInstance); int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); if (exit_code >= 0) return false; // CEFの設定 CefSettings settings; settings.no_sandbox = true; // マルチスレッドメッセージループを無効にする settings.multi_threaded_message_loop = false; CefInitialize(main_args, settings, nullptr, nullptr); /////////////////////////////////////////////////// auto frame = new MyFrame(); frame->Show(); return true; } int OnRun() override { // wxGUIEventLoop を使って自前ループを構築 MyCustomEventLoop* loop = new MyCustomEventLoop; wxEventLoopBase::SetActive(loop); // アクティブなループとして登録 int retval = loop->Run(); // ここでメッセージループ開始 delete loop; // ループ終了後に解放 // CEFのシャットダウン CefShutdown(); return retval; } virtual int OnExit() { return wxApp::OnExit(); } }; // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
wxWidgetsはメッセージループを隠蔽している。これを加工する。
wxGUIEventLoopを継承して、必要な機能のみ実装する。
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #include <wx/evtloop.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> class MyCustomEventLoop : public wxGUIEventLoop { public: MyCustomEventLoop() = default; virtual ~MyCustomEventLoop() = default; protected:
// Yieldが必要な場合 virtual void DoYieldFor(long eventsToProcess) override { // デフォルトの実装 wxGUIEventLoop::DoYieldFor(eventsToProcess); }
// メッセージループの各イテレーション開始時に呼ばれるフック virtual void OnNextIteration() override { ////////////////////////////////// static int counter = 0; counter++; char ss[64]; snprintf(ss, sizeof(ss), "OnNextIteration called: %d", counter); wxLogDebug(ss); ////////////////////////////////// // デフォルトの実装 wxGUIEventLoop::OnNextIteration(); }
////////////////////////////////////////////// // メッセージを処理する bool Dispatch() override { MSG msg;// メッセージループの処理はOS依存 if (!::GetMessage(&msg, NULL, 0, 0)) return false; // WM_QUIT if (msg.message == WM_LBUTTONDOWN)// 処理例:WM_LBUTTONDOWNを無効化 { wxLogDebug("Ignored in custom Dispatch"); // WM_LBUTTONDOWN このメッセージを処理しないためにここでtrueを返す return true; } ProcessMessage(&msg); return true; } //////////////////////////////////////////////
// イベント処理のメインループ // *メッセージループ中に何かしたいだけなら、OnNextIteration() をオーバーライドするだけでよい // *メッセージを横取りするような用途では、Dispatch() をオーバーライドするだけでよい // より細かい制御が必要なら DoRun() をオーバーライドすることもできる #define IF_YOU_NEED_DORUN #if defined( IF_YOU_NEED_DORUN ) virtual int DoRun() override { for (;;) { // 1) ループ開始フック OnNextIteration(); // 2) キュー内の全イベントを処理 while (!m_shouldExit && Pending()) { // Dispatch() が false を返したらループ終了 if (!Dispatch()) break; } if (m_shouldExit) break; // 3) アイドル処理:戻り値 true なら更にアイドルイベントを送り続ける if (ProcessIdle()) { continue; } // 4) 何もやることがなければ少し待機 wxThread::Sleep(10); } // 返り値は Exit() でセットされたコード return m_exitcode; } #endif // IF_YOU_NEED
}; class MyFrame : public wxFrame { public: MyFrame() : wxFrame(nullptr, wxID_ANY, "メッセージループ") { Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); // クリックイベント Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event) { wxLogMessage("Left mouse button clicked at (%d, %d)", event.GetX(), event.GetY()); }); } private: void OnClose(wxCloseEvent& evt) {
if (wxEventLoopBase::GetActive()) wxEventLoopBase::GetActive()->Exit();// プログラム終了
evt.Skip(); // フレーム自体も閉じる } }; class MyApp : public wxApp { public: bool OnInit() override { auto frame = new MyFrame(); frame->Show(); return true; } int OnRun() override { // wxGUIEventLoop を使って自前ループを構築 MyCustomEventLoop* loop = new MyCustomEventLoop; wxEventLoopBase::SetActive(loop); // アクティブなループとして登録 int retval = loop->Run(); // ここでメッセージループ開始 delete loop; // ループ終了後に解放 return retval; } }; // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
CEFをwxWidgetsの上に貼り付けてみる。注意点として、配布されたdllではdebugモードで実行できないらしい。必ずReleaseモードで実行する。
#ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #include <include/cef_app.h> #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib") ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string>
class MyHandler : public CefClient, public CefLifeSpanHandler { public: MyHandler() {} CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { m_Browser = browser; } void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { m_Browser = nullptr; } void CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; };
// ウィンドウ作成 class MyFrame : public wxFrame {
CefRefPtr<MyHandler> g_handler;
public: void PostCreate() {
g_handler = CefRefPtr<MyHandler>(new MyHandler); CefBrowserSettings browser_settings; CefWindowInfo window_info; CefRect cefRect( 0, 0, 400, 400); HWND hwnd = (HWND)this->GetHandle(); window_info.SetAsChild(hwnd, cefRect); CefBrowserHost::CreateBrowser( window_info, g_handler, "https://www.google.com", browser_settings, nullptr, nullptr);
this->Layout(); // レイアウトの更新 } void OnClose(wxCloseEvent& event) {
// CEFのブラウザを閉じる g_handler->CloseAllBrowsers(true);
event.Skip(); // デフォルトの処理を実行 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() {
HINSTANCE hInstance = ::GetModuleHandle(NULL); CefMainArgs main_args(hInstance); int exit_code = CefExecuteProcess(main_args, nullptr, nullptr); if (exit_code >= 0) return false; // CEFの設定 CefSettings settings; settings.no_sandbox = true; // マルチスレッドメッセージループを有効にする settings.multi_threaded_message_loop = true; CefInitialize(main_args, settings, nullptr, nullptr);
/////////////////////////////////////////////////// MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } virtual int OnExit() {
// CEFのシャットダウン CefShutdown();
return wxApp::OnExit(); } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
Win32apiのCreateWindowのhwndにChromeを張り付ける。
CefRunMessageLoopを使用すると、メッセージループにCEFのものを使う。
#include <windows.h> #include <iostream> #include <include/cef_app.h> #ifdef _DEBUG #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Debug\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Debug\\libcef_dll_wrapper.lib") #else #pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib") #pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib") #endif
class MyHandler : public CefClient , public CefLifeSpanHandler { public: MyHandler() {} CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; } void OnAfterCreated(CefRefPtr<CefBrowser> browser) override { m_Browser = browser; } void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { m_Browser = nullptr; } void CloseAllBrowsers(bool force_close) { if (m_Browser) { m_Browser->GetHost()->CloseBrowser(force_close); } } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; };
class MyApp : public CefApp { IMPLEMENT_REFCOUNTING(MyApp); };
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { MyHandler* handler; LPCREATESTRUCT pcs; switch (msg) { case WM_CLOSE:
handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (handler) { handler->CloseAllBrowsers(true); }
DestroyWindow(hwnd); return 0; case WM_CREATE:
pcs = (LPCREATESTRUCT)lp; handler = (MyHandler *)pcs->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(handler));
return 0; case WM_DESTROY:
CefQuitMessageLoop();
PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /////////////////////////////////////////////////////////////////////////
CefMainArgs main_args(hInstance); CefRefPtr<MyApp> app(new MyApp); // MyAppのインスタンス用のポインタ // サブプロセス処理 int exit_code = CefExecuteProcess(main_args, app, nullptr); if (exit_code >= 0) return exit_code; // CEFの設定 CefSettings settings; settings.no_sandbox = true; CefInitialize(main_args, settings, app, nullptr);
///////////////////////////////////////////////////////////////////////// HWND hwnd; MSG msg; WNDCLASS winc; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return -1;
CefRefPtr<MyHandler> g_handler = CefRefPtr<MyHandler>(new MyHandler);
hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("CEF test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, g_handler.get() ); if (hwnd == NULL) return -1;
CefRefPtr<CefClient> client = g_handler; // 型は CefClient で渡す CefBrowserSettings browser_settings; CefWindowInfo window_info; RECT rect; GetClientRect(hwnd, &rect); // 親ウィンドウのクライアント領域 CefRect cefRect( rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); window_info.SetAsChild(hwnd, cefRect); CefBrowserHost::CreateBrowser( window_info, g_handler, "https://www.google.com", browser_settings, nullptr, nullptr); CefRunMessageLoop(); // メッセージループ // settings.multi_threaded_message_loop = true;の時はコメントアウトすること CefShutdown(); // CEF終了処理
return 0; }
CefRunMessageLoopを使わない場合、メッセージループは自分で書き、CEFのメッセージを処理するためにループ内で CefDoMessageLoopWork() を呼び出す。
また、CefDoMessageLoopWork内でPeekMessageを呼んでいる関係でWM_QUITを検知できなくなるので、GetMessageを使わずにMsgWaitForMultipleObjectsとPeekMessageでメッセージループを管理する。
// CefRunMessageLoop(); // メッセージループ // 注意 GetMessageは使わない // CefDoMessageLoopWorkは中でPeekMessageを使っている // PeekMessage は強制的にWM_QUITを取り出してしまうので、 // こちら側でGetMessageをつかうと先にWM_QUITを取り出されて // 終了を感知できなくなりループから抜け出せなくなる // **CefDoMessageLoopWorkは内部でタイマーイベントなどを多数キューに投げているので、 // **GetMessageがタイマーを補足し、次に入っていたWM_QUITはCefDoMessageLoopWorkが取り出すという // **現象が起こる // bool should_quit = false; while (!should_quit) { // MsgWaitForMultipleObjects = メッセージが来るまでスレッドをスリープ DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, INFINITE, QS_ALLINPUT); // もし何かメッセージが来たら処理 if (result == WAIT_OBJECT_0) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { should_quit = true; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } } if (!should_quit) { CefDoMessageLoopWork(); } }
multi_threaded_message_loopをtrueに設定すると、CEFが専用スレッドで動く。この場合、CefRunMessageLoop(),CefShutdown()を使用してはいけない。
メッセージループは自プログラムのメッセージだけを処理する。
CefSettings settings; settings.no_sandbox = true; settings.multi_threaded_message_loop = true; // を指定して専用スレッドでCEFを動かす場合、 // CefRunMessageLoop()も CefDoMessageLoopWork() も CefShutdown() も使ってはいけない /* ... */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 使わない CefRunMessageLoop(); // settings.multi_threaded_message_loop = true;の時はコメントアウトすること // 使わない CefShutdown(); return 0;// msg.wParam; }
CEFはアプリケーションにChromeを組み込むためのフレームワークである(辞書的説明)。用は自アプリにChromeの機能を埋め込むためのライブラリ。
以下からビルド済みライブラリをダウンロード:
https://cef-builds.spotifycdn.com/index.html
ライブラリ自体はビルド済みだが、C++から使用するにはwrapper dllをビルドした方がよい。このためにCMakeする。
使用時、/MD でリンクしたいなら、必ずUSE_SANDBOX=OFFにする。
これはCEFが持っているsandboxという機能が/MTでビルドされているからで、この/MDバージョンは用意されていない。使う場合はCEFのビルドそのものをやらないといけなくなる。sandboxを使わないとセキュリティが下がるらしいが、とりあえず当分配布するものは作らないのでsandboxをOFFにする。
libcef_dll_wrapperをビルドする。他のものはサンプルなので無視してもいい。
今回はlibcef_dll_wrapperを/MDに設定して、libcef_dll_wrapperを右クリック→ビルド でビルドする。
・libcef_dll_wrapper/Release/libcef_dll_wrapper.lib
・libcef_dll_wrapper/Debug/libcef_dll_wrapper.lib
が生成される。
一応、ALL_BUILDしたcefsimpleを実行してみる。
上ではlibcef_dll_wrapperだけビルドする話をした。当然ALL_BUILDもできるが全てのプロジェクトで/MDまたは/MTに統一する必要がある。
あと、cefsimple.exeは二つ起動すると二つ目がChromeのウィンドウになるという謎動作をする。
pyenv-virtualenvを使用すれば、Python 環境のバージョンを含めた作業環境の切り替えが出来る。
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
.vcxprojとcsprojはxmlなので、pugixmlで内容を解析できる。例えばプロジェクト名を変えてみる。
#include <iostream> #include <fstream> #include "pugixml.hpp"
// vcxprojファイルへのアクセスと編集 void vcxproj(std::string vcxproj_path) { pugi::xml_document doc; doc.load_file(vcxproj_path.c_str()); pugi::xml_node property_group; pugi::xml_node project = doc.child("Project"); for (pugi::xml_node node : project.children("PropertyGroup")) { if (node.attribute("Label") && std::string(node.attribute("Label").value()) == "Globals") { property_group = node; break; } } // プロジェクト名を取得 pugi::xml_node root_namespace = property_group.child("RootNamespace"); std::cout << root_namespace.child_value();
// プロジェクト名を編集 root_namespace.text().set("MyNewProjectName"); doc.save_file(vcxproj_path.c_str()); }
// csprojファイルへのアクセスと編集 void csproj(std::string csproj_path) { pugi::xml_document doc; doc.load_file(csproj_path.c_str()); pugi::xml_node project = doc.child("Project"); pugi::xml_node root_namespace; for (pugi::xml_node node : project.children("PropertyGroup")) { pugi::xml_node candidate = node.child("RootNamespace"); if (candidate) { root_namespace = candidate; break; } } // プロジェクト名を取得 std::cout << root_namespace.text().as_string() << std::endl; // プロジェクト名を編集 root_namespace.text().set("MyNewProjectName"); doc.save_file(csproj_path.c_str()); }
int main() { std::string cs_proj_path = R"(C:\test\ConsoleApp1-SCharpProj\ConsoleApp1-SCharpProj.csproj)"; std::string vc_proj_path = R"(C:\test\ConsoleAppl-CPPProj\ConsoleAppl-CPPProj.vcxproj)"; csproj(cs_proj_path); vcxproj(vc_proj_path); }