最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

sdk - CSDK <<>> UXP Plugin internal Communication of the Adobe Photoshop Hybrid Plugin - Stack Overf

programmeradmin3浏览0评论

I'm making a UXP Hybrid plugin for Adobe Photoshop. I understood a synerge of Photoshop DOM API and UXP plugin well, but now my team needs to access the Photoshop brush in detail so that we can access to its properties and modify those. I double-checked that most of the advanced features in Photoshop API weren't very helpful, so a UXP plugin is there to let a plugin to communicate with Python, etc. while CSDK side of a hybrid plugin serves the internal access of Photoshop's powerful features.

After figuring out everything, I was looking through this official document: / which tells the messaging between CSDK <<>> UXP Plugin. However, our plugin is not an automation plugin nor a filter plugin, so there were no information about how can I get/access to the plugin reference for gPlugInRef so that Photoshop won't crash and send the message to the right UXP plugin, here which is itself. (Also the main entry point of this document was totally different with my case but that's another long story of struggling.)

Based on the SDK files that I have a look into, I was using this one for my final attempt. This didn't work even I am using the name for the plugin.

SPErr ref = gPluginSuite->GetNamedPlugin("MyPluginName", &gPlugInRef);
            if (ref != kSPNoError) {
                writeLog("Cannot find the plugin.");
                break;
            }

I thought this is the only and easiest way to get a plugin reference to run this code internally to its UXP self, but it didn't work out. the logfile created "Cannot find the plugin." So, obviously, this code wasn't working.

const char* UXP_MANIFEST_ID = "MyPluginID";
            SPErr sending = sUxpProcs->SendUXPMessage(gPlugInRef, UXP_MANIFEST_ID, desc);

While I checked all suites are imported well, onto the things which is defined in module.cpp together.

// For general use of CSDK API. (without any interaction or messages)
static const SPBasicSuite* gBasicSuite = nullptr;
static const PSColorSpaceSuite2* gColorSpaceSuite = nullptr;

// My globals
SPPluginRef gPlugInRef = nullptr;

static const SPPluginsSuite* gPluginSuite = nullptr;

PsUXPSuite1* sUxpProcs = nullptr; // * Acquired this inside of the function.
PSActionDescriptorProcs* sDescriptorProcs = nullptr; // * Acquired this inside of the function.

If anyone wants to look at the whole module.cpp which is the key file to compile .uxpaddon file for a UXP hybrid plugin, here's mine (Not everything here is a target of this question. extern "C" UXP_EXTERN_API_STDCALL(SPErr) PSDLLMain is the main trouble):

#include <exception>
#include <stdexcept>
#include <string>
#include <fstream>

#include "utilities/UxpAddon.h"
#include "utilities/UxpTask.h"
#include "utilities/UxpValue.h"

#include "SPBasic.h"
#include "SPPlugs.h" // Required for SPPlugins suite
#include "SPProps.h" // Required for accessing plugin properties

#include "PIUXPSuite.h" // For messaging
#include "PIActions.h" // For PIActionDescriptor
#include "PIGeneral.h" // For PlugInInfor Struct

#include "PIColorSpaceSuite.h"

// For general use of CSDK API. (without any interaction or messages)
static const SPBasicSuite* gBasicSuite = nullptr;
static const PSColorSpaceSuite2* gColorSpaceSuite = nullptr;

// My globals
SPPluginRef gPlugInRef = nullptr;

static const SPPluginsSuite* gPluginSuite = nullptr;

PsUXPSuite1* sUxpProcs = nullptr; // * Acquired this inside of the function.
PSActionDescriptorProcs* sDescriptorProcs = nullptr; // * Acquired this inside of the function.

// To receive a message from a UXP plugin
void UXPMessageHandler(PIActionDescriptor descriptor) {
    // do something
}

void writeLog(const std::string& message) {
    std::ofstream logFile;
    logFile.open("FileLocation", std::ios_base::app);  // Open in append mode.
    // (Added) If std::ios_base::app is not inserted, the logging system wouldn't work properly.
    if (logFile.is_open()) {
        logFile << message << std::endl;  // Write message to file
        logFile.close();  // Close the file after writing
    } else {
        printf("Unable to open log file.\n");
    }
}

extern "C" UXP_EXTERN_API_STDCALL(SPErr) PSDLLMain(const char* selector, SPBasicSuite* spBasic, SPPluginRef gPlugInRef, void*) {
    // In automation plugin, void* could be handled as a 'message' but here it doesn't.
    try {
        do {
            writeLog("***** Hello world. *****"); // Confirmed. This function is naturally called when the plugin is launched.
            // Cache the basic suite so we can use at any time
            gBasicSuite = spBasic;
            
            writeLog("Start acquiring kPSUXPSuite.");
            SPErr uxp = gBasicSuite->AcquireSuite(kPSUXPSuite, kPSUXPSuiteVersion1, (const void**)&sUxpProcs);
            SPErr action = gBasicSuite->AcquireSuite(kPSActionDescriptorSuite, kPSActionListSuiteVersion, (const void**)&sDescriptorProcs);
            if (uxp != kSPNoError || action != kSPNoError) {
                writeLog("Cannot acquire kPSUXPSuite.");
                writeLog("Cannot acquire kPSActionDescriptorSuite.");
                break;
            }
            writeLog("Done acquiring kPSUXPSuite and kPSActionDescriptorSuite.");
            
            SPErr spErr = gBasicSuite->AcquireSuite(kPSColorSpaceSuite, kPSColorSpaceSuiteVersion, (const void**)(&gColorSpaceSuite));
            if (spErr != kSPNoError) {
                writeLog("Cannot acquire kPSColorSpaceSuite.");
                break;
            }
            if (gColorSpaceSuite == nullptr) {
                writeLog("gColorSpaceSuite is NULL.");
                break;
            }
            Color8 colorArray[1] = {11, 30, 201, 0}; // Blue RGB Colour
            spErr = gColorSpaceSuite->Convert8(plugIncolorServicesRGBSpace, plugIncolorServicesCMYKSpace, colorArray, 1);
            if (spErr != kSPNoError) {
                writeLog("Failed to convert colour space.");
                break;
            }
            
            // c.f. There's a thing called PIGeneral.h - PlugInInfo, but it's totally useless in this situation.
            writeLog("Start acquiring kPSPluginsSuite.");
            SPErr plugin = gBasicSuite->AcquireSuite(kSPPluginsSuite, kSPPluginsSuiteVersion6, (const void**)(&gPluginSuite));
            if (plugin != kSPNoError) {
                writeLog("Cannot acquire kSPPluginsSuite.");
                break;
            }
            if (gPluginSuite == nullptr) {
                writeLog("gPluginSuite is NULL.");
                break;
            }
            
            SPErr ref = gPluginSuite->GetNamedPlugin("PluginName", &gPlugInRef);
            if (ref != kSPNoError) {
                writeLog("Cannot find the plugin.");
                break;
            }
            
            PIActionDescriptor desc;
            sDescriptorProcs->Make(&desc);
            sDescriptorProcs->PutString(desc, 'helo', "Hello World!");
            sDescriptorProcs->PutFloat(desc, 'fltp', 0.952);
            
            // FINAL Step: Send out a message to specified plugin!
            const char* UXP_MANIFEST_ID = "PluginID";
            SPErr sending = sUxpProcs->SendUXPMessage(gPlugInRef, UXP_MANIFEST_ID, desc);
            if (sending != kSPNoError) {
                writeLog("Failed to send a message.");
                // break;
            }
            else {
                
            }
            
            writeLog("Successfully changed the colour mode and sent out the message.");
            
        } while (false);
        
        return kSPNoError;
    }
    catch(...) {
        writeLog("An exception occurred in PSDLLMain.");
        return -1;
    }
}

namespace {
/*
 * The function is used to return 'hello world' message.
 * Javascript equivalent function() { return 'hello world'; }; in C++
 * No entry point should throw an exception. The implementation must use try/catch
 * and return nullptr if an exception was caught.
 * This method is invoked on the JavaScript thread.
 */

addon_value MyFunction(addon_env env, addon_callback_info info) {
    try {
        addon_value message = nullptr;
        Check(UxpAddonApis.uxp_addon_create_string_utf8(env, "hello world", 11, &message));
        return message;
    } catch (...) {
        return CreateErrorFromException(env);
    }
}

/*
 * This function will echo the provided argument after converting to and from a
 * standard value type.
 * No entry point should throw an exception. The implementation must use try/catch
 * and return nullptr if an exception was caught.
 * This method is invoked on the JavaScript thread.
 */
addon_value MyEcho(addon_env env, addon_callback_info info) {
    try {
        // Allocate space for the first argument
        addon_value arg1;
        size_t argc = 1;
        Check(UxpAddonApis.uxp_addon_get_cb_info(env, info, &argc, &arg1, nullptr, nullptr));

        // Convert the first argument to a value that can be retained past the
        // return from this function. This is needed if you want to pass arguments
        // to an asynchronous/deferred task handler
        Value stdValue(env, arg1);

        return stdValue.Convert(env);
    } catch (...) {
        return CreateErrorFromException(env);
    }
}

/*
 * This function will echo the provided argument after converting to and from a
 * standard value type.
 * The implementation illustrates how to create an asynchronous task, where the
 * initial call returns a promise/deferred which is then later resolved.
 * No entry point should throw an exception. The implementation must use try/catch
 * and return nullptr if an exception was caught.
 * This method is invoked on the JavaScript thread.
 */
addon_value MyAsyncEcho(addon_env env, addon_callback_info info) {
    try {
        // Allocate space for the first argument
        addon_value arg1;
        size_t argc = 1;
        Check(UxpAddonApis.uxp_addon_get_cb_info(env, info, &argc, &arg1, nullptr, nullptr));

        // Convert the first argument to a value that can be retained past the
        // return from this function. This is needed if you want to pass arguments
        // to an asynchronous/deferred task hand;er
        Value stdValue(env, arg1);

        // Create a heap copy using move. This prevents a deep copy of the data & we can pass that
        // ptr to another context
        std::shared_ptr<Value> valuePtr(std::make_shared<Value>(std::move(stdValue)));

        auto scriptThreadHandler = [](Task& task, addon_env env, addon_deferred deferred) {
            try {
                HandlerScope scope(env);
                bool isError = false;
                const Value& result = task.GetResult(isError);
                addon_value resultValue = result.Convert(env);

                if (isError)
                    Check(UxpAddonApis.uxp_addon_reject_deferred(env, deferred, resultValue));
                else
                    Check(UxpAddonApis.uxp_addon_resolve_deferred(env, deferred, resultValue));
            } catch (...) {
            }
        };

        auto mainThreadHandler = [valuePtr, scriptThreadHandler](Task& task) {
            try {
                task.SetResult(std::move(*(valuePtr.get())), false);
                task.ScheduleOnScriptingThread(scriptThreadHandler);
            } catch (...) {
            }
        };

        auto task = Task::Create();
        return task->ScheduleOnMainThread(env, mainThreadHandler);
    } catch (...) {
        return CreateErrorFromException(env);
    }
}

/* Method invoked when the addon module is being requested by JavaScript
 * This method is invoked on the JavaScript thread.
 */
addon_value Init(addon_env env, addon_value exports, const addon_apis& addonAPIs) {
    addon_status status = addon_ok;
    addon_value fn = nullptr;

    // MyFunction
    {
        status = addonAPIs.uxp_addon_create_function(env, NULL, 0, MyFunction, NULL, &fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to wrap native function");
        }

        status = addonAPIs.uxp_addon_set_named_property(env, exports, "my_function", fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to populate exports");
        }
    }

    // MyEcho
    {
        status = addonAPIs.uxp_addon_create_function(env, NULL, 0, MyEcho, NULL, &fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to wrap native function");
        }

        status = addonAPIs.uxp_addon_set_named_property(env, exports, "my_echo", fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to populate exports");
        }
    }

    // MyAsyncEcho
    {
        status = addonAPIs.uxp_addon_create_function(env, NULL, 0, MyAsyncEcho, NULL, &fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to wrap native function");
        }

        status = addonAPIs.uxp_addon_set_named_property(env, exports, "my_echo_async", fn);
        if (status != addon_ok) {
            addonAPIs.uxp_addon_throw_error(env, NULL, "Unable to populate exports");
        }
    }
    
    return exports;
}

}  // namespace

/*
 * Register initialization routine
 * Invoked by UXP during uxpaddon load.
 */
UXP_ADDON_INIT(Init)

void terminate(addon_env env) {
    try {
    } catch (...) {
    }
}

/* Register addon termination routine
 * Invoked by UXP during uxpaddon un-load.
 */
UXP_ADDON_TERMINATE(terminate)

What should I do to retrieve the plugin (itself) reference so that I can make internal messaging for a UXP Hybrid Plugin?

发布评论

评论列表(0)

  1. 暂无评论