Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS General Discussion » How to manage load/save of customized state Collapse All
Subject Author Date
Istvan Mehlhoffer Mar 1, 2005 - 6:57 AM

Hi,

I’m currently writing a plugin dll to a Prof-UIS application. This plugin adds custom toolbars and menus to the main app using ProfAuto. These user-items are automatically archived by the main application (CustomizeStateSave and CustomizeStateLoad), that results the following issue:
The main app always loads the custom items at the start, even if my plugin is already removed. Is there any way to delegate the load/save actions to the plugin to avoid the appear of toolbar and menu items without command handler on the UI? Any other suggestions and solutions are welcome. Thanks.

Hunor

Svetlozar Asenov Feb 20, 2006 - 3:43 PM

We have almost the same problem. Our project maintains a plugin system based on COM interfaces and we need to keep information which plugin owns a command. The second solution seems very fine and suitable for us, but there is something that i don’t understand. If i add a command in my plugin through the IExtAutoCommandCollection->Add() method, exactly which command profile will be used? Seems that in your solution it is supposed to be one command profile for each plugin. In the Add() method there is a call to stat_GetCmdProfile(m_hWndFrame). Which profile it returns?

Technical Support Feb 21, 2006 - 6:00 AM

Any ProfAuto-based application is first of all a customizable application. ProfAuto just provides a set of COM wrapper objects in the parallel COM world. Your application initializes the single command profile for the main frame window. This is typically performed at the beginning of the CMainFrame::OnCreate() method where the g_CmdManager->ProfileSetup() method is called. So, your application is based only on this single command profile. This profile can be found automatically in your code, in Prof-UIS code or in ProfAuto code. The command manager knows HWND of the main frame window attached to the command profile. All other windows are children or popup descendants of the main frame window. So, the command manager can find HWND of the main frame window and return its command profile. Basically the CExtCmdManager::ProfileNameFromWnd() method does this work. Having added a command in ProfAutom you simply add a new command item to the command profile of the main frame window. The second way just allows you to define your own command item classes which may keep you per-command custom data. It is not essential to define you command description classes. You can use the command name to specify which plugin has allocated the command.

Technical Support Mar 1, 2005 - 11:02 AM

First of all, thank you for an interesting question. We can offer you two different solutions. In both cases, your main application should know all plugin names, command identifiers, the names of toolbars and command categories added by each plugin. So, after your app starts up, you should check the loaded state, find out which plugins have been removed and clean everything relating to each removed plugin.

First solution is very simple but not universal. Develop a small COM object with methods like SetupCommand(), SetupCommandCategory(), and SetupToolbar(). These methods should be similar to ProfAuto APIs but require a plugin name as one of the parameters. This allows your application to store information about all commands, command categories and toolbars added by plugins somewhere, i.e. the application will know which plugin and what are added. This is enough to clean up information that corresponds to removed plugins when your application is starting up. The main disadvantage is that plugins have access to both new COM object’s API and ProfAuto API and still able to produce command leaks.

Second solution is a bit more complicated but has no disadvantages. Your plugins should use ProfAuto API "as is". You should initialize all plugins step by step and let the command manager know which plugin is currently being initialized. This allows the command manager to remember all command items registered by any plugin. To do this, we need our own command profile and command item classes derived from ProfAuto types:

class CMyCmdItem : public CAutoCmdItem
{
public:
    CString m_strMyPluginName;
    CMyCmdItem( UINT nCmdID  = IDC_STATIC  )
        : CAutoCmdItem( nCmdID )
    {
    }
    CMyCmdItem( const CExtCmdItem & other )
        : CAutoCmdItem( other )
    {
    }
};
 
class CMyCmdProfile : public CAutoCmdProfile
{
public:
    CString m_strInitializedPluginName;
protected:
    CExtCmdItem * OnCreateCmdItem( const CExtCmdItem & _cmd )
    {
        CMyCmdItem * pCmdItem = new CAutoCmdItem( _cmd );
        if( ! m_strInitializedPluginName.IsEmpty() )
            m_strMyPluginName = m_strInitializedPluginName;
        return pCmdItem;
    }
public:
    virtual bool SerializeState( CArchive & ar )
    {
          // this method needs to be optimized
        if( ! CAutoCmdProfile::SerializeState( ar ) )
        {
            ASSERT( FALSE );
            return false;
        }
        if( ar.IsStoring() )
        {
            DWORD dwCount = m_cmds.GetCount();
            ar << dwCount;
            for( POSITION pos = m_cmds.GetStartPosition(); pos != NULL; )
            {
                UINT nCmdID = 0;
                CExtCmdItem * pCmdItem = NULL;
                m_cmds.GetNextAssoc( pos, nCmdID, pCmdItem );
                ASSERT( CExtCmdManager::IsCommand(nCmdID) );
                ASSERT( pCmdItem != NULL && pCmdItem->m_nCmdID == nCmdID );
                ar << nCmdID;
                ar << ((CMyCmdItem*)pCmdItem)->m_strMyPluginName;
            }
        } // if( ar.IsStoring() )
        else
        { 
            DWORD dwCount;
            ar >> dwCount;
            for( DWORD i = 0; i < dwCount; i++ )
            {
                UINT nCmdID;
                ar >> nCmdID;
                ar >> (((CMyCmdItem*)CmdGetPtr(nCmdID))->m_strMyPluginName);
            }
        } // else from if( ar.IsStoring() )
        return true;
    }
};

Use the following code to setup your command profile and command item types:
CMyCmdProfile * pProfile = new CMyCmdProfile;
    pProfile->m_sName = pApp->m_pszProfileName; // or any other profile name
    VERIFY(
        g_CmdManager->ProfileSetup(
            pApp->m_pszProfileName,
            GetSafeHwnd(), 
            pProfile
            )
        );
You can the same code lines in the CMainFrame::OnCreate() method of the <span class=newgreen>ActiveScripts</code> application. Now, all command items in the command profile of your main frame window are based on the CMyCmdItem and they are able to remember which plugin have created them. You should initialize plugins using the following rule:
CMyCmdProfile * pProfile = (CMyCmdProfile *)
    g_CmdManager->ProfileGetPtr(
        pApp->m_pszProfileName; // or any other profile name
        );
    ASSERT( pProfile != NULL );
    pProfile->m_strInitializedPluginName =
        _T("plugin name to be initialized right now");
 
    // TO DO: insert plugin initialization code here
    // and let it do anything with ProfAuto
    pProfile->m_strInitializedPluginName.Empty();
After that, your application knows exactly which command is initialized by which plugin. Your code can traverse the pProfile->m_cmds map and analyze any property of any command. Use ProfAuto when you need to remove commands, command categories or toolbars.

How to clean up the information associated with a removed plugin? First of all, traverse pProfile->m_cmds map and populate some temporary list with command identifiers added by the plugin. After that, find out which of these detected commands correspond to identifiers of CExtToolControlBar windows in your main frame window. The CFrameWnd::GetControlBar() method allows you to get the control bar by its command identifier. So, you are able to detect all toolbars created by a plugin and remove them. Then you can safely remove all commands you stored in the temporary list. Unfortunately, it is not possible to detect which command categories have been added by some subsystem, but this is not a problem. After removing all toolbars and commands of all uninstalled plugins, you may simply walk through all command categories and remove each category which have no child command tree nodes. This solution should work correctly because you have no any reason to keep empty categories. In fact, an empty category in your application is a category entry leak generated by some plugin and nothing else.

Please do not hesitate to contact us if you have any questions.

Istvan Mehlhoffer Mar 3, 2005 - 3:30 AM

Hi,


Thanks for your fast and detailed response. The second solution seems to be appropriate and acceptable for me.


Have a nice day!


Hunor