Визуальное программирование и MFC

       

Класс сервера


Пока имеется только оболочка автоматизированного сервера, а в программе для DLL-модуля еще нет самого объекта. Мастер AppWizard создал лишь единственный класс MFC - CSvrDllApp, определив его как производный от класса CWinApp библиотеки MFC. Согласно принятым правилам, в любом EXE- или DLL-модуле, создаваемом на базе MFC, должен быть предусмотрен класс, производный от CWinApp. В нем должен содержаться измененный вариант CWinApp::InitInstance - виртуальной функции, активизируемой автоматически при первом вызове механизмом MFC данного EXE- или DLL- модуля. (В случае EXE-модулей MFC функци CWinApp::InitInstance вызывается из процедуры WinMain; а если это DLL-модули - из DllMain.)

Созданный мастером AppWizard вариант InitInstance содержит всего несколько строк:

BOOL CSvrDllApp::InitInstance() { COleObjectFactory::RegisterAll(); return TRUE; }

COleObjectFactory - это особый класс MFC, содержащий фабрику класса OLE (OLE class factory); любая из фабрик класса MFC-сервера является экземпляром этого исходного класса. При фактическом создании каждой фабрики класса COleObjectFactory записывает его адрес в свой внутренний связанный список, так что всегда может получить доступ к любому или сразу ко всем фабрикам класса сервера.

Любая фабрика класса запускается функцией COleObjectFactory::RegisterAll, которая и регистрирует его как способного к созданию определенного типа объектов. Будучи статической функцией (то есть доступной глобально и не требующей для своего вызова обращения через некоторый экземпляр класса), RegisterAll просматривает связанный список всех фабрик классов, чтобы зарегистрировать каждую из них. Как только они начнут работать, к ним можно обращаться для создания обслуживаемых ими экземпляров OLE объектов. А поскольку InitInstance вызывается из функции DllMain внутреннего сервера (при первой загрузке этого DLL модуля), все его фабрики классов с самого начала готовы производить экземпляры предоставляемых этим сервером объектов.

Так как при работе с мастером AppWizard, был избран вариант с автоматизацией, появились еще и следующие три функции: DllGetClassObject, DllCanUnloadNow и DllRegisterServer. Все они относятся к типу STDAPI, т.
е. подчиняются стандартному соглашению о вызове функций Windows API, не входят в состав ни одного из классов Cи++ и могут вызываться из любой программы Windows (как любая другая функция Windows API).



Первые две функции обеспечивают взаимодействие внутреннего сервера с библиотекой функций OLE Component Object Model
. Например, обратившись к функции CoCreateInstance из OLE API, COM-клиент (или контейнер) может создать экземпляр предоставляемого сервером объекта. В свою очередь, эта функция обратится к CoGetClassObject, чтобы получить указатель на интерфейс IUnknown фабрики класса требуемого объекта, и затем, используя имеющийся в этом интерфейсе указатель, вызовет IClassFactory::CreateInstance. Эта функция запустит фабрику класса, чтобы создать экземпляр запрашиваемого объекта.



DllGetClassObject - это стандартная функция API, которую должен содержать любой внутренний сервер, чтобы обеспечить работу функции CoGetClassObject: если сервер организован в виде DLL модуля, то CoGetClassObject будет вызывать DllGetClassObject
. Мастер AppWizard формирует следующий фрагмент программы для этой функции с реализацией вызова внутренней функции MFC - AfxDllGetClassObject:

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return AfxDllGetClassObject(rclsid, riid, ppv); }

Функции AfxDllGetClassObject просматривает имеющийся на сервере связанный список фабрик объектов (предоставляемый COleObjectFactory) в поиске экземпляра, который позволит получить нужный тип объекта, и передает в вызывающую программу указатель на его интерфейс IUnknown.

Аналогично обстоят дела с функцией DllCanUnloadNow. Время от времени OLE клиент может вызывать функцию OLE API - CoFreeUnusedLibraries, чтобы высвободить ресурсы и выгрузить из памяти неиспользуемые больше DLL модули. Внутри CoFreeUnusedLibraries организуется вызов функции DllCanUnloadNow, осуществляемый для каждого из имеющихся внутренних серверов; именно поэтому мастер AppWizard добавил экземпляр и этой функции в нашем примере:



STDAPI DllCanUnloadNow(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return AfxDllCanUnloadNow(); }

DllCanUnloadNow обращается к внутренней функции MFC - AfxDllCanUnloadNow. Первым делом из нее вызывается другая внутренняя функция - AfxOleCanExitApp. Сначала она быстро проверяет, больше ли нуля общее количество объектов, имеющихся у этой прикладной программы или DLL модуля MFC. Если активных объектов нет, в принципе работа данного модуля может быть завершена. В этом случае функция AfxDllCanUnloadNow просматривает содержащийся в COleObjectFactory связанный список, чтобы отыскать хотя бы одну задействованную фабрику класса (учитывая также фабрики в DLL-модулях, загруженных из текущего модуля). Если таковые отсутствуют, функция сообщает, что данный DLL модуль можно выгрузить из памяти. Таким образом, использование функции DllCanUnloadNow служит гарантией того, что внутренний сервер MFC будет выгружен клиентом из памяти, как только он перестанет использовать его объекты или фабрики классов.



Если же внутренний сервер не содержит функции DllCanUnloadNow и не экспортирует ее извне, тогда его DLL модуль будет оставаться в памяти до тех пор, пока клиент не вызовет функцию CoUninitialize; именно ее использует OLE клиент (или контейнер), чтобы отказаться от OLE библиотеки после закрытия рабочих окон программы и завершения основного цикла обработки сообщений. В случае когда клиент использует несколько серверов, те из них, которые не содержат функцию DllCanUnloadNow, будут оставаться в памяти еще достаточно долго с момента последнего к ним обращения, возможно завладев при этом значительной долей памяти и системных ресурсов. Функция DllCanUnloadNow используется для того, чтобы выгрузить из памяти внутренний сервер сразу после окончания работы с ним, высвободив тем самым занятые им память и ресурсы.

Теперь подробно рассмотрим третью введенную мастером AppWizard функцию - DllRegisterServer.


Содержание раздела