Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.11.20;
Скачать: CL | DM;

Вниз

COM Server&Client (почти по Тенцеру)   Найти похожие ветки 

 
Сергей Александров   (2005-02-07 04:56) [0]

1) Приложение-сервер (EXE).

Имеется интерфейс (файл Intf.pas):


type
 ITest = interface (IUnknown)
 ["{6847A0CC-1EDA-4AD1-9B80-A42A06412D7D}"]
   procedure A; stdcall;
 end;


Определена константа:

const
 Class_Test: TGUID = "{A266C4D4-B1F4-4439-B024-BA73E33F7E87}";


Реализуется и регистрируется (файл SrvSrc.pas):

unit SrvSrc;

interface
uses Windows, ActiveX, Classes, ComObj, Intf;

type
 TTest = class (TComObject, ITest)
 protected
   procedure A; stdcall;
 end;

implementation
uses ComServ;

{ TTest }

procedure TTest.A;
begin
 Beep
end;

initialization
 CoInitialize(nil);
 TComObjectFactory.Create(ComServer, TTest, Class_Test,
   "Test", "", ciMultiInstance, tmSingle);
end.


2) Приложение-клиент.

в uses добавлен файл с объявлением интерфеса:
uses Intf.pas

По клику на кнопке выполняется следующий код:

procedure TForm2.Button1Click(Sender: TObject);
var
 ATest: ITest;
begin
 ATest := CreateComObject(Class_Test) as ITest; // ?
end;


В результате: сервер запускается, клиент выдает ошибку: Interface not supported.
Примечание: ссылку на IUnknown выдает прекрасно.

Ткните носом, пожалуйста в незнание очевидных вещей. Вроде почти Copy/Paste из Тенцера, а не пашет, хоть убей. Помогите разобраться.


 
Набережных С. ©   (2005-02-07 07:59) [1]

>ITest = interface (IUnknown)

Такому интерфейсу для межапартаментного взаимодействия нужна прокси/стаб длл. В поставке Delphi поддержки этого нет. Либо писать самому, либо использовать IMarshal, что ничуть не проще. Наследуй интерфейс от IDispatch, включи в проект библиотеку типов и все заработает.


 
Сергей Александров   (2005-02-07 09:45) [2]

Спасибо за доступное объяснение, сдвинулся с мертвой точки.

:)


 
Григорьев Антон ©   (2005-02-07 11:28) [3]

Наследовать от IDispatch в данном случае не обязательно. Просто нужно изначально делать COM-объект с библиотекой типов. Проще всего для этого воспользоваться мастером создания COM-объекта в диалоговом окне File/New/Other... на странице ActiveX. Если там не убирать галочки, стоящие по умолчанию, то будет создан COM-объект с фабрикой класса и с библиотекой типов, поддерживающих универсальный маршалинг, унаследованные от TTypedComObject и TTypedComObjectFactory соответственно. Для создания библиотеки типов откроется специальное окно - там всё очевидно, сами сможете разобраться, что к чему. Созданная библиотека типов будет автоматически помещена в exe-файл сервера и зарегистрирована при его запуске.


 
Набережных С. ©   (2005-02-07 14:31) [4]

>Григорьев Антон ©   (07.02.05 11:28) [3]

Вот только смысла в таких интерфейсах никакого нет - ограничения те же, что и у потомков IDispatch, а возможностей автоматизации нет. И забивать ими голову на начальном этапе освоение не нужно.


 
Владислав ©   (2005-02-07 15:20) [5]

> Набережных С. ©   (07.02.05 07:59) [1]

Ну вы не пугайте человека :)

Вот часть idl файла:


[
object,
uuid(FA307E85-5272-44d0-BDB2-F9433C8BAC8B),
helpstring("IServerConnector Interface"),
pointer_default(unique)
]
interface IServerConnector : IUnknown{
[helpstring("method GetServerList")] HRESULT GetServerList([out] IServerList ** ServerList);
};

// IServerList Interface

[
object,
uuid(297481F0-A3D2-43d1-B0DB-9A6C4B007885),
helpstring("IServerList Interface"),
pointer_default(unique)
]
interface IServerList : IUnknown{
[helpstring("method RegisterServer")] HRESULT RegisterServer([in] BSTR ConnStr, [out] LPDBOBJECTID ObjID, [out] IServer ** Server);
[helpstring("method UnregisterServer")] HRESULT UnregisterServer([in] LPDBOBJECTID ObjID);
[helpstring("method EnumServer")] HRESULT EnumServer([out] IEnumServer ** Enum);
};

// IServer Interface

[
object,
uuid(4F2431AA-1637-4d8c-B093-4E045E9D7AC0),
helpstring("IServer Interface"),
pointer_default(unique)
]
interface IServer : ICommonObject{
[helpstring("method Connect")] HRESULT Connect([in] BSTR ConnStr);
[helpstring("method Disconnect")] HRESULT Disconnect(void);
[helpstring("method GetConnectString")] HRESULT GetConnectString([out] BSTR * ConnStr);

[helpstring("method CreateDatabase")] HRESULT CreateDatabase([in] BSTR FileName, [out] LPDBOBJECTID ObjID, [out] IDatabase ** Database);
[helpstring("method DropDatabase")] HRESULT DropDatabase([in] LPDBOBJECTID ObjID);
[helpstring("method RegisterDatabase")] HRESULT RegisterDatabase([in] BSTR FileName, [out] LPDBOBJECTID ObjID, [out] IDatabase ** Database);
[helpstring("method UnregisterDatabase")] HRESULT UnregisterDatabase([in] LPDBOBJECTID ObjID);
[helpstring("method EnumDatabase")] HRESULT EnumDatabase([out] IEnumDatabase ** Enum);
};

// IEnumServer Interface

[
object,
uuid(D1019908-2CB3-4e71-9649-942750D0ED47),
helpstring("IEnumServer Interface"),
pointer_default(unique)
]
interface IEnumServer : IUnknown{
[helpstring("method Next")] HRESULT Next([in] ULONG celt, [out] IServer ** rgelt , [out] ULONG * pceltFetched);
[helpstring("method Skip")] HRESULT Skip([in] ULONG celt);
[helpstring("method Reset")] HRESULT Reset(void);
[helpstring("method Clone")] HRESULT Clone([out] IEnumServer ** ppenum);
};

// ICommonObject Interface (Base interface)

[
object,
uuid(03E64637-436F-459d-A13A-C767F56B5A4F),
helpstring("ICommonObject Interface"),
pointer_default(unique)
]
interface ICommonObject : IUnknown{
[helpstring("method GetObjectInfo")] HRESULT GetObjectInfo([out] LPDBOBJECTID id, [out] BSTR * Name, [out] BSTR * Description);
[helpstring("method SetObjectInfo")] HRESULT SetObjectInfo([in] BSTR Name, [in] BSTR Description);
};


Вот использование:

procedure TForm1.Button1Click(Sender: TObject);
var
 hr: HRESULT;
 LCount: Cardinal;
 LServConn: IServerConnector;
 LServList: IServerList;
 LEnumServ: IEnumServer;
 LCommnObj: ICommonObject;
 LServ: IServer;
begin
 hr := CoCreateInstance(CLASS_ServerConnector, nil, CLSCTX_INPROC_SERVER,
   IID_IServerConnector, LServConn);
 if Succeeded(hr) then
 begin
   hr := LServConn.GetServerList(LServList);
   if Succeeded(hr) then
   begin
     hr := LServList.EnumServer(LEnumServ);
     if Succeeded(hr) then
     begin
       hr := LEnumServ.Next(1, LCommnObj, LCount);
       while (hr <> S_FALSE) do
       begin
         LServ := IServer(LCommnObj);
         hr := LEnumServ.Next(1, LCommnObj, LCount);
       end
     end
   end
 end
end;


Никаких специальных прокси/заглушек, никаких IDispatch.
Пример рабочий.


 
Набережных С. ©   (2005-02-07 16:10) [6]

>Владислав ©   (07.02.05 15:20) [5]

>hr := CoCreateInstance(CLASS_ServerConnector, nil, CLSCTX_INPROC_SERVER,
IID_IServerConnector, LServConn);

Вы о чем? Смысл выделенного параметра Вам понятен? А также смысл выражения "для межапартаментного взаимодействия"?


 
Григорьев Антон ©   (2005-02-07 17:39) [7]


> Набережных С. ©   (07.02.05 14:31) [4]
> >Григорьев Антон ©   (07.02.05 11:28) [3]
>
> Вот только смысла в таких интерфейсах никакого нет - ограничения
> те же, что и у потомков IDispatch, а возможностей автоматизации
> нет. И забивать ими голову на начальном этапе освоение не
> нужно.

<offtopic>
Это смотря что считать начальным этапом. Если начинать изучение с того, куда щёлкнуть мышкой, чтобы всё работало - тогда, может, и так. А вот если последовательно разбираться с теорией, тогда всё-таки лучше сначала разобраться с интерфейсами и маршалингом вообще на примере custom-интерфейсов и лишь потом приступить к изучению интерфейсов диспетчеризации и дуальных интерфейсов.
</offtopic>


 
Сергей Александров   (2005-02-07 18:32) [8]

Спасибо за обилие информации.

Не подскажите ли, каков оптимальный (общепринятый) порядок действий в следующей ситуации.
Имеется уже готовый проект, с развитой системой плагинов, реализованных через интерфейсы. То есть уже существует система интерфейсов, достаточно сложная. И захотелось все это дело (то есть готовые интерфейсные юниты) сделать доступными через COM (Для вызова из других приложений в виде CreateCOMObject(...)). Неужели - единственный выход, это заново описывать эти интерфейсы в редакторе типов. Нельзя ли библиотеку типов сформировать для этих юнитов хотя-бы "полуавтоматически".

Вот, например, фрагмент интерфейса главного АПИ сервера:

 IXApplication = interface (IUnknown)
   ["{EC477519-755C-4DA2-9223-9BBA17A57DDF}"]
   procedure Halt; stdcall;
   function GetAPIVersion: TXAPIVersion;stdcall;
   function GetMainWindow: IMainWindow;stdcall;
   function GetCurrentDocumentWindow: IDocumentWindow;stdcall;
   function GetCurrentReportWindow: IReportWindow;stdcall;
   procedure ShowProgressIndicator (const ACaption: WideString);
   procedure UpdateProgressIndicator (const Pos,MaxPos: Integer);
   procedure HideProgressIndicator;
   function CreateGeoCalculator (AType: Integer): IGeoCalculator;
   function AppSettings: IApplicationSettings;
   procedure Halt; stdcall;
   ...
 end;


Нельзя ли для него typelibrary сформировать как-то "автоматически", или придется typelibrary переописывать самому.

Заранее благодарен за ответ.


 
Набережных С. ©   (2005-02-07 19:20) [9]

>Григорьев Антон ©   (07.02.05 17:39) [7]

>лучше сначала разобраться с интерфейсами и маршалингом вообще на примере custom-интерфейсов и лишь потом приступить к изучению интерфейсов диспетчеризации

Что-то я не уловил смысл этой фразы применительно к [3] и [4]. Можно разъяснить, что значит "маршалингом вообще на примере custom-интерфейсов" и почему "лишь потом приступить к изучению интерфейсов диспетчеризации и дуальных интерфейсов.
"?
И как это соотносится с [3]? Очень хочется услышать.

А для стандартной работы с IDispatch вовсе не нужно знать тонкости дуализма и диспетчиризации, многие не знают и ничего, обходятся, благо Delphi это позволяет. Когда почувствует потребность, тогда и разберется.

>Сергей Александров   (07.02.05 18:32) [8]

Придется вручную, по крайней мере мне такие конвертеры не известны. Но и это еще не все. Если у какого-то интерфейса хотя-бы в одном из методов есть параметр, не совместимый с OLE Automation, то Delphi не сможет тебе помочь в его маршалинге. В этом случае придется писать IDL файл и делать прокси/стаб в студии. Либо писать их для него вручную, однако это дело непростое. Ошибка была сделана на этапе проектирования - не тот базовый интерфейс выбран. И если завтра тебе скажут, что нужно подключаться к твоему серверу из клиента на бейсике или из скрипта, то придется полностью переписывать весь сервер.


 
Сергей Александров   (2005-02-07 23:24) [10]


>  [9] Набережных С. ©   (07.02.05 19:20)


Спасибо за разъяснения. Буду копать, может часть интерфейса вытащить в КОМ все-же удастся, хотя-бы базовые функции. Еще раз спасибо.


 
Григорьев Антон ©   (2005-02-08 16:03) [11]


> Набережных С. ©   (07.02.05 19:20) [9]
> А для стандартной работы с IDispatch вовсе не нужно знать
> тонкости дуализма и диспетчиризации, многие не знают и ничего,
> обходятся, благо Delphi это позволяет. Когда почувствует
> потребность, тогда и разберется.

Мне очень не нравится такой подход, потому что без знания теории постоянно лезут проблемы, стоит лишь чуть-чуть выйти за рамки тех возможностей, которые предусмотрели авторы VCL. Отсюда и утверждение о том, что сначала лучше разобраться с тем, как вообще это всё работает, а потом уже изучать IDispatch как частный случай использования интерфейсов.


 
Набережных С. ©   (2005-02-08 16:44) [12]

>Григорьев Антон ©   (08.02.05 16:03) [11]

Антон, мне тоже не очень. Вот перед нами пример, к чему  это может привести. Но проблема-то в том, что делать надо сейчас, а не когда все доскально изучишь. Потому и говорю, что не надо отвлекаться на неавтоматизационные интерфейсы. Соглась, круг задач, где следует им отдать предпочтение, черезвычно ограничен. С другой стороны, стандартная работа с потомками IDispatch ничем не отличается от работы с такими интерфесами, так что никаких дополнительных навыков они не дадут. И еще одно НО. Delphi позволяет начать работу с интерфейсами, не имея о них глубоких знаний, она даже не требует от программиста знания IDL! А практика упрощает процесс понимания, что же это за зверь такой. И тогда уже будет проще идти дальше, вникая во все тонкости и разбираясь с возможностями. Проще уже будет понять саму сущность СОМ. Такое вот мое мнение. И извини за излишнюю резкость.


 
Владислав ©   (2005-02-09 11:42) [13]

> Набережных С. ©   (07.02.05 16:10) [6]

Мда... конечно знаю. Виноват. Это моя невнимательность.


 
Григорьев Антон ©   (2005-02-09 14:33) [14]


> Набережных С. ©   (08.02.05 16:44) [12]
> И извини за излишнюю резкость.

Ничего страшного, я не обиделся. Согласен, что практика иногда заставляет что-то делать, не оставляя время на получение глубоких знаний. Просто мне сейчас приходится очень много думать вот об этом проекте: http://www.delphikingdom.com/lyceum/seminar.asp?partID=8 - вот и начал мыслить однобоко, с крутым креном в сторону теории :))



Страницы: 1 вся ветка

Текущий архив: 2005.11.20;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.029 c
14-1130186619
LordOfRock
2005-10-25 00:43
2005.11.20
Меня флудят :(((


14-1130348890
Knight
2005-10-26 21:48
2005.11.20
Если кто-то будет в IRC-е... забегайте


2-1131189872
Kostafey
2005-11-05 14:24
2005.11.20
Программа то работает, то не работает на разных PC


5-1109970079
BRom
2005-03-05 00:01
2005.11.20
Видимость внутреннего компоненте другими


1-1130404652
leonon
2005-10-27 13:17
2005.11.20
Работа с TWebBrowser