Как объявить COM-интерфейс со свойствами в C++

Я пытаюсь вызвать зарегистрированный COM-интерфейс (Inproc dll) в среде Eclipse C++ (инструментарий MinGW GCC). Поэтому я не верю, что могу просто импортировать библиотеку типов dll так, как обычно делаю это в Visual C++. Я пытаюсь сам определить интерфейс, чтобы упростить вызовы функций, а не использовать Invoke.

IDL для интерфейса вне oleview выглядит так:

  uuid(9293C753-B073-11D2-BD89-0060978EEB9C),
  helpstring("IXSequence Interface"),
  dual
]
dispinterface IXSequence {
    properties:
    methods:
        [id(0x00000001), helpstring("method Close")]
        void Close();
        [id(0x00000002), helpstring("method New")]
        void New();
        [id(0x00000003), helpstring("method Open")]
        void Open(
                        BSTR FileName, 
                        long ReadOnly);
        [id(0x00000004), helpstring("method Save")]
        void Save(
                        BSTR UserName, 
                        BSTR Comment, 
                        long Overwrite);
        [id(0x00000005), helpstring("method SaveAs")]
        void SaveAs(
                        BSTR FileName, 
                        BSTR UserName, 
                        BSTR Comment, 
                        long Overwrite);
        [id(0x00000006), propget, helpstring("property Header")]
        VARIANT Header();
        [id(0x00000007), propget, helpstring("property AuditDataCollection")]
        VARIANT AuditDataCollection();
        [id(0x00000008), propget, helpstring("property Samples")]
        VARIANT Samples();
        [id(0x00000009), propget, helpstring("property BracketType")]
        XBracketTypes BracketType();
        [id(0x00000009), propput, helpstring("property BracketType")]
        void BracketType([in] XBracketTypes rhs);
        [id(0x0000000a), propget, helpstring("property UserLabel")]
        BSTR UserLabel(short Index);
        [id(0x0000000a), propput, helpstring("property UserLabel")]
        void UserLabel(
                        short Index, 
                        [in] BSTR rhs);
        [id(0x0000000b), propget, helpstring("property TrayConfiguration")]
        BSTR TrayConfiguration();
        [id(0x0000000b), propput, helpstring("property TrayConfiguration")]
        void TrayConfiguration([in] BSTR rhs);
        [id(0x0000000c), propget, helpstring("property FileName")]
        BSTR FileName();
        [id(0x0000000d), propget, helpstring("property NewFile")]
        long NewFile();
};

Я попытался определить интерфейс в своем собственном заголовочном файле следующим образом:

const GUID CLSID_XcalFiles = { 0x9293C754, 0xB073, 0x11D2, {0xBD, 0x89, 0x00, 0x60, 0x97, 0x8E, 0xEB, 0x9C } };
const IID IID_IXSequence = { 0x9293C753, 0xB073, 0x11D2, {0xBD, 0x89, 0x00, 0x60, 0x97, 0x8E, 0xEB, 0x9C } };

typedef enum {
    XUnspecified = 0,
    XOverlapped = 1,
    XNonBracketed = 2,
    XNonOverlapped = 3,
    XOpen = 4
} XBracketTypes;

enum XSampleTypes
{
    XSampleUnknown = 0,
    XSampleBlank = 1,
    XSampleQC = 2,
    XSampleStdClear = 3,
    XSampleStdUpdate = 4,
    XSampleStdBracket = 5,
    XSampleStdBracketStart = 6,
    XSampleStdBracketEnd = 7,
    XSampleProgram = 8,
    XSampleNumbersOfDifferentTypes = 9
};

DECLARE_INTERFACE_(IXSequence, IDispatch)
{
//methods
    STDMETHOD_(void, Close)(THIS)PURE;
    STDMETHOD_(void, New)(THIS)PURE;
    STDMETHOD_(void, Open)(THIS_ BSTR FileName, long ReadOnly)PURE;
    STDMETHOD_(void, Save)(THIS_ BSTR UserName, BSTR Comment, long Overwrite)PURE;
    STDMETHOD_(void, SaveAs)(THIS_ BSTR FileName, BSTR UserName, BSTR Comment, long Overwrite)PURE;
    STDMETHOD_(VARIANT, GetHeader)(THIS)PURE;
    STDMETHOD_(VARIANT, GetAuditDataCollection)(THIS)PURE;
    STDMETHOD_(VARIANT, GetSamples)(THIS)PURE;
//properties
    STDMETHOD_(XBracketTypes, BracketType)(THIS)PURE;
    STDMETHOD_(void, BracketType)(THIS_ XBracketTypes rhs)PURE;
    STDMETHOD_(BSTR, UserLabel)(THIS_ short Index)PURE;
    STDMETHOD_(void, UserLabel)(THIS_ short Index, BSTR rhs)PURE;
    STDMETHOD_(BSTR, TrayConfiguration)(THIS)PURE;
    STDMETHOD_(void, TrayConfiguration)(THIS_ BSTR rhs)PURE;
    STDMETHOD_(BSTR, FileName)(THIS)PURE;
    STDMETHOD_(long, NewFile)(THIS)PURE;

};

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

STDMETHOD(GetBracketType)(THIS_ XBracketTypes* rhs)PURE;

Имеет ли это смысл? Есть ли лучший способ объявить свойства для COM-интерфейса, чтобы я мог использовать их как обычные свойства?


person forother    schedule 05.11.2019    source источник
comment
Свойства COM поддерживаются обычными методами, одним геттером и одним сеттером. Некоторые клиенты (например, языки сценариев) могут обертывать вызовы методов синтаксическим сахаром, чтобы они выглядели как настоящие свойства, но все это иллюзия. Базовые методы действительно выглядят так, как вы показываете; Ты на правильном пути.   -  person Igor Tandetnik    schedule 06.11.2019
comment
Теперь вы не можете скомпилировать свой IDL с помощью компилятора MIDL? Одним из его результатов является файл .h, который очень похож на тот, который вы создаете вручную.   -  person Igor Tandetnik    schedule 06.11.2019
comment
Языки сценариев @IgorTandetnik обычно проходят через IDispatch::GetIDsOfNames() и IDispatch::Invoke(), они не имеют прямого доступа к методам получения/установки свойств. Таким образом, когда скрипт обращается к obj.Prop или вызывает obj.Method(), он фактически вызывает obj.GetIDsOfNames(), чтобы получить DispID Prop или Method (возникает ошибка, если DispID не может быть найден), а затем вызывает obj.Invoke() с этим DispID и соответствующими флагами и параметрами как нужный.   -  person Remy Lebeau    schedule 06.11.2019
comment
Я бы намекнул, что если вы собираетесь звонить из C++, смотрите на вывод интерфейса, а не на вывод dispinterface в OLEVIEW. Интерфейс будет классифицирован как dispinterface и interface, но тот, который вы хотите использовать, будет указан в разделе interface.   -  person Joseph Willcoxson    schedule 06.11.2019
comment
Брр. Это dispinterface, что означает, что клиенты должны вызывать этих членов через IDispatch. Сервер не обязан заставлять его работать, когда вы игнорируете это и вызываете их напрямую. Что должно быть проще для прохождения через Eclipse, просто много шероховатого кода.   -  person Hans Passant    schedule 06.11.2019


Ответы (1)


Обычно интерфейсы С++ выглядят примерно так:

STDMETHOD(get_BracketType)(BracketType* BType);

STDMETHOD(put_BrackedType)(BracketType Btype);

Как правило, свойства возвращают типы HRESULT... как и методы. Они будут иметь префикс get_ или put_ перед именем свойства.

Не уверен, как это делает среда Eclipse C++.

person Joseph Willcoxson    schedule 05.11.2019
comment
Вышеупомянутое должно работать, если это двойной интерфейс. Если это не двойной интерфейс, вы должны использовать Invoke. - person Joseph Willcoxson; 06.11.2019