Using CEF inside a Photoshop Plugin instead of basic HWND

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.

Using CEF inside a Photoshop Plugin instead of basic HWND

Postby abizeau » Thu Feb 15, 2018 7:54 pm

Hello,

I have an issue about integrating CEF inside a Photoshop Plugin. (Note that I'm on Windows 7, using latest Spotify CEF build = cef_binary_3.3282.1733.g9091548_windows64).

So lets start from the beginning, Im using Photoshop CS6 SDK, Im building a Photoshop Plugin for CS and CC (Note that Adobe created their own CEF integration in Photoshop CC, but not for CS).

In the SDK, you have the following code :

Code: Select all
Boolean DoUI(void)
{
   PlatformData* platform = (PlatformData*)(gFilterRecord->platformData);

   INT_PTR result = DialogBoxParam(GetDLLInstance(),
                     (LPSTR)"DISSOLVEDIALOG",
                     (HWND)platform->hwnd,
                     (DLGPROC)DissolveProc,
                     NULL);
   //used by the DissolveProc routine, don't let the error go further
   *gResult = noErr;
   return (result == kDOK);
}



Which is the code that the Plugin call when its about to render a UI, note that GetDLLInstance() is the function returning the instance of the Plugin inside Photoshop.exe.

So Im trying to replace the DialogBoxParam, by a call to a CEF, by using the following,

Code: Select all
Boolean DoUI(void)
{
    // Enable High-DPI support on Windows 7 or newer.
    CefEnableHighDPISupport();

    void* sandbox_info = NULL;

    // Provide CEF with command-line arguments.
    CefMainArgs main_args(GetDLLInstance()); 

    // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
    // that share the same executable. This function checks the command-line and,
    // if this is a sub-process, executes the appropriate logic.
    int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info);
    if ( exit_code >= 0 )
    {
        // The sub-process has completed so return here.
        return exit_code;
    }

    // Specify CEF global settings here.
    CefSettings settings;   
    settings.log_severity = LOGSEVERITY_VERBOSE;

    // SimpleApp implements application-level callbacks for the browser process.
    // It will create the first browser instance in OnContextInitialized() after
    // CEF has initialized.
    CefRefPtr<SimpleApp> app(new SimpleApp);

    // Initialize CEF.
    CefInitialize(main_args, settings, app.get(), sandbox_info);

    // Run the CEF message loop. This will block until CefQuitMessageLoop() is
    // called.
    CefRunMessageLoop();

    // Shut down CEF.
    CefShutdown();
   
    return m_result;
}


The windows seems to be generated with a title (cefsimple), but that it, nothing is render inside (Full white windows), despite the url is still pointing on http://www.google.com, I also tried to point on a local index.html in-case, but same thing.

And when looking to the console output (With log_severity = LOGSEVERITY_VERBOSE), I have the following errors :

Code: Select all
[0215/193116.650:VERBOSE1:node_controller.cc(172)] Initializing node 336CC32B5DD8104D.59E670AE16D6CAED
[0215/193116.700:VERBOSE1:pref_proxy_config_tracker_impl.cc(154)] 000000001993A5A0: set chrome proxy config service to 00000000199CEFD0
[0215/193116.703:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Google 'Aviator' log
[0215/193116.703:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Google 'Icarus' log
[0215/193116.703:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Google 'Pilot' log
[0215/193116.704:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Google 'Rocketeer' log
[0215/193116.704:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Google 'Skydiver' log
[0215/193116.704:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: DigiCert Log Server
[0215/193116.704:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: DigiCert Log Server 2
[0215/193116.705:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Symantec log
[0215/193116.705:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Symantec 'Vega' log
[0215/193116.705:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Symantec 'Sirius' log
[0215/193116.706:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: WoSign log
[0215/193116.706:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Venafi Gen2 CT log
[0215/193116.706:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: CNNIC CT log
[0215/193116.707:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: StartCom log
[0215/193116.707:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Comodo 'Sabre' CT log
[0215/193116.707:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Comodo 'Mammoth' CT log
[0215/193116.708:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Izenpe log
[0215/193116.708:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Venafi log
[0215/193116.708:VERBOSE1:multi_log_ct_verifier.cc(75)] Adding CT log: Certly.IO log
[0215/193116.709:VERBOSE1:network_connection.cc(30)] Updating NetworkConnection's Cached Data
[0215/193116.721:VERBOSE1:webrtc_internals.cc(109)] Could not get the download directory.
[0215/193116.723:VERBOSE1:node.cc(425)] Merging local ports A7442D2716584C92.377BC346C92224FA@336CC32B5DD8104D.59E670AE16D6CAED and FEAC31E0176631AF.7F704A013398132F@336CC32B5DD8104D.59E670AE16D6CAED
[0215/193116.740:VERBOSE1:file_util_win.cc(436)] CreateDirectory(GPUCache), directory already exists.
'Photoshop.exe' (Win32): Loaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'. Cannot find or open the PDB file.
'Photoshop.exe' (Win32): Unloaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'
[0215/193116.771:VERBOSE1:node.cc(435)] Observing lost connection from node 336CC32B5DD8104D.59E670AE16D6CAED to node 659EDC4E11620515.E73C896755BC5607
[0215/193116.772:VERBOSE1:browser_gpu_channel_host_factory.cc(147)] Failed to create channel on existing GPU process. Trying to restart GPU process.
'Photoshop.exe' (Win32): Loaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'. Cannot find or open the PDB file.
'Photoshop.exe' (Win32): Unloaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'
[0215/193116.843:VERBOSE1:node.cc(435)] Observing lost connection from node 336CC32B5DD8104D.59E670AE16D6CAED to node 43D1DA02D8B89775.34966F45C185AD46
[0215/193116.874:VERBOSE1:node.cc(435)] Observing lost connection from node 336CC32B5DD8104D.59E670AE16D6CAED to node 6639CC9A6EB0057C.91C5570B4AE0EF81
[0215/193116.875:VERBOSE1:browser_gpu_channel_host_factory.cc(147)] Failed to create channel on existing GPU process. Trying to restart GPU process.
'Photoshop.exe' (Win32): Loaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'. Cannot find or open the PDB file.
'Photoshop.exe' (Win32): Unloaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'
[0215/193116.973:VERBOSE1:node.cc(435)] Observing lost connection from node 336CC32B5DD8104D.59E670AE16D6CAED to node ADB9ECF33DB15621.E465F4BC6B17DAEE
[0215/193116.975:VERBOSE1:browser_gpu_channel_host_factory.cc(147)] Failed to create channel on existing GPU process. Trying to restart GPU process.
'Photoshop.exe' (Win32): Loaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'. Cannot find or open the PDB file.
'Photoshop.exe' (Win32): Unloaded 'C:\Program Files\Adobe\Adobe Photoshop CS4 (64 Bit)\Photoshop.exe'
[0215/193117.092:VERBOSE1:node.cc(435)] Observing lost connection from node 336CC32B5DD8104D.59E670AE16D6CAED to node 9D0092C04E20D31C.8C43A54970CBB588
[0215/193117.094:VERBOSE1:browser_gpu_channel_host_factory.cc(147)] Failed to create channel on existing GPU process. Trying to restart GPU process.
[0215/193117.099:ERROR:browser_gpu_channel_host_factory.cc(121)] Failed to launch GPU process.
[0215/193117.101:ERROR:gpu_process_transport_factory.cc(1019)] Lost UI shared context.
[0215/193117.106:VERBOSE1:histogram.cc(401)] Histogram: Compositing.Browser.LayersUpdateTime.0 has bad minimum: 0
[0215/193118.443:VERBOSE1:histogram.cc(401)] Histogram: Compositing.Browser.LayersUpdateTime.0 has bad minimum: 0
The thread 0x9738 has exited with code 0 (0x0).
The thread 0x9424 has exited with code 0 (0x0).
The thread 0x91a0 has exited with code 0 (0x0).
The thread 0x9290 has exited with code 0 (0x0).
The thread 0x9008 has exited with code 0 (0x0).
The thread 0x712c has exited with code 0 (0x0).
The thread 0x9708 has exited with code 0 (0x0).
The thread 0x19bc has exited with code 0 (0x0).
The thread 0xc4 has exited with code 0 (0x0).
[0215/193119.890:VERBOSE1:media_stream_manager.cc(486)] ~MediaStreamManager
The thread 0x95dc has exited with code 0 (0x0).
The thread 0x8978 has exited with code 0 (0x0).
First-chance exception at 0x000007FEFD94A06D (KernelBase.dll) in Photoshop.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out.
[0215/193119.896:VERBOSE1:statistics_recorder.cc(528)] Collections of all histograms


After that, there are a tons more a message about "Histogram:" and tons of text, I didn't copy since its too long and maybe irrelevant, and the end of that log finish with :

Code: Select all
[0215/193120.900:FATAL:broker_services.cc(134)] Check failed: false.


Without verbosity, I just see the 3 followings line about CEF:

Code: Select all
[0215/193117.099:ERROR:browser_gpu_channel_host_factory.cc(121)] Failed to launch GPU process.
[0215/193117.101:ERROR:gpu_process_transport_factory.cc(1019)] Lost UI shared context.
[0215/193120.900:FATAL:broker_services.cc(134)] Check failed: false.


I achieve to disable gpu using the OnBeforeCommandLineProcessing. This only removes the line about "Failed to launch GPU process.", the Lost UI and Check failed persist.

I don't know what I'm doing wrong ? Is there anything about the GetDllInstant() that I provided to CefMainArgs ? is Photoshop.exe process blocking CEF for an unknown reason ?

I understand that Photoshop.exe might be connected to the GPU, so CEF multi-process could not connect to it, but that not important for me. So I would like to know, what could I do or test a settings to get something working ?

PS: when closing the windows, a lot of exceptions are thrown by photoshop and CEF, I don't really mind right now, because it might due to the current error anyway.

Thanks
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby abizeau » Fri Feb 16, 2018 2:53 pm

Okay,

I tested few things, but none are working perfectly yet.

I tried to change the CefSettings to use single_process. This is super slow, but its achieved to render the http://www.google.com page. But when I'm closing the windows, there is a tons of errors and Photoshop is crashing.

Another of my test was to use the CefSettings browser_subprocess_path, and pointing on cefsimple.exe, this run the process just fine (and fast), the thing is similar, when I'm closing the windows and come back to main process, the CefRunMessageLoop stop and I reach the CefShutdown, but errors are happening after, but Photoshop is not crashing at least. But on the second launch of the application, nothing is working, Photoshop doesnt allow me to start the plugin (for an unknown reason...).

So, this project doesn't look impossible, but there is something wrong in what I do, or during my initialization.

Does anyone can help me or point me on a thread of this forum, I have been searching for two days without success.

Regards,
Alex
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby magreenblatt » Fri Feb 16, 2018 3:00 pm

CEF needs to be initialized and shutdown a single time in the process. You're not going to be able to do that from inside a plugin that gets loaded/unloaded multiple times. Is there a way to keep your plugin loaded for the life of the Photoshop process?
magreenblatt
Site Admin
 
Posts: 10184
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby abizeau » Fri Feb 16, 2018 3:46 pm

I dont know everything about how Photoshop works and call the plugin, but Photoshop do two things.

When Photoshop.exe is lauching, its loading all the plugin (including mine) by passing in the following method :

Code: Select all
BOOL APIENTRY DllMain(HANDLE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID /*lpReserved*/)
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACH:
            LoadThirdPartyLibraries();
            break;
        case DLL_THREAD_ATTACH:
            LoadThirdPartyLibraries();
            break;
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
            break;
    }

    dllInstance = static_cast<HINSTANCE>( hModule );

    return  true;
}


In this method, we introduce the switch case to load our 3rd Party lib using delay loads on dll. So normally, Photoshop.exe is just creating the dllInstance in there.

Until, you call the Plugin itself, nothing happen (as far as I know). Once you execute your plugin, its call this:
Code: Select all
DLLExport MACPASCAL void PluginMain(const int16 selector,
                                    FilterRecordPtr filterRecord,
                                    intptr_t * data,
                                    int16 * result)
{
// ... Some code in there to execute different Photoshop action (selection, call UI, call about, etc.)
// In code above, I talk about the DoUI() function, this is call in there.
}


Using the debugger, I noticed that Photoshop is calling the DllMain again, before calling the PluginMain function (dont know why, since dllInstance already exist at that point...)

So if I get what you are saying:

on DLL_PROCESS_ATTACH, I should Init CEF. (How?)
on DLL_PROCESS_DETACH, I should shutdown CEF. (How?)

And I could run my message loop only in the DoUI() ?

Im not sure to understand everything of what is happening in there. But as far as I can see, Im able to run CEF inside a Photoshop Plugin (single process or sub-process), but that not very efficient or bug free right now. Im sure there is a way to get it working, but Im not an expert with CEF, and that mostly why I started with the cefsimple right now, nothing fancy for the moment.
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby magreenblatt » Fri Feb 16, 2018 4:02 pm

DllMain is called by Windows when a DLL is loaded/unloaded. Is DLL_PROCESS_ATTACH/DLL_PROCESS_DETACH passed only a single time during the Photoshop process lifespan? If so, you can use those signals to initialize and shutdown CEF. Is there a callback from Photoshop before a DLL is unloaded or is DLL_PROCESS_DETACH your only shutdown signal?
magreenblatt
Site Admin
 
Posts: 10184
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby abizeau » Fri Feb 16, 2018 4:29 pm

No to all those questions.

Photoshop will search for all plugin and see if he can load it or not (missing dependencies). If the plugin is able to load, I assumed that Photoshop will mark this plugin as "loadable".
And next, when you have to call your plugin, the DLL_PROCESS_ATTACH/DLL_PROCESS_DETACH is called at each time the plugin need to be called.

But Photoshop keep an handle pointer for each plugin to have a global memory, so on a plugin call, you can know what was the "preferences, or others" from the previously called plugin. So I could create a preference "isCEFInit" to know if its inited or not. The thing is, I dont know when Photoshop.exe is closing, so I cant CefShutdown properly.

But this doesnt resolve my other (main) problem, where the rendering still on a white page, except if I run it as a single_process. But maybe this init/close/reinit is another issue, single_process or not...

I would like to know what Adobe are doing with their Cloud Creative... They have a process called Adobe CEF Helper.exe, this thing might do some black magic to be able to keep CEF init/shutdown as long as the machine is running or something like that, I dont know. But how this "Adobe CEF Helper.exe" is talking to "Photoshop.exe" I dont know...
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby magreenblatt » Fri Feb 16, 2018 4:45 pm

abizeau wrote:when you have to call your plugin, the DLL_PROCESS_ATTACH/DLL_PROCESS_DETACH is called at each time the plugin need to be called.

That means your plugin DLL is being loaded/unloaded multiple times. You therefore need to use a separate DLL for the CEF integration. Load this separate DLL using LoadLibrary from your plugin and it should stay loaded for the remainder of the process lifespan (across multiple loads/unloads of your plugin DLL) until something calls FreeLibrary or the process terminates. Not having a shutdown signal before DLL_PROCESS_DETACH might still be problematic because you won't be able to shutdown CEF properly.
magreenblatt
Site Admin
 
Posts: 10184
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby abizeau » Fri Feb 16, 2018 5:12 pm

So correct me if I am wrong,

I should create a second C++ PluginCEFRunner.dll (or application .exe, to run as a subprocess ?) that will be LoadLibrary() by MyPlugin.dll when its will be called by Photoshop Process.

Next, MyPlugin.dll will be load at the moment I want to execute the plugin code, and since the PluginCEFRunner.dll is loaded already (the CefInit is already done, right ?). So I just need to do CefExecuteProcess on the UI I need to do during the MyPlugin.dll execution ?

Afterward, I will close the CEF window and then Photoshop process will unload MyPlugin.dll since its execution is done, but PluginCEFRunner.dll is still loaded ?

And if this is the thing to do, What should be in PluginCEFRunner.dll ? What code ? Something like the Main application entry-point
from https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-separate-sub-process-executable ?
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby magreenblatt » Fri Feb 16, 2018 5:29 pm

PluginCEFRunner.dll will initialize and shutdown CEF. For example, it exports DoCefInitialize and DoCefCreateWindow functions.

The 1st time MyPlugin.dll is loaded by Photoshop it will call LoadLibrary("PluginCEFRunner.dll") and execute the DoCefInitialize function. Use the persistent storage that Photoshop offers to track that you've already performed the initialization. The implementation of DoCefInitialize calls CefInitialize with CefSettings.multi_threaded_message_loop = true and browser_subprocess_path pointing to a separate exe for subprocesses. That way you don't need to run the CEF message loop yourself.

The 2nd+ times that MyPlugin.dll is loaded it will call LoadLibrary("PluginCEFRunner.dll") (to get a handle for the already loaded DLL) but not execute the DoCefInitialize function.

You can then call DoCefCreateWindow at any time after DoCefInitialize has been called.
magreenblatt
Site Admin
 
Posts: 10184
Joined: Fri May 29, 2009 6:57 pm

Re: Using CEF inside a Photoshop Plugin instead of basic HWN

Postby abizeau » Fri Feb 16, 2018 5:49 pm

Okay, I did understand then!

I will give a try to that. This could solved the problem of the 1st and 2nd execution, with a single CefInit and a single CefShutdown.

But the problem still, when Im on first execution, my UI is empty (white windows), there nothing in it, I have the "Lost UI shared context" error and nothing happen in the log. But If I use the CefSetting -> Single process, the UI is render (but really slow). What could be the problem ? Photoshop block something ? Because when Im using cefsimple.exe on my machine I have no issue and nothing, so there no problem with my CEF build.

Do you have any idea why this is happening ? That my main concern, because the dll thing, I think I can fix it now.
abizeau
Techie
 
Posts: 34
Joined: Thu Feb 15, 2018 7:07 pm

Next

Return to Support Forum

Who is online

Users browsing this forum: Google [Bot] and 33 guests