Communicate With Main Application Thread

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

Communicate With Main Application Thread

Postby KKlouzal » Sat Aug 27, 2022 3:30 pm

I need to call a function in my main applications thread from a javascript function.
Javascript->Renderer->Browser->Main Application

I've been able to pass the message all the way down to the browser thread but not sure how to get out of the browser and over to my main (non cef) application thread.

Expose function to javascript:
Code: Select all
class MyCefApp : public CefApp, public CefRenderProcessHandler
{
public:

   bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
   {
      printf("[CEF] CefAPP:ProcessMessage %s\n", message->GetName().ToString().c_str());
      return false;
   }

   CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler()
   {
      return this;
   }

   void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
   {
      CefRefPtr<CefV8Value> object = context->GetGlobal();
      CefRefPtr<CefV8Handler> handler = new MyV8Handler();
      object->SetValue("register", CefV8Value::CreateFunction("register", handler), V8_PROPERTY_ATTRIBUTE_NONE);
   }

   void OnWebKitInitialized()
   {
      printf("WEBKIT\n");

      std::string extensionCode =
         "var test;"
         "if (!test)"
         "test = {};"
         "(function() {"
         "test.myfunc = function(RemoteAddr, RemotePort) {"
         "native function myfunc(RemoteAddr, RemotePort);"
         "return myfunc(RemoteAddr, RemotePort);"
         "};"
         "})();";
      
      CefRefPtr<CefV8Handler> handler = new MyV8Handler();
      CefRegisterExtension("v8/test", extensionCode, handler);
   }

   IMPLEMENT_REFCOUNTING(MyCefApp);
};


Handle javascript function execution in render thread:
Code: Select all
class MyV8Handler : public CefV8Handler {
public:

   bool Execute(const CefString& name,   CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
   {
      printf("[CEF] V8Handler:Execute %S\n", name.c_str());

      if (name == "myfunc")
      {
         if (arguments.size() == 2)
         {
            std::string RemoteIP = arguments[0]->GetStringValue().ToString();
            unsigned int RemotePort = arguments[1]->GetUIntValue();
            printf("MYFUNC %s %u\n", RemoteIP.c_str(), RemotePort);

            CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("My_Message");
            CefRefPtr<CefListValue> args = msg->GetArgumentList();
            args->SetString(0, arguments[0]->GetStringValue());
            args->SetInt(1, arguments[1]->GetIntValue());
            
            CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
            context->GetBrowser()->GetMainFrame()->SendProcessMessage(PID_BROWSER, msg);
            return true;
         }
      }

      // Function does not exist.
      return false;
   }

   // Provide the reference counting implementation for this class.
   IMPLEMENT_REFCOUNTING(MyV8Handler);
};


handle ipc message in browser thread:
Code: Select all
class BrowserClient : public CefClient
{
public:
   BrowserClient(RenderHandler* renderHandler)
      : m_renderHandler(renderHandler)
   {}

   virtual CefRefPtr<CefRenderHandler> GetRenderHandler()
   {
      return m_renderHandler;
   }

   bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefProcessId process, CefRefPtr<CefProcessMessage> message)
   {
      printf("[CEF] BrowserClient:ProcessMessage %s\n", message->GetName().ToString().c_str());
      if (message->GetName() == "My_Message")
      {
         std::string RemoteIP = message->GetArgumentList()->GetString(0).ToString();
         unsigned int RemotePort = message->GetArgumentList()->GetInt(1);
         printf("My_Message %s %u\n", RemoteIP.c_str(), RemotePort);
         WorldEngine::NetCode::ConnectToServer(RemoteIP.c_str(), RemotePort);
         return true;
      }
      return false;
   }

   CefRefPtr<CefRenderHandler> m_renderHandler;

   IMPLEMENT_REFCOUNTING(BrowserClient);
};


What's the best way to kick this over to my main (non cef) application thread from here?
WorldEngine::NetCode::ConnectToServer(RemoteIP.c_str(), RemotePort); returns an access violation because I'm trying to call a winsock function inside a thread that hasn't had winsock initialized in, so that lead me to realize I haven't quite gotten back down to my applications thread yet..
KKlouzal
Newbie
 
Posts: 9
Joined: Fri Nov 08, 2019 5:30 am

Re: Communicate With Main Application Thread

Postby magreenblatt » Sat Aug 27, 2022 5:21 pm

How are you running the main application thread? Does it support some form of message queue?
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Communicate With Main Application Thread

Postby KKlouzal » Sat Aug 27, 2022 6:17 pm

I suppose I should have also posted how I initialize CEF:
Code: Select all
      void Initialize()
      {
         CEFAPP = new MyCefApp();
         CefMainArgs args(::GetModuleHandle(NULL));

         int result = CefExecuteProcess(args, CEFAPP, nullptr);
         if (result >= 0)
         {
            printf("CEF Process Ended; Exit (%i)\n", result);
            //return;
         }

         CefSettings settings;
         settings.windowless_rendering_enabled = 1;
         settings.multi_threaded_message_loop = 0;
         settings.no_sandbox = 1;
         
         //wchar_t szPath[MAX_PATH];
         //GetModuleFileNameW(NULL, szPath, MAX_PATH);
         //auto P = std::filesystem::path{ szPath }.parent_path();
         //printf("PATH: %s\n", P.generic_string().c_str());

         //CefString(&settings.resources_dir_path) = P.generic_string() + "/Resources";

         bool result2 = CefInitialize(args, settings, nullptr, nullptr);
         if (!result2)
         {
            printf("CEF Initialize Error\n");
         }

         renderHandler = new RenderHandler();

         CefBrowserSettings browserSettings;
         browserSettings.windowless_frame_rate = 30;

         CefWindowInfo window_info;
         window_info.parent_window = glfwGetWin32Window(WorldEngine::VulkanDriver::_Window);
         window_info.SetAsWindowless(glfwGetWin32Window(WorldEngine::VulkanDriver::_Window));

         browserClient = new BrowserClient(renderHandler);

         browser = CefBrowserHost::CreateBrowserSync(window_info, browserClient.get(), "about:blank", browserSettings, nullptr, nullptr);
         browser->GetMainFrame()->LoadURL("file:///./html/main.html");
         //browser->GetHost()->SendKeyEvent(..);
      }


Offscreen rendering, rendering the onPaint pixel buffer with Vulkan. The window is created using GLFW, I can get access to the HWND used to initialize my vulkan render window. GLFW takes care of the underlying windows message pump and doesn't provide a way to hook into it for custom messages.. I have a loop running in my main application that executes (currently over 1500 times a second) so there is a place to perform logic in the main (non cef) application thread.
KKlouzal
Newbie
 
Posts: 9
Joined: Fri Nov 08, 2019 5:30 am

Re: Communicate With Main Application Thread

Postby KKlouzal » Sat Aug 27, 2022 9:53 pm

So, the code posted above, works and does exactly what I needed it to do.
The error was in my function call (was trying to access a pointer to a class member before that pointer was initialized).
Dumb mistake on my part.

Nonetheless, the code is documented here for those who come after me.
KKlouzal
Newbie
 
Posts: 9
Joined: Fri Nov 08, 2019 5:30 am


Return to Support Forum

Who is online

Users browsing this forum: No registered users and 114 guests