Использование производного IUnknown объекта ATL COM в .NET

Мой ИДЛ:

[
    object,
    uuid(52D64BCC-03F1-442B-BED1-70992111E2B1),
    helpstring("ISimpleObject Interface"),
    pointer_default(unique)
]
interface ISimpleObject : IUnknown{
    [helpstring("method Hoop"), local] HRESULT Hoop(void);
};
[
    uuid(3D9ABD55-3C84-43C8-9C34-3915B6B34989),
    version(1.0),
    helpstring("ComServer 1.0 Type Library")
]
library ComServerLib
{
    importlib("stdole2.tlb");
    [
        uuid(42E2236D-1DA0-455F-9EF4-31A4A1E04F47),
        helpstring("SimpleObject Class")
    ]
    coclass SimpleObject
    {
        [default] interface ISimpleObject;
    };
};

Мой COM-класс:

class ATL_NO_VTABLE CSimpleObject :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CSimpleObject, &CLSID_SimpleObject>,
    public ISimpleObject
{
public:
    CSimpleObject()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_SIMPLEOBJECT)

BEGIN_COM_MAP(CSimpleObject)
    COM_INTERFACE_ENTRY(ISimpleObject)
END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

public:
    // ISimpleObject
    STDMETHOD(Hoop)(void)
    {
        return S_OK;
    }
};

OBJECT_ENTRY_AUTO(__uuidof(SimpleObject), CSimpleObject)

Мой .NET-клиент:

[ComImport]
[Guid("52D64BCC-03F1-442B-BED1-70992111E2B1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISimpleObject
{
    [PreserveSig]
    int Hoop();
}

[ComImport]
[Guid("42E2236D-1DA0-455F-9EF4-31A4A1E04F47")]
public class SimpleObject
{
}

class Program
{
    static void Main(string[] args)
    {
        SimpleObject simpleObject = new SimpleObject();

        ISimpleObject simpleObjectInterface = (ISimpleObject)simpleObject;

        int returnValue = simpleObjectInterface.Hoop(); // Error!
    }
}

Клиент получает исключение "System.AccessViolationException: Попытка чтения или записи защищенной памяти...". Почему?

Я использую Visual Studio 2008, Windows Vista x64. Проекты C++ и C# имеют конфигурацию x86.


person vpp    schedule 23.03.2012    source источник


Ответы (1)


int Hoop();

является неправильным переводом объявления IDL на C#. Правильным будет

void Hoop();

.NET оборачивает коды результатов COM (HRESULT) в соответствующие исключения C#. Например, если вы вернете E_NOTIMPL в коде C++, среда выполнения .NET создаст экземпляр класса NotImplementedException.

*ОБНОВЛЕНИЕ

Согласно MSDN, основной поток приложения по умолчанию инициализируется как «MTA». Ваш объект CSimpleObject настроен для размещения в «STA». Для совершения межапартаментных вызовов среда выполнения COM будет использовать ваша реализация прокси/заглушки для сортировки соответствующих данных.

Среде выполнения требуется допустимая реализация прокси/заглушки, которая генерируется MIDL из вашего IDL-файла.

Вы пометили функцию «Пяльцы» как «Локальную». Это означает, что MIDL не будет генерировать маршалинговый код для этого метода, поэтому вы получите исключение.

Следует удалить атрибут «local», поскольку он предназначен для использования только внутри сервера, на котором реализован объект. Но в качестве возможного решения я могу предложить вам использовать объект из потока, настроенного для работы в STA. В любом случае, это плохой подход, так как он не гарантирует, что не будет задействована сортировка.

Вы можете пометить свой основной поток для запуска в STA следующим образом:

[STAThread()]
static void Main(string[] args)
{
    ...
}

Я надеюсь, что это поможет вам решить проблему.

person Igor Chornous    schedule 23.03.2012
comment
К сожалению, это не работает. Атрибут PreserveSigAttribute позволяет получить код результата (HRESULT) через возвращаемое значение. - person vpp; 01.04.2012
comment
vpp, извините, не заметил этого атрибута. я обновил ответ - person Igor Chornous; 02.04.2012