Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Доступ к серверу с помощью пользовательских интерфейсов




В данном разделе приведен пример клиентского приложения (проект ConsoleClient_1), в котором иллюстрируется использование внутрипроцессного сервера DLLMathServer (см. л.р. «Разработка внутрипроцессного сервера и клиентского приложения с использованием ATL»). В данном приложении показано, как можно организовать использование сервера практически «с нуля», управляя почти полностью его загрузкой и вызовом методов. Далее будет показано, как этот процесс можно существенно упростить, если воспользоваться интеллектуальными указателями. 

В общем виде процесс взаимодействия клиентского приложения и сервера представлен на рис. 37.1.

Рис. 37.1 Обобщенная схема взаимодействия клиент-сервер

 

Когда клиент запрашивает сервер по его GUID (или ProgID), то этот запрос перехватывает СОМ-библиотека (Ole32.dll) и пытается отыскать в реестре Windows путь к каталогу, в котором размещен сервер. Еслит сервер не находится, то, естественно, возникает ошибка. В противном – счастливом – случае сервер загружается в память и автоматически создается объект «фабрика классов» в соответствии с пожеланиями клиента (выраженными где-то в параметрах соответствующих функций).

Объект фабрика классов создает СОМ-объект и, в случае удачи, возвращает указатель на интерфейс IUnknown. Далее все элементарно: используя указатель на интерфейс IUnknown с помощью функции QueryInterface мы получаем указатели на требуемые интерфейсы сервера и с их помощью вызываем соответствующие функции, т.е. заставляем сервер выполнять наши желания.

Сервер DLLMathServer имеет такие интерфейсы и методы (фрагменты файла DLLMathServer.idl):

[

  object,

  uuid(2CB07C07-F24C-4997-9BB3-F9436D291049),

  helpstring("IMathem Interface"),

  pointer_default(unique)

]

interface IMathem : IUnknown{

  [, helpstring("method Cube")] HRESULT Cube([in] DOUBLE Arg, [out] DOUBLE* Res);

};

 [

  object,

  uuid(60A322B4-F137-4f88-BC8A-EF45B977A99B),

  helpstring("IMathem2 Interface"),

  pointer_default(unique)

]

interface IMathem2 : IUnknown{

  [, helpstring("method Summa")] HRESULT Summa([in] DOUBLE* Arr, [in] LONG Num, [out] DOUBLE* Res);

};

 

Обратите внимание на числовые константы, заданные в качестве аргумента атрибута uuid: это GUID интерфейсов, которые нам необходимы для получения этих интерфейсов с помощью метода IUnknown::QueryInterface().

Для разработки этого клиентского приложения создадим обычное консольное приложение (имя проекта ConsoleClient_1) с поддержкой MFC. Теперь создадим файл MathemInt.h с помощью команды FileèNewèFileèVisual C++èHeader File(.h). Наполним файл описанием интерфейсов и их методов, которое должно, конечно, соответствовать содержимому файла DLLMathServer.idl:

struct IMathem : public IUnknown

{

  STDMETHOD_(HRESULT, Cube)(double, double *) =0;

};

struct IMathem2 : public IUnknown

{

  STDMETHOD_(HRESULT, Summa)(double *, long, double *) =0;

};

 

Этот файл надо подключить к главному и единственному файлу нашего приложения ConsoleClient_1.cpp.

Далее в начало этого же файла, например, после оператора using namespace std, надо ввести определения констант GUID интерфейсов, значения которых надо взять из файла DLLMathServer.idl, фрагмент которого приведен выше.

Теперь модифицируем главный файл нашего приложения в соответствии со следующим листингом:

// ConsoleClient_1.cpp

#include "stdafx.h"

#include "ConsoleClient_1.h"

#include "MathemInt.h"

#include <ConIO.h>

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

CWinApp theApp;

using namespace std;

static const GUID IID_IMathem =

{0x2CB07C07, 0xF24C,0x4997,{0x9B, 0xB3,0xF9,0x43,0x6D,0x29,0x10,0x49}};

static const GUID IID_IMathem2 =

{0x60A322B4, 0xF137,0x4f88,{0xBC, 0x8A,0xEF,0x45,0xB9,0x77,0xA9,0x9B}};

Struct OleInit

{ OleInit()

{

 CoInitialize(NULL);

 TRACE("CoInitialize(NULL) executed\n");

}

~OleInit()

{

 CoUninitialize();

 TRACE("CoUninitialize() executed\n");

}

} Init;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

{

       _tprintf(_T("Fatal Error: MFC initialization failed\n"));

       return nRetCode = 1;

}

CLSID clsid;

LPCLASSFACTORY pClf;

LPUNKNOWN pUnk;

IMathem * pMath;

IMathem2 * pMath2;

HRESULT hr;

// получаем GUID сервера (clsid) по его «внешнему» имени ProgID

if((hr=CLSIDFromProgID(L"DLLMathServer.Mathem",&clsid))!=NOERROR)

{

       TRACE("ProgID for DLLMathServer not found - error %x\n",hr);

       _getch(); return 1;

}

/* Создаем экземпляр объекта фабрики классов и получаем интерфейс фабрики

классов. В этот момент, при успешном выполнении функции CoGetClassObject(),   

запускается сервер

*/

if((hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER, NULL,

   IID_IClassFactory,(void **)&pClf))!=NOERROR)

{

       TRACE("Failed to start server - %x\n",hr);

       _getch(); return 1;

}

/* создаем СОМ объект и получаем указатель pUnk

на его интерфейс IUnknown */

     

pClf->CreateInstance(NULL,IID_IUnknown,(void **) &pUnk);

/* получаем указатели pMath и pMath2 на интерфейсы IMathem и IMathem2 */

pUnk->QueryInterface(IID_IMathem, (void **) &pMath);

pUnk->QueryInterface(IID_IMathem2,(void **) &pMath2);

TRACE(" pUnk=%p, pMath=%p, pMath2=%p\n",pUnk,pMath,pMath2);

// вызываем методы интерфейсов IMathem и IMathem2

double Res;

pMath->Cube(3,&Res);

cout<<"Cube(3)="<<Res<<endl;

double Arr[]={2,4,6,8};

long N=sizeof(Arr)/sizeof(Arr[0]);

double Sum;

pMath2->Summa(Arr,N,&Sum);

cout<<"Sum="<<Sum<<endl;

/* освобождаем все указатели на интерфейсы */

pClf->Release();

pUnk->Release();

pMath->Release();

pMath2->Release();

_getch();

return nRetCode;

}

 

Программа должна компилироваться и выполняться. Не забудьте зарегистрировать сервер DLLMathServer.dll.

Несколько комментариев к тексту программы. Что дает объявление экземпляра Init структуры OleInit, выполненное в глобальной области? «Автоматический» вызов функции инициализации OLE CoInitialize(NULL) при старте приложения и функции CoUninitialize() – при завершении работы приложения. Чтобы убедиться в этом – понаблюдайте за выводом в окно Debug.

И еще одно замечание. Так писать программу, как это сделано выше, могут только боги, которые не ошибаются, а простым смертным свойственны ошибки. Большинство функций OLE возвращают коды ошибок; методы СОМ объектов тоже могут и должны это делать. Эти возвращаемые значения надо контролировать. Например, функция QueryInterface() также возвращает код ошибки. Попробуйте «испортить» значение GUID интерфейса IMathem, запустите приложение на выполнение и Вы увидите, что при вызове метода интерфейса IMathem возникает исключительная ситуация, а сообщение об ошибке содержит мало полезной информации. Вряд ли Вашему заказчику понравится такое поведение Вашей программы. Что же делать? Да вот что:

hr=pUnk->QueryInterface(IID_IMathem, (void **) &pMath);

if(hr!=S_OK)

{

cout<< GetErrorMessageA(hr)<<endl;   // в консольном приложении

AfxMessageBox(GetErrorMessage(hr)); // в любом приложении

     // корректно завершить работу программы

}

 

Описание глобальных функций GetErrorMessage() и GetErrorMessageA() см. в разделе «Обработка ошибок».










Последнее изменение этой страницы: 2018-04-12; просмотров: 380.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...