Почему ошибка при формировании интерфейса?

0 голосов
спросил 22 Апр, 10 от dmitry12081973 (1,920 баллов) в категории Программные продукты Esri
Здравствуйте.
Подскажите, пожалуйста, что я не так делаю.

Создаю COM-объект в Delphi 7, при этом получаю автоматически с помощью делфиевского окна "COM Object Wizard" следующий код:

unit MAPCOMServerInit;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, MAPCOMServer_TLB, StdVcl, esriSystemUI_TLB;

type
  TMAPCOMServerDima = class(TAutoObject, ICommand)
  protected
    function Get_Caption: WideString; safecall;
    function Get_Category: WideString; safecall;
    function Get_Checked: WordBool; safecall;
    function Get_Enabled: WordBool; safecall;
    function Get_HelpContextID: Integer; safecall;
    function Get_HelpFile: WideString; safecall;
    function Get_Message: WideString; safecall;
    function Get_Name: WideString; safecall;
    function Get_Tooltip: WideString; safecall;
    function Get_Bitmap: OLE_HANDLE; safecall;
    procedure OnClick; safecall;
    procedure OnCreate(const hook: IDispatch); safecall;
  end;

implementation

uses ComServ;

function TMAPCOMServerDima.Get_Caption: WideString;
begin
end;

function TMAPCOMServerDima.Get_Category: WideString;
begin
end;

function TMAPCOMServerDima.Get_Checked: WordBool;
begin
end;

function TMAPCOMServerDima.Get_Enabled: WordBool;
begin
end;

function TMAPCOMServerDima.Get_HelpContextID: Integer;
begin
end;

function TMAPCOMServerDima.Get_HelpFile: WideString;
begin
end;

function TMAPCOMServerDima.Get_Message: WideString;
begin

end;

function TMAPCOMServerDima.Get_Name: WideString;
begin
end;

function TMAPCOMServerDima.Get_Tooltip: WideString;
begin
end;

function TMAPCOMServerDima.Get_Bitmap: OLE_HANDLE;
begin
end;

procedure TMAPCOMServerDima.OnClick;
begin
end;

procedure TMAPCOMServerDima.OnCreate(const hook: IDispatch);
begin
end;

initialization
  TAutoObjectFactory.Create(ComServer, TMAPCOMServerDima, Class_MAPCOMServerDima,
    ciMultiInstance, tmApartment);

В результате компиляции этого кода выдаётся ошибка:
Declaration of 'Get_Bitmap' differs from declaration in interface 'ICommand'

Смотрю исходный код файла esriSystemUI_TLB.pas, в котором этот ICommand объявлен и который соответственно взят в качестве предка для моего класса, вижу:

  ICommand = interface(IUnknown)
    ['{36B06538-4437-11D1-B970-080009EE4E51}']
    function Get_Enabled: WordBool; safecall;
    function Get_Checked: WordBool; safecall;
    function Get_Name: WideString; safecall;
    function Get_Caption: WideString; safecall;
    function Get_Tooltip: WideString; safecall;
    function Get_Message: WideString; safecall;
    function Get_HelpFile: WideString; safecall;
    function Get_HelpContextID: Integer; safecall;
    function Get_Category: WideString; safecall;
    procedure OnCreate(const hook: IDispatch); safecall;
    function Get_Bitmap: OLE_HANDLE; safecall;
    procedure OnClick; safecall;
    property Enabled: WordBool read Get_Enabled;
    property Checked: WordBool read Get_Checked;
    property Name: WideString read Get_Name;
    property Caption: WideString read Get_Caption;
    property Tooltip: WideString read Get_Tooltip;
    property Message: WideString read Get_Message;
    property HelpFile: WideString read Get_HelpFile;
    property HelpContextID: Integer read Get_HelpContextID;
    property Bitmap: OLE_HANDLE read Get_Bitmap;
    property Category: WideString read Get_Category;
  end;

То есть объявление функции Get_Bitmap точно такое же, как и в моём классе TMAPCOMServerDima.

Почему тогда ругается компилятор?

39 Ответы

0 голосов
ответил 22 Апр, 10 от pooperec (10,820 баллов)
Подключите esriSystem_TLB.

А ActiveX отключите...

    отличаються типы OLE_HANDLE
0 голосов
ответил 22 Апр, 10 от dmitry12081973 (1,920 баллов)
Спасибо! Теперь работает!
0 голосов
ответил 22 Апр, 10 от dmitry12081973 (1,920 баллов)
А можно ещё вопрос.
У меня есть на форме  есть компонент TMapControl. Как сделать так, чтобы при щелчке мыши на карте вызывалось событие TMAPCOMServerDima.OnClick;

0 голосов
ответил 23 Апр, 10 от pooperec (10,820 баллов)
Оо....

А что, не вызывается?
А вы декларировали, так как TMAPCOMServerDima.OnClick; больше похоже на событие щелчок по форме, а не мап контролу...
0 голосов
ответил 23 Апр, 10 от dmitry12081973 (1,920 баллов)
Извините за, наверное, наивные вопросы.

Я создал класс TMAPCOMServerDima, описаный выше.

Получаю ссылку типа ICommand на созданный COM-объект путём вызова в своём коде функции
CLASS_MAPCOMServerDima: TGUID = '{0755D69B-B817-4841-B167-77E1BC15D75C}';
class function CoMAPCOMServerDima.Create: ICommand;
begin
  Result := CreateComObject(CLASS_MAPCOMServerDima) as ICommand;
end;
которая была автоматом создана после закрытия окна "COM Object Wizard"

Как теперь мне работать с компонентом TMapControl, экземпляр которого с загруженной в него картой лежит на форме?

Пытался разобраться с этим с помощью примера:
https://forum.esri-cis.ru/index.php?qa=8661
но в нём мне непонятно:
1. Как вызывается функция TKK.OnCreate (при создании класса TKK?) и откуда передаётся параметр IDispatch?
2. Как вы
зывается функция TKK.OnClick (при нажатии мышкой на TMapControl ?)
0 голосов
ответил 23 Апр, 10 от pooperec (10,820 баллов)
Вы таки начали задавтаь правильные вопросы, всё же советую прочитать побольше про технологию COM.

"...путём вызова в своём коде..." В каком "своём", в плане: Какой код использует функцию "CoMAPCOMServerDima.Create"?

Остальные вопросы:
1. Вот пример вызова:
var
x : ICommand;
Begin
{или так} x:=CreateCOMObject(CLASS_MAPCOMServerDima) as ICommand;
// {или так} x:=CoMAPCOMServerDima.Create;
x.OnCreate(pMapControl._Object); {или передача диспинтерфейса, тут можно передать любой диспатч интерфейс, ArcMap передает IApplication (IMxApplication), ArcCatalog передает свой IMx}
// x.Click;
End;

2. Функция OnClick вызывается при нажатии на саму кнопку. Для "отработки" нажатия по Контролу нужно чтобы класс, помимо интерфейса ICommand, реализовал ещё и интерфейс ITool.

З.Ы. Добавлять реализацию можно (нужно) так:
View -> Type Library -> появляется окно управления библиотекой,

слева перечислены интерфейсы описанные в библиотеки, а также классы которые их реализуют, у Вас будет что-то в виде:
image
перейдя на закладку Uses вы можете выбирать зарегистрированные библиотеки, интерфейсы которых можно будет реализовывать в рамках данной библиотеки. Как-то так:
image
добавлять можно нажав ПКМ, и выбрав опцию "Show all libraries", и там выбирая нужные библиотеки.
(но Вам это ПОКА не нужно, т.к. ITool описан в той же библиотеке что и ICommand...

Потом перейдите, на сам класс, и в закладке "Implements" нажмите ПКМ, и выбрав пункт Insert Interfaces добавьте интерфейс ITool (или любой интересующий),
image
в ваш клас добавляться методы интерфейса ITool, среди них такие как:
    function OnContextMenu(x, y: Integer): WordBool; safecall;
    procedure OnDblClick; safecall;
    procedure OnKeyDown(keyCode, shift: Integer); safecall;
    procedure OnKeyUp(keyCode, shift: Integer); safecall;
    procedure OnMouseDown(button, shift, x, y: Integer); safecall;
    procedure OnMouseMove(button, shift, x, y: Integer); safecall;
    procedure OnMouseUp(button, shift, x, y: Integer); safecall;

0 голосов
ответил 23 Апр, 10 от dmitry12081973 (1,920 баллов)
Спасибо большое за столь содержательное описание проблемы.

Продвинулся дальше - выполняю следующий код:

Command.OnCreate(MapControl1.Object_)

при этом вызывается следующая процедура:

procedure TMAPCOMServerDima.OnCreate(const hook: IDispatch);
begin
    pmxApp := hook as IApplication; - на этой строке программа вылетает с ошибкой: "Interface not supported"
  end;

Как можно устранить эту ошибку?

0 голосов
ответил 24 Апр, 10 от pooperec (10,820 баллов)
Я вам уже писал, что:

>x.OnCreate(pMapControl._Object); {или передача >диспинтерфейса, тут можно передать любой диспатч
> интерфейс, ArcMap передает IApplication
>(IMxApplication)
, ArcCatalog передает свой IMx}

Передаете то Вы правильно, а вот на этапе приёма:
1. Всегда (!) проверяйте соответствие интерфейсов
а) путём присвоения через pObject.QueryInterface(), в Вашем случае :
>pmxApp.QueryInterface(hook,IApplication);
>if pmxApp<>nil then {тут код успеха, интерфейс присвоен правильно}
else {Интерфейс hook не может быть приведен к IApplication, и присвое переменной}

б) Через функцию "supports(hook, IApplication)", она вернет "ПРАВДА", если интерфейс соответствует, иначе "ЛОЖЬ"...

Это Вам пригодиться в дальнейшем, так как, например интерфейс ILayer может быть не только IFeatureLayer, но и слоем растра, или ... или ... и так далее...

В Вашем случаем, присваивайте диспатч интерфейс hook интерфейсу IMapControl, либо интерфейсу ihookhelper...
0 голосов
ответил 25 Апр, 10 от dmitry12081973 (1,920 баллов)
>1. Всегда (!) проверяйте соответствие интерфейсов
Это я понял.

>В Вашем случаем, присваивайте диспатч интерфейс hook интерфейсу IMapControl, либо интерфейсу ihookhelper...

Перепробовал разные варианты:

pmxApp := hook as IMapControl - пишет, что IMapControl неизвестный идентификатор

пробовал также:
pmxApp := hook as IMapControl2
pmxApp := hook as IMapControl3
pmxApp := hook as IMapControl4
- тоже не получается.

pmxApp := hook as ihookhelper - интерфейс не поддерживается

Пока не могу врубиться, как правильно сделать.
0 голосов
ответил 26 Апр, 10 от pooperec (10,820 баллов)
1. А какой тип переменной pmxApp?
2. Правильно сделать так:
var
pHook : IHookHelper;
//...
//...
//...
procedure TMAPCOMServerDima.OnCreate(const hook: IDispatch);
begin
if supports(hook,IApplication) then ShowMessage('Нас вызвал ArcMap');
if supports(hook,IMapControl) then Begin
// ShowMessage('Нас вызвал MapControl');
pHook:=CoHookHelper.Create as IHookHelper;
pHook.hook:=hook;

End;

end;
Добро пожаловать на сайт Вопросов и Ответов, где вы можете задавать вопросы по GIS тематике и получать ответы от других членов сообщества.
...