Тема: Программная загрузка *.mns

Как можно программно выполнять загрузку панели инструментов из *.mns файла при загрузке ARX?
И аналогично, убирать, при выгрузке?

Re: Программная загрузка *.mns

t_errno MenuLoader::loadMenu()
{
    if ( acDocManager->documentCount() >= 1 ) {
        CString MenuFileName = "MNUNAME.MNU", GroupName = "GROUP_NAME_FROM_MNU_FILE_HERE";
        //----------------------------------------------------------
        // Other (throw cleary com) implementation
        AutoCAD::IAcadApplication        * pAcad;
        AutoCAD::IAcadMenuBar            * pMenuBar;
        AutoCAD::IAcadMenuGroups        * pMenuGroups;
        AutoCAD::IAcadMenuGroup            * pMenuGroup;
        AutoCAD::IAcadPopupMenus        * pPopUpMenus;
        AutoCAD::IAcadPopupMenu            * pPopUpMenu;
        HRESULT hr                = NOERROR;
        LPUNKNOWN pUnk            = NULL;
        LPDISPATCH pAcadDisp    = NULL;
        pAcadDisp = acedGetAcadWinApp()->GetIDispatch(TRUE);
        // Use IUnknown to get the AutoCAD application object.
        hr = pAcadDisp->QueryInterface(AutoCAD::IID_IAcadApplication,(void**)&pAcad);
        pAcadDisp->Release();
        if (FAILED(hr))    return ErCode::eFAIL;
        // Make sure AutoCAD is visible.
        pAcad->put_Visible(true);
        // Get the menu bar and menu groups collections
        pAcad->get_MenuBar(&pMenuBar);
        pAcad->get_MenuGroups(&pMenuGroups);
        pAcad->Release();
        // Determine how many menus are current on the menu bar.
        long numberOfMenus;
        pMenuBar->get_Count(&numberOfMenus);
        pMenuBar->Release();
        // Determine is menu already loaded
        bool bIsMenuLoaded = false;
        CString mnuPath = pGlobVars->getMenuPath();
        for(long i = 0; i < numberOfMenus; i++)
        {
            _variant_t index = i;
            pMenuGroups->Item(index, &pMenuGroup);
            BSTR menu_name;
            pMenuGroup->get_Name(&menu_name);
            if(menu_name == GroupName)
            {
                bIsMenuLoaded = true;
                break;
            }
            pMenuGroup->Release();
        }
        // If menu is not loaded then load it.
        if(!bIsMenuLoaded)
        {
            _bstr_t path = mnuPath + MenuFileName;
            pMenuGroups->Load(path, _variant_t(false), &pMenuGroup);
            pMenuGroup->get_Menus(&pPopUpMenus);
            pMenuGroup->Release();
            long numOfPops = 0;
            pPopUpMenus->get_Count(&numOfPops);       
            for ( int i = 0; i < numOfPops; i++ )
            {
                _variant_t popUpNum;
                popUpNum = (long)i;
                pPopUpMenus->Item(popUpNum, &pPopUpMenu);
                pPopUpMenu->InsertInMenuBar(_variant_t(numberOfMenus+(long)i-2L));
                pPopUpMenu->Release();
            }
            pPopUpMenus->Release();
        }
        pMenuGroups->Release();
        //----------------------------------------------------------
    }
    return ErCode::sOK;
}

Re: Программная загрузка *.mns

t_errno MenuLoader::unloadMenu()
{
        CString GroupName = "GROUP_NAME_FROM_MNU_FILE_HERE";
        AutoCAD::IAcadApplication        * pAcad;
        AutoCAD::IAcadMenuBar            * pMenuBar;
        AutoCAD::IAcadMenuGroups        * pMenuGroups;
        AutoCAD::IAcadMenuGroup            * pMenuGroup;
        HRESULT hr                = NOERROR;
        LPUNKNOWN pUnk            = NULL;
        LPDISPATCH pAcadDisp    = NULL;
        pAcadDisp = acedGetAcadWinApp()->GetIDispatch(TRUE);
        // Use IUnknown to get the AutoCAD application object.
        hr = pAcadDisp->QueryInterface(AutoCAD::IID_IAcadApplication,(void**)&pAcad);
        pAcadDisp->Release();
        if (FAILED(hr))    return ErCode::eFAIL;
        // Get the menu bar and menu groups collections
        hr = pAcad->get_MenuBar(&pMenuBar);
        if (FAILED(hr))    return ErCode::eFAIL;
        hr = pAcad->get_MenuGroups(&pMenuGroups);
        if (FAILED(hr))    return ErCode::eFAIL;
        pAcad->Release();
        // Determine how many menus are current on the menu bar.
        long numberOfMenus;
        pMenuBar->get_Count(&numberOfMenus);
        pMenuBar->Release();
        for(long i = 0; i < numberOfMenus; i++)
        {
            _variant_t index = i;
            pMenuGroups->Item(index, &pMenuGroup);
            BSTR menu_name;
            pMenuGroup->get_Name(&menu_name);
            if(GroupName == menu_name)
            {
                pMenuGroup->Unload();
                break;
            }
            pMenuGroup->Release();
        }
        pMenuGroups->Release();
    return ErCode::sOK;
}

Re: Программная загрузка *.mns

Выгружаю меню по событию beginQuit или отслеживаю когда закрывается последний документ. т.е. если приходит событие в реакторе, что документ закрывается и кол-во документов равно 1, то зову unloadMenu.
Еще тонкий момент - в 2002-м событие beginQuit приходит до закрытия всех документов, а в 2004(5) после. (Про что я сильно ругался где-то ранее). Поэтому и приходится отслеживать либо закрытие последнего документа, либо сообытие о выходе - что раньше произойдет.

Re: Программная загрузка *.mns

Но после закрытия последнего документа может наступить открытие еще нескольких. А менюшки уже не будет :( И что делать бедным пользователям?

Re: Программная загрузка *.mns

> ROMA
Загрузить его (программно) в реакторе AcApDocManagerReactor::documentActivated(), если меню еще не было загружено. Так что пользователям ничего делать будет не нужно! :)

Re: Программная загрузка *.mns

Кстати, начиная с AutoCAD 2000 есть недокументированная функция, экспортируемая из acad.exe и присутствующая в acad.lib:

bool __cdecl AcadIsQuitting(void);

С ее помощью в момент закрытия последнего документа можно определить закрывается ли при этом и сам AutoCAD.

Re: Программная загрузка *.mns

При использовании этой функции в 2002 CAD все работает как и ожидалось, но в 2005 эта функция всегда возвращает false.
В чем может быть дела?
Вызов функции помещен в методы documentToBeDestroyed
documentDestroyed

Re: Программная загрузка *.mns

> ROMA
На то она и недокументированная... wink
Есть еще один вариант:

static bool isMenuUnloaded = false;
// Здесь свое имя меню
static char sMenuName[]="MyMenuName";
// -----------------------------------------------------------------------------
void QuitingReactor::documentLockModeChanged(
AcApDocument * param2, AcAp::DocLockMode  myPreviousMode,
AcAp::DocLockMode  myCurrentMode, AcAp::DocLockMode  currentMode, const char * pGlobalCmdName)
{
  char sRealGlobalName[256];
  if (*pGlobalCmdName == '#') strcpy(sRealGlobalName,pGlobalCmdName+1);
              else            strcpy(sRealGlobalName,pGlobalCmdName);
  if (!isMenuUnloaded) {
    if (!stricmp(sRealGlobalName,"QUIT") ||
        !stricmp(sRealGlobalName,"EXIT")) {
     veto(); // Запрещаем выполнение команды
     char cmd[1024];
     sprintf(cmd,"(command \"_.menuunload\" \"%s\") ",sMenuName);
     isMenuUnloaded = true;
     acDocManagerPtr()->sendStringToExecute(curDoc(),cmd,true,false,false);
     sprintf(cmd,"_.%s ",sRealGlobalName);
     acDocManagerPtr()->sendStringToExecute(curDoc(),cmd,true,false,false);
    }
  }
  AcApDocManagerReactor::documentLockModeChanged (param2, myPreviousMode, myCurrentMode, currentMode, pGlobalCmdName) ;
}

Увы, но это тоже не идеальный вариант... Если был хотя бы один несохраненный чертеж, то пользователь может отказаться от завершения работы AutoCAD, а меню уже выгружено. Так что или нужно принудительно сохранять все чертежи или наоборот сбрасывать у них DBMOD и они не будут сохранены...

Re: Программная загрузка *.mns

А есть еще решения?
Как все таки убирать за собой? меню и toolbar?

Re: Программная загрузка *.mns

Помогите разобраться
Нужно подключить меню при загрузке AutoCAD
Попробовал подключить меню, как советовал KonstantinM в начале темы.
Не получается получить интерфейсы COM
Выводится сообщение “'AutoCAD' : is not a class or namespace name” или “IID_IAcadApplication' : is not a member of 'AutoCAD”
Подскажите, что делаю неправильно
Делаю как указано в справочной системе по разделу “COM, importing ActiveX interfaces”, (правда с некоторыми отклонениями - там не говорится, что нужно создавать проект с поддержкой COM. И поскольку Wizard сам генерирует часть кода, некоторые части я пропускал, поскольку код уже присутствует).
1. Создаю новый проект с поддержкой MFC и COM
2.Добавляю в проект библиотеки
acad.lib
acdb16.lib
rxapi.lib
acedapi.lib
3. Создаю новый CPP файл, в котором создаю функцию

void AddMenu(void) {
    AutoCAD::IAcadApplication *pAcad;
    AutoCAD::IAcadMenuBar *pMenuBar;
    AutoCAD::IAcadMenuGroups *pMenuGroups;
    AutoCAD::IAcadMenuGroup *pMenuGroup;
    AutoCAD::IAcadPopupMenus *pPopUpMenus;
    AutoCAD::IAcadPopupMenu *pPopUpMenu;
    AutoCAD::IAcadPopupMenuItem *pPopUpMenuItem;
    HRESULT hr = NOERROR;
    CLSID clsid;
    LPUNKNOWN pUnk = NULL;
    LPDISPATCH pAcadDisp = NULL;
    hr = ::CLSIDFromProgID(L"AutoCAD.Application", &clsid);
    if (FAILED(hr))
        return;
    if(::GetActiveObject(clsid, NULL, &pUnk) != S_OK)
        return;
    hr = pUnk->QueryInterface(IID_IDispatch, (LPVOID*) &pAcadDisp);
    pUnk->Release();
    if (FAILED(hr))
        return;
    hr = pAcadDisp->QueryInterface([b]AutoCAD::IID_IAcadApplication [/b],  (void**)&pAcad);
    pAcadDisp->Release();
    if (FAILED(hr))
        return;
    pAcad->put_Visible(true);
}; 

Компилятор выводит ошибку
error C2653: 'AutoCAD' : is not a class or namespace name
Если в файле StdAfx.h, в строке, созданной генератором
#import "acax16enu.tlb" raw_interfaces_only  no_namespace
удалить часть строки - “no_namespace” – выводится сообщение
error C2039: 'IID_IAcadApplication' : is not a member of 'AutoCAD'
Что нужно сделать, чтобы видеть идентификатор 'IID_IAcadApplication' ?

Re: Программная загрузка *.mns

> Ura
Или вместо:

#import "acax16enu.tlb" raw_interfaces_only no_namespace

использовать:

#import "acax16enu.tlb"

Или убрать пространство имен (namespace) AutoCAD у всех переменных (т.е. вместо AutoCAD::IID_IAcadApplication писать IID_IAcadApplication и т.д.)

Re: Программная загрузка *.mns

Спасибо Александр, за помощь. Получилось.
Компилируется нормально.
Правда не все понятно.
Попробовал оставить только

#import "acax16enu.tlb"

Выводится сообщение об ошибке
error C2039: 'IID_IAcadApplication' : is not a member of 'AutoCAD'
Если оставить

#import "acax16enu.tlb" raw_interfaces_only no_namespace 

и убрать пространство имен AutoCAD
Выводится сообщение об ошибке
error C2371: 'AcadSecurityParamsType' : redefinition; different basic types’
и так по всем функциям “acax16enu.tlb”
Попробовал заменить на строку из примера samples\com\AsdkPlainComSamp_dg\ AsdkPlainComDocSamp.cpp (из которого и был взят фрагмент функции)

#import "acax16enu.tlb" no_implementation raw_interfaces_only named_guids 

Все заработало. Компилируется без ошибок
Если изменить “named_guids” на любое другое значение, например на “named”, начинается все по новой.
‘warning C4185: ignoring unknown #import attribute 'named'
Возможно потому и сработало, что весь код взят из одного примера, в котором забито конкретное пространство имен? Возможно этим можно объяснить, почему Твои рекомендации не подошли на 100%. Но ссылки на named_guids нигде нет, по крайней мере я не нашел. Странно все это.
И тут еще вопрос, на что влияет “named_guids”? Если я создам еще одну программу с тем же именем, не будут ли они конфликтовать? Или это действует только в пределах одной программы на этапе компиляции?
Хотелось бы узнать, что эти ключи означают, и где можно определить пространство имен. Нигде в справке описания не нашел.

Re: Программная загрузка *.mns

> Ura
Прочитай в описание директивы #import в по MS VS Help. Там описано что такое named_guids.

Re: Программная загрузка *.mns

> Александр Ривилис
Спасибо.
Теперь все понятно.