別に詰まったわけではないし、公式からダウンロードしたもので事足りるのだけれど、一応成功事例を残しておく。
Window 11, Visual C++ 2022 x64
以下を設定
BUILD_SHARED_LIBS=ON
これをすると、WEBRTCが使えなくなるのでOFFにする
BUILD_WEBRTC=OFF
BUILD_WEBRTC_FROM_SOURCE=OFF
あと、/MDにしたいので
STATIC_WINDOWS_RUNTIME=OFF

ビルド結果は、以下+resources。DLLが少ないのは良いこと。
Release:
lib/Open3D.lib
lib/tbb12.lib
bin/Open3D.dll
bin/tbb12.dll
Debug:
lib/Open3D.lib
lib/tbb12_debug.lib
bin/Open3D.dll
bin/tbb12_debug.dll

#include <iostream> #include <vector> #include <filesystem> #include <regex> #include <unordered_map> struct PathList { std::vector<std::filesystem::path> directories; std::vector<std::filesystem::path> files; };
// 指定したパス内のファイル一覧を取得
PathList GetFileList(const std::filesystem::path& _path) { PathList result; if (!std::filesystem::exists(_path) || !std::filesystem::is_directory(_path)) { return result; // 存在しない場合は空を返す } for (const auto& entry : std::filesystem::directory_iterator(_path)) { if (entry.is_directory()) { result.directories.push_back(entry.path()); } else if (entry.is_regular_file()) { result.files.push_back(entry.path()); } } return result; }
// パスのファイル名からIDを取り出し、pair<size_t, path> の配列で返す std::vector<std::pair<size_t, std::filesystem::path>> GetFileIncdies(const std::vector<std::filesystem::path>& pathlist, const std::regex& pattern, std::vector<std::filesystem::path>* excep) { std::vector<std::pair<size_t, std::filesystem::path>> result; for (const auto& p : pathlist) { const std::string name = p.filename().string(); std::smatch m; if (std::regex_search(name, m, pattern)) { // パターンにマッチしたかどうか bool ok = false; size_t idx = 0; for (size_t i = 1; i < m.size(); ++i) { const std::string s = m[i].str(); if (s.empty()) continue; // すべて数字かを判定 const bool all_digits = std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c) != 0; }); if (!all_digits) continue; try { idx = static_cast<size_t>(std::stoull(s)); ok = true; break; } catch (...) { // stoull 失敗時は次のキャプチャを試す } } if (ok) { result.emplace_back(idx, p); } else if (excep) { excep->push_back(p); } } else { if (excep) excep->push_back(p); } } // インデックス昇順、同値ならパスの辞書順 std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) { if (a.first != b.first) return a.first < b.first; return a.second < b.second; }); return result; }
template<typename Tuple,typename Pair,int Index> std::unordered_map<size_t, Tuple> CreateDataSetList(std::unordered_map<size_t, Tuple>& datamap,std::vector<Tuple>& datasets, std::vector<Pair>& pairs) { for (const auto& p : pairs) { size_t idx = p.first; const auto& path = p.second; auto& tupleitem = datamap[idx]; std::get<Index>(tupleitem) = path; // Index番目にパスをセット } return datamap; }
// マップをIDでソートしたものを返す template<typename Tuple> std::vector<std::pair<size_t, Tuple>> SortByID(const std::unordered_map<size_t, Tuple>& byidmap) { std::vector<std::pair<size_t, Tuple>> sorted; sorted.reserve(byidmap.size()); for (const auto& kv : byidmap) { sorted.emplace_back(kv.first, kv.second); } std::sort(sorted.begin(), sorted.end(), [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; // size_tキーで昇順 }); return sorted; }
using Tuple = std::tuple<std::filesystem::path, std::filesystem::path, std::filesystem::path>; using Pair = std::pair<size_t, std::filesystem::path>;
// パスとパターンからファイル一覧を作成し、インデックスとパスのペアを返す関数 std::vector<std::pair<size_t, std::filesystem::path> > GetFiles(const std::filesystem::path& path,std::regex pattern){ PathList fileList = GetFileList(path); std::vector<std::pair<size_t, std::filesystem::path> > flist; flist = GetFileIncdies(fileList.files, pattern, nullptr); return flist; }
int main() { std::vector< Tuple > dataSetList; std::vector<Pair > list_1 = GetFiles("C:\\test\\tmp\\txt", std::regex(R"(新規 テキスト000_(\d+)\.txt)") ); std::vector<Pair > list_2 = GetFiles("C:\\test\\tmp\\doc", std::regex(R"(doc_000_(\d+)\.doc)") ); std::unordered_map<size_t, Tuple> byidmap; CreateDataSetList<Tuple, Pair, 0>(byidmap, dataSetList, list_1); CreateDataSetList<Tuple, Pair, 1>(byidmap, dataSetList, list_2); std::vector<std::pair<size_t, Tuple>> sorted = SortByID(byidmap); for(const auto& b : sorted) { std::cout << "ID: " << b.first << " " << std::get<0>(b.second).filename() << " " << std::get<1>(b.second).filename() << " " << std::get<2>(b.second) << "\n"; } }
nlohmann::jsonによる書き込みを行う。書き込み順は記述順と同じでなくなる点に注意。
#include <iostream> #include <nlohmann/json.hpp> #include <fstream> int main() { nlohmann::json jsondata;
jsondata["information"] = { {"Name", "Tarou"}, {"Age", 18}, {"weight", 60.5} }; nlohmann::json& data = jsondata["data"]; data["dates"].push_back("2023-10-01T12:00:00+09:00"); data["dates"].push_back("2023-10-02T12:00:00+09:00"); nlohmann::json countries = { {{"id", 1}, {"name", "Russia"}}, {{"id", 2}, {"name", "France"}} }; data["countries"] = countries;
// JSONデータをファイルに保存
std::string filename = R"(C:\test\data\data2.json)";
std::ofstream file(filename);
if (file.is_open()) {
file << jsondata.dump(2); // インデントのスペース数
file.close();
std::cout << "JSON data saved to " << filename << std::endl;
} else {
std::cerr << "Failed to open file for writing: " << filename << std::endl;
}
return 0;
}
{
"data": {
"countries": [
{
"id": 1,
"name": "Russia"
},
{
"id": 2,
"name": "France"
}
],
"dates": [
"2023-10-01T12:00:00+09:00",
"2023-10-02T12:00:00+09:00"
]
},
"information": {
"Age": 18,
"Name": "Tarou",
"weight": 60.5
}
}
nlohmann::jsonの使用例を殆どやっていなかったことに気づいたのでここに置いておく。
#include <iostream> #include <nlohmann/json.hpp> #include <fstream> int main() { // json形式の文字列 std::string jsontext = R"( { "information":{ "Name":"Tarou", "Age":18, "weight":60.5 }, "data":{ "dates":[ "2023-10-01T12:00:00+09:00", "2023-10-02T12:00:00+09:00" ], "countries":[ { "id":1, "name":"Russia" }, { "id":2, "name":"France" } ] } } )"; //std::string filename = R"(C:\test\data\data.json)"; //std::ifstream file(filename); //nlohmann::json jsondata = nlohmann::json::parse(file); nlohmann::json jsondata = nlohmann::json::parse(jsontext); { nlohmann::json information = jsondata["information"]; // データの取得 std::string name = information["Name"]; int age = information["Age"]; double weight = information["weight"]; std::cout << "Name: " << name << std::endl; std::cout << "Age: " << age << std::endl; std::cout << "Weight: " << weight << std::endl; } { nlohmann::json data = jsondata["data"]; { nlohmann::json dates = data["dates"]; for (const auto& date : dates) { std::string date_str = date.get<std::string>(); std::cout << "Date: " << date_str << std::endl; } } { nlohmann::json countries = data["countries"]; for (const auto& country : countries) { int id = country["id"]; std::string name = country["name"]; std::cout << "Country ID: " << id << ", Name: " << name << std::endl; } } } }
構造体で受け取ることもできるが、あらかじめfrom_json関数を定義しておく必要がある
struct Country { int id; std::string name; };
// .get関数を使用するための定義 void from_json(const nlohmann::json& j, Country& p) { j.at("id").get_to(p.id); j.at("name").get_to(p.name); }
int main() { std::string filename = R"(C:\test\data\data.json)"; std::ifstream file(filename); nlohmann::json jsondata = nlohmann::json::parse(file); nlohmann::json data = jsondata["data"]; { std::vector<std::string> vectors = data["dates"]; for (const auto& date : vectors) { std::cout << "Date from vector: " << date << std::endl; } } { std::vector<Country> countries = data["countries"].get<std::vector<Country> >(); for (const auto& country : countries) { std::cout << "Country ID: " << country.id << ", Name: " << country.name << std::endl; } } }
#include <iostream> #include <string> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/depth_first_search.hpp> // .dotファイルの出力用 #include <boost/graph/graphviz.hpp>
// Vertexクラス // Boost Graph Libraryでは node ではなく vertex という用語を使用 struct VertexProperty { std::string name; };
// グラフ定義 using Graph = boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, //単方向グラフ VertexProperty >;
using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
// 深さ優先探索 class DFSVisitor : public boost::default_dfs_visitor { public: void discover_vertex(Vertex v, const Graph& g) const { std::cout << "Visited: " << g[v].name << std::endl; } };
int main() { Graph mygraph; // 頂点の追加 Vertex A = boost::add_vertex({ "A" }, mygraph); Vertex B = boost::add_vertex({ "B" }, mygraph); Vertex C = boost::add_vertex({ "C" }, mygraph); Vertex D = boost::add_vertex({ "D" }, mygraph); Vertex E = boost::add_vertex({ "E" }, mygraph); boost::add_edge(A, B, mygraph); // A→B boost::add_edge(A, C, mygraph); // A→C boost::add_edge(C, D, mygraph); // C→D boost::add_edge(C, E, mygraph); // C→E // 深さ優先探索 DFSVisitor vis; boost::depth_first_search(mygraph, boost::visitor(vis).root_vertex(A)); // DOTファイルに出力 std::ofstream ofs("graph.dot"); write_graphviz(ofs, mygraph, [&](std::ostream& out, const Vertex v) { out << "[label=\"" << mygraph[v].name << "\"]"; } ); // dot.exe -Tpng graph.dot -o a.png return 0; }

・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; }