2011-09-07 2 views
5

Как заставить Excel (2007) VBA выпускать ссылки на объект COM-сервера?Как выпустить inprocess COM-объект сервера из Excel VBA

Я написал COM-сервер inprocess (Single instance DLL) в Visual Foxpro 9 SP2, который был создан из кода Excel VBA на моей машине разработки. Кажется, что в Excel есть ссылка на COM-объект/dll, хотя я его установил = Nothing. Это мешает мне перестроить DLL из-за сообщения «Запрет доступа к файлам TestCOM.dll» до тех пор, пока я не выйду из Excel, который является болью каждый раз, когда я хочу внести изменения и протестировать его.

Я вареный код вниз к очень простой схеме испытаний: Проект VFP9 (TestCOM) имеет только один .prg файл со следующим содержимым

DEFINE CLASS TestClass As Session OLEPUBLIC 

ENDDEFINE 

УВА код выглядит следующим образом:

Sub Test() 

    Set objTest = CreateObject("TestCOM.TestClass") 
    Set objTest = Nothing 

End Sub 

Я попытался удалить ссылку на библиотеку COM-сервера в проекте VBA, но это не имеет никакого значения. Я пробовал с и без DIMing переменных объекта, и это не имеет никакого значения. Я попытался создать новый проект VFP DLL, но проблема не устранена.

Если я создаю приложение/dll VFP как INPROCESS/DLL и запускаю код VBA, я получаю эту проблему, но если я создам его как OUTOFPROCESS/EXE и запустим код VBA, я НЕ получаю эту проблему.

Я нашел очень похожую проблему в COM Object Cleanup, за исключением того, что мой COM-сервер написан на Visual Foxpro 9 с пакетом обновления 2 (SP2), тогда как это относится к C#, и OP не объяснил подробно, обойти его; если это возможно.

+0

Для любых других читают эту тему я впоследствии понял, что если проект будет построен как вне процесса (EXE) COM-сервер, то вы можете только восстановить проект без выхода из Excel, если у вас нет ссылки на библиотеку типов в VBA/Excel. – Caltor

ответ

6

Процедура, используемая для создания экземпляра вашего COM-класса из кода в вашей DLL, заключается в том, что Excel вызывает слой библиотеки COM, чтобы найти вашу реализацию, используя либо ProgID, либо ClassID. Когда у вас есть сервер inproc, это означает, что он находит путь к вашей DLL и использует LoadLibrary для загрузки его в ваш клиентский процесс, а затем создает фабрику классов и методы вызовов в DLL. Таким образом, конечный результат заключается в том, что Excel вызывает LoadLibrary в вашей DLL, и это блокирует файл до тех пор, пока Excel не вызовет FreeLibrary в дескрипторе.

Использование интерфейсов COM вы не контролируете это. Вы вызываете CoCreateInstance() (или из VBA вы создаете объект с помощью New или CreateObject, который вызывает этот API Win32). Реализация этого обрабатывает LoadLibrary и все остальное, пока вы не получите указатель интерфейса для работы. Некоторые приложения будут периодически вызывать CoFreeUnusedLibraries(), чтобы попытаться выпустить загруженные COM-библиотеки, которые в настоящее время не используются. Стандартная реализация класса по умолчанию поддерживает счетчик созданных объектов, который может использоваться для определения того, используется ли DLL или нет, но это не всегда надежно, поскольку авторы COM-классов могут не подчиняться правилам. Выход из Excel, очевидно, освобождает блокировку файла.

Когда вы создаете свой COM-класс как внепроцессный сервер, он живет в отдельном исполняемом файле или DLL, жизнь которого управляется по-разному. Excel больше не держит блокировку в DLL и освобождение экземпляра COM вполне может позволить процессу хостинга выйти.

Вы можете конвертировать DLL для использования в качестве локального сервера (вне процесса), организовав его размещение в DllHost. Если вы используете утилиту OleView и найдите свои классы ProgId, вы можете включить хостинг в суррогатном процессе (dllhost). Прошло некоторое время с тех пор, как я это сделал, но в Интернете должна быть информация об использовании суррогатного хостинга. Очевидно, что размещение объекта COM вне процесса делает все медленнее и создает потенциал для различных проблем, связанных с сортировкой.Если вы поддерживаете совместимые с oleautomation интерфейсы, это должно быть хорошо.

+0

Благодарим вас за отличный подробный технический ответ. Итак, вкратце вы в основном говорите, что с интерфейсами COM я нахожусь во власти клиента, в данном случае Excel, и я никак не могу заставить выпуск DLL? Я должен был упомянуть в своем вопросе, что эта проблема не возникает, когда COM-сервер вызывается из другой копии VFP, поэтому ошибка, похоже, связана с клиентом, а не с сервером. Я думаю, что в качестве обходного пути я создам этот проект как сервер Out of Process (EXE) во время разработки, а затем переключу его на In Process (DLL) для окончательного тестирования и выпуска. – Caltor

+2

Требуется один дополнительный ингредиент. COM-сервер должен вернуть S_OK из своей точки входа DllCanUnloadNow(). Непростой тест в среде VBA + Foxpro. –

1

Добавление короткого ответа ....

Высвобождение DLL является узловатой проблемой и один знакомые разработчикам в процессе COM компоненты для использования в Excel.

Есть два условия, которые должны быть удовлетворены

1) Не допускается использование раннего связывания ссылок библиотеки (Tools-> Reference), используйте вместо позднего связывания. Ссылка на ранние ссылки инструментов проведет блокировку.

2) Позвоните CoFreeUnusedLibraries, чтобы выгрузить COM серверов, у которых больше нет клиентов.

Из вашего образца кода вы уже устарели, но, пожалуйста, проверьте свои рекомендации. В то время как точка 2) упоминается в payyhoyts answer, код не указан.

Вот копия и pasteable декларация

Private Declare Sub CoFreeUnusedLibraries Lib "ole32.dll"()