What is the canonical way in the max api for an external to get its own path?

Shakeeb Alireza's icon

Hi folks,

As I want the external to have the absolute path of its own 'Resources' folder, I have been able to get it via Apple Core Foundation API:

CFBundleRef bundle;
CFURLRef resources_url;
CFURLRef resources_abs_url;
CFStringRef resources_str;
const char* resources_path;

// Look for a bundle using its identifier
bundle = CFBundleGetBundleWithIdentifier(CFSTR("org.me.py"));
resources_url = CFBundleCopyResourcesDirectoryURL(bundle);
resources_abs_url = CFURLCopyAbsoluteURL(resources_url);
resources_str = CFURLCopyFileSystemPath(resources_abs_url, kCFURLPOSIXPathStyle);
resources_path = CFStringGetCStringPtr(resources_str, kCFStringEncodingUTF8);
python_home = Py_DecodeLocale(resources_path, NULL);

post("resources_path: %s", resources_path);
if (python_home == NULL) {
error("cannot set python_home");
}
Py_SetPythonHome(python_home);

The drawback here is that I have to hard-code the app-id or set it via a compile-time macro (which is ultimately doable).

Nonetheless, just wondering if there is a more direct or general max api method for an external to get its own path. I couldn't find anything definitive in the forums.

I did try the following without success:

void py_locate_path_from_string(t_py* x, char* s)
{
    char filename[MAX_PATH_CHARS];
    char pathname[MAX_PATH_CHARS];
    short path;
    t_fourcc type = FOUR_CHAR_CODE('MAXB');
    t_filehandle fh;
    t_max_err err;
   strncpy_zero(filename, s, MAX_PATH_CHARS);
    if (locatefile_extended(filename, &path, &type, &type, 1)) {
        // nozero: not found
        py_error(x, "can't find file %s", s);
        return;
    } else {
        pathname[0] = 0;
        err = path_toabsolutesystempath(path, filename, pathname);
        if (err != MAX_ERR_NONE) {
            py_error(x, "can't convert %s to absolutepath", s);
            return;
        }
        py_log(x, "full path is: %s", pathname);
    }
}

... but then again (I guessed the FOUR_CHAR_CODE was MAXB for .mxo since there is no reference to an .mxo in the max file formats.

Anyway, just curious...

S

Timothy Place's icon

Hi,

The uiref example project in the traditional SDK provides a demonstration of how to do this. The ext_main() function receives an moduleref parameter which is a CFBundleRef on the Mac or an HMODULE on Windows.

Cheers,
Tim

Shakeeb Alireza's icon

Appreciate the help, Tim. I will check it for sure.

Thanks

S

Shakeeb Alireza's icon

For future reference: there was a small typo in Timothy's helpful post which could be confusing: the uiref project mentioned above is actually called uires. It's in the ui folder of the max sdk.

Shakeeb Alireza's icon

Just in case it's useful for others, and to complete the thread to its solution, the following (not tested but something similar worked for me) should capture the above requirement (to get the path of an external for both OS X and win64):

#if defined(__APPLE__)
#include <CoreFoundation/CoreFoundation.h>
#include <libgen.h>
#endif

// globals
static const char* external_path[MAX_PATH_CHARS];



void ext_main(void* module_ref)
{
    //... normal ext_main code here

    // xx_class = c;

#if defined(__APPLE__)
    // get/set external path for macOS
    CFURLRef ext_url_ref = CFBundleCopyBundleURL(module_ref);
    CFStringRef mac_path = CFURLCopyFileSystemPath(
        ext_url_ref, kCFURLPOSIXPathStyle);
    external_path = CFStringGetCStringPtr(
        mac_path, CFStringGetSystemEncoding());
    CFRelease(ext_url_ref);
    CFRelease(mac_path);
    post("osx external path: %s", external_path);
#else
    // get/set external_path for win64
    GetModuleFileName(moduleRef, (LPCH)external_path, sizeof(external_path));
    post("win64 external path: %s", external_path);
#endif
}