自分で導入したvcpkg installをコマンドプロンプトから実行して以下のエラーに遭遇
error: Could not locate a manifest (vcpkg.json) above the current working directory.
This vcpkg distribution does not have a classic mode instance.
これは自分で導入したvcpkgを実行したつもりで、実はVisual Studioが管理しているvcpkgが実行された場合に起こるらしい。具体的には
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat
などでVisual Studioの環境を設定した場合、且つ、環境変数などで自前のvcpkgのパスを通していた場合に起こる可能性がある。
つまり、vcvars64.batが環境を整備しているときに、Visual Studio以外で導入したvcpkgの存在が確認できた時、これを有効にしてしまうとVisual Studioのvcpkgを使っているつもりなのに異なるvcpkgを操作してしまったということが起こり得てしまいまずいので、強制的にVS用のvcpkgに上書きされる。
この上書きはdoskeyというコマンドで行われるらしい。以下の確認用コマンドで、もし
の結果が
などであれば、vcpkgのパスが上書きされているので、以下を実行してこの設定を消す。
描画した頂点の番号や座標値などを表示したいとき用。
#include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <Eigen/Dense> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkActor.h> #include <vtkPolyDataMapper.h> #include <vtkPolyData.h> #include <vtkPoints.h> #include <vtkCellArray.h> #include <vtkUnsignedCharArray.h> #include <vtkProperty.h> #include <vtkPointData.h> //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
// 座標群生製
std::vector<Eigen::Vector3d> make_cloud(int count) { std::vector<Eigen::Vector3d> cloud; for (size_t i = 0; i < count; i++) { double x = (double)rand() / (double)RAND_MAX; double y = (double)rand() / (double)RAND_MAX; double z = (double)rand() / (double)RAND_MAX; cloud.push_back(Eigen::Vector3d(x, y, z)); } return cloud; }
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// #include <vtkStringArray.h> #include <vtkLabeledDataMapper.h> #include <vtkActor2D.h> #include <vtkTextProperty.h>
// ラベルを表すPolyDataを作成 vtkSmartPointer<vtkPolyData> toLabel(const std::vector<Eigen::Vector3d>& data) { ////////////////////////// // auto points = vtkSmartPointer<vtkPoints>::New(); // インデックス文字列を作成 auto labels = vtkSmartPointer<vtkStringArray>::New(); labels->SetName("labels"); for (vtkIdType i = 0; i < data.size(); ++i) { std::string text = std::to_string(i); points->InsertNextPoint(data[i].data()); // 位置 labels->InsertNextValue(text.c_str()); // ラベル } auto polyData = vtkSmartPointer<vtkPolyData>::New(); polyData->SetPoints(points); polyData->GetPointData()->AddArray(labels); return polyData; }
// ラベル表示用のActorを作成 vtkSmartPointer<vtkActor2D> toLabelActor( vtkSmartPointer<vtkPolyData> poly, int fontSize = 20 ) { double color[3] = { 1.0, 1.0, 1.0 }; auto labelMapper = vtkSmartPointer<vtkLabeledDataMapper>::New(); labelMapper->SetInputData(poly); labelMapper->SetLabelModeToLabelFieldData(); labelMapper->SetFieldDataName("labels"); auto textProperty = labelMapper->GetLabelTextProperty(); textProperty->SetFontSize(fontSize); // フォントサイズ textProperty->SetColor(color); // 色 //textProperty->SetShadow(1); auto labelActor = vtkSmartPointer<vtkActor2D>::New(); labelActor->SetMapper(labelMapper); return labelActor; }
//////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// int main(int /*argc*/, char** /*argv*/) { auto renderer = vtkSmartPointer<vtkRenderer>::New(); auto cloud = make_cloud(10);// 座標群を作成 ////////////////////////////////////// // 座標にデータ番号を表示 auto textpoly = toLabel(cloud); vtkSmartPointer<vtkActor2D> textactor = toLabelActor(textpoly); renderer->AddActor(textactor);// ラベル表示 ////////////////////////////////////// renderer->ResetCamera(); auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); interactor->Start(); //イベントループへ入る return 0; }
VTK の MFC 対応 vtkMFCWindow は、文字セットがUnicodeの場合、Debugモードで実行中に以下で実行時エラーが発生する。
m_vtkMFCWindow = new vtkMFCWindow(this);
VTKのCMakeLists.txtを編集してこれを回避できるらしい。
VTKのソースコードをダウンロードしたら、以下のファイルを開く
VTK-9.5.1\GUISupport\MFC\CMakeLists.txt
CMakeLists.txt内、ファイル中央付近に以下のように追加をする。
# C runtime lib linkage and MFC lib linkage *MUST* match.
# If linking to C runtime static lib, link to MFC static lib.
# If linking to C runtime dll, link to MFC dll.
if (vtk_mfc_static)
set(CMAKE_MFC_FLAG 1)
else ()
set(CMAKE_MFC_FLAG 2)
endif ()
set(classes
vtkMFCWindow
)
vtk_module_add_module(VTK::GUISupportMFC
CLASSES ${classes})
vtk_add_test_mangling(VTK::GUISupportMFC)
### 追加 ###
vtk_module_definitions(VTK::GUISupportMFC PRIVATE _UNICODE UNICODE)
#############
if (vtk_mfc_static)
vtk_module_link(VTK::GUISupportMFC
PRIVATE
"debug;nafxcwd;optimized;nafxcw"
"debug;LIBCMTD;optimized;LIBCMT"
Uxtheme
windowscodecs)
else ()
vtk_module_definitions(VTK::GUISupportMFC
PRIVATE
_AFXDLL)
endif ()
別に詰まったわけではないし、公式からダウンロードしたもので事足りるのだけれど、一応成功事例を残しておく。
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);