Library structure

ZModeler load plug-in models as a regular .dll libraries with LoadLibrary API function call. The only limitation is that ZModeler scans for modules which match *.zm? template or, literally, those that have file extension starting with "zm". ZModeler does not perform additional operations with the plug-in. Plug-in module is responsible for registering properly in ZModeler and the most important step is a classes collection exporting.

Consider that plug-in module is presented in ZModeler SDK with core::libs::ILibrary interface. Instances of these interfaces are held by core::IRepository interface (implemented in ZModeler2.exe) and each library is wrapped into helper interface core::libs::ILibraryHolder (implemented in ZModeler2.exe). Plug-in module have to create an instance of core::libs::ILibrary interface and register it in repository. Since repository "knows" which plugin it loads at this moment, it will wrap the library you register into properly-configured ILibraryHolder interface.

core::libs::ILibrary interface must be able provide names and to create instances of all available classes in your plug-in (of cause only those, that publically export). ZModeler SDK provides a helper class core::libs::CDefaultLibrary with the base implmentation of these routines. Your task is to create an overloaded version of this class and (optionally) implement some functions in this class related to library loading and initalization routines. ZModeler provides a set of macros for implementing an overloaded version of this class and registering classes that plug-in will export.

ZM_LIBRARY_DEFAULT_DECLARE
  ZM_LIBRARY_NO_COPYRIGHT_INFO
  ZM_LIBRARY_COLLECTION_BEGIN
    ZM_LIBRARY_REG
    ZM_LIBRARY_REG2
  ZM_LIBRARY_COLLECTION_END
  ZM_LIBRARY_REQURES (optional)
  ZM_LIBRARY_INITIALIZATION (optional)
  ZM_LIBRARY_STARTUP (optional)
  ZM_LIBRARY_DYNAMIC_UNLOAD (optional)
ZM_LIBRARY_DEFAULT_END

Once helper class is implemented, you have to instantate and register it in DllMain function. ZModeler SDK provides a set of macros for this purpose too.

ZM_DLLMAIN_DEFAULT_BEGIN
  ZM_LIBRARY_DEFAULT
  ZM_LIBRARY_DESCRIPTION
  ZM_LIBRARY_REGISTER
  ZM_DLLMAIN_UNLOAD_BEGIN (optional)
  ZM_DLLMAIN_UNLOAD_END (optional)
ZM_DLLMAIN_DEFAULT_END

Example

Below is an example of implementation with these macros. You can click on macros to see how they expand when compiling. In general, you don't have to bother, just follow common template and these macro will do the rest for you. As you can see in original source code, all plug-ins reside on these macros.


ZM_LIBRARY_DEFAULT_DECLARE()
class CLibrary : public core::libs::CDefaultLibrary
{
  public:
    INTERFACE_DECLAREMAP(CLibrary)
    CLibrary() : core::libs::CDefaultLibrary()
      { m_Handle = 0; }
  public:
  ZM_LIBRARY_NO_COPYRIGHT_INFO()
    ZRESULT getCopyrightInfo(core::ICopyrightInfo** ppInfo)
    {
      return ZRESULT_NOT_IMPLEMENTED;
    }
  ZM_LIBRARY_COLLECTION_BEGIN()
    tClassMap m_mapClasses;
    void InitToolsMap()
    {
    ZM_LIBRARY_REG(services::CMaterialsService,          ZM_CLASS_SERVICE)
    ZM_LIBRARY_REG(rend::CMaterial,                      ZM_CLASS_SHARED)
    ZM_LIBRARY_REG(core::tools::CMaterialsEditorDialog,  ZM_CLASS_TOOL)
      m_mapClasses["services::CMaterialsService"] = tLibClassInfo(
             services::CMaterialsService::CreateInstance, ZM_CLASS_SERVICE);
      m_mapClasses["rend::CMaterial"] = tLibClassInfo(
             rend::CMaterial::CreateInstance, ZM_CLASS_SHARED);
      m_mapClasses["core::tools::CMaterialsEditorDialog"] = tLibClassInfo(
             core::tools::CMaterialsEditorDialog::CreateInstance, ZM_CLASS_TOOL);
  ZM_LIBRARY_COLLECTION_END()
    }
    ZRESULT getClassesList(tClassInfoSeq& classList)
    {
      if (0==m_mapClasses.size())
        InitToolsMap();
      tClassMap::iterator it = m_mapClasses.begin();
      classList.clear();
      int pos = 0;
      while (it != m_mapClasses.end())
      {
        classList[pos].m_strClassName = it->first;
        classList[pos].m_info = it->second;
        it++;
        pos++;
      }
      return ZRESULT_OK;
    }
    ZRESULT createClass(ZString name,
                        ZUnknown** ppUnk)
    {
      if (NULL==ppUnk)
        return ZRESULT_INVALID_ARG;
      (*ppUnk) = NULL;
      if (0==m_mapClasses.size())
        InitToolsMap();
      tClassMap::iterator it = m_mapClasses.find(name);
      if (it == m_mapClasses.end())
        return ZRESULT_FALSE;
      return it->second.m_creator((void**)ppUnk);
    }
    ZRESULT getDescription(ZStringSeq& strDesc)
    {
      strDesc = m_strModuleDesc;
      return ZRESULT_OK;
    }
    ZRESULT getHandle(tModuleHandle& handle)
    {
      handle = m_Handle;
      return (0==handle) ? ZRESULT_FALSE : ZRESULT_OK;
    }
    ZRESULT setHandle(tModuleHandle  handle)
    {
      m_Handle = handle;
      return ZRESULT_OK;
    }
  ZM_LIBRARY_REQUIRES(ZM_LIBREQUIRES_STARTUP)
    ZRESULT requires(DWORD& dwFlags)
    {
      dwFlags = ZM_LIBREQUIRES_STARTUP;
      return ZRESULT_OK;
    }
  ZM_LIBRARY_STARTUP(startupLibrary)
    ZRESULT startup()
    {
      extern ZRESULT startupLibrary();
      AfxSetResourceHandle(m_Handle);
      ZRESULT zr = startupLibrary();
      extern DLL_LINKAGE HINSTANCE g_hAppHandle;
      AfxSetResourceHandle(g_hAppHandle);
      return zr;
    }
ZM_LIBRARY_DEFAULT_END()
    ZStringSeq    m_strModuleDesc;
    tModuleHandle m_Handle;
};
INTERFACE_BEGINMAP(CLibrary)
  INTERFACE_ENTRY(core::libs::ILibrary)
INTERFACE_ENDMAP()

ZM_DLLMAIN_DEFAULT_BEGIN()
HINSTANCE   g_hDLLHandle;
BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,
    DWORD fdwReason,
    LPVOID lpReserved )
{
  g_hDLLHandle = hinstDLL;
  switch( fdwReason )
  {
    case DLL_PROCESS_ATTACH:
  ZM_LIBRARY_DEFAULT(hinstDLL)
    ZPtr<CLibrary> pCLib;
    CLibrary::CreateInstance(&pCLib);
    if (!pCLib)
      return FALSE;
    pCLib->setHandle(hinstDLL);
  ZM_LIBRARY_DESCRIPTION(_T("ZModeler 2 Materials service library"))
    pCLib->m_strModuleDesc.add(_T("ZModeler 2 Materials service library"));
  ZM_LIBRARY_REGISTER()
    if (g_pZModeler)
    {
      ZPtr<core::IRepository> pRepos;
      g_pZModeler->getRepository(&pRepos);
      if (pRepos)
        return ZRESULT_OK == pRepos->addLibrary(pCLib);
    }
ZM_DLLMAIN_DEFAULT_END()
    break;
  }
  return TRUE;
}

ZRESULT startupLibrary()
{
  //
  // perform library startup stuff here: register event dispathcing etc.
  //
  return ZRESULT_OK;
}

Resource handle helpers

Your plug-in might need an access to it's resource handle or should be able to switch current resource handle to Dll's handle for loading resources properly. There are several macros or helpers that does this trick.

ZM_NEED_DLL_HANDLE This macro declares a variable which will be initialized with a pointer to Dll's HINSTANCE.
ZM_NEED_APP_HANDLE This macro declares a variable which will be initialized with a pointer to ZModeler's EXE HINSTANCE.
ZM_AUTO_HANDLE_SWITCH This macro is placed inside your code when you need to switch current resource handle to Dll's resource handgle. When this macro/variable runs out of scope, it resotres previouse resource handle. For example, you can place this macro before loading menu or toolbar from resource.
See Also:
References overview