Вызов COM-метода создает необъяснимое исключение COMException в C #, отлично работает в C ++. - PullRequest
0 голосов
/ 22 февраля 2019

Я изучаю пример использования COM-интерфейса в C #, поэтому я написал кокласс и интерфейсы для (I)DxDiagProvider и IDxDiagContainer COM-объектов.

Однако, хотя, по-видимому, эквивалентенкод отлично работает в C ++, вызов одного из его методов завершается с COMException (HRESULT -1) в C #.Возможно, я что-то испортил, портируя это на C #, но не вижу ошибки.

Собственный код (работает)

Объекты определены в необработанном C следующим образом, которая также является единственной информацией, которую я имею о них (не имея библиотеки для tlbimp или чего-либо еще):

struct IDxDiagContainerVtbl;
struct IDxDiagContainer { IDxDiagContainerVtbl* lpVtbl; };
struct IDxDiagContainerVtbl
{
    HRESULT(__stdcall* QueryInterface)(IDxDiagContainer* This, const IID* const riid, void** ppvObject);
    ULONG(__stdcall* AddRef)(IDxDiagContainer* This);
    ULONG(__stdcall* Release)(IDxDiagContainer* This);
    HRESULT(__stdcall* EnumChildContainerNames)(IDxDiagContainer* This, DWORD dwIndex, LPWSTR pwszContainer, DWORD cchContainer);
    HRESULT(__stdcall* EnumPropNames)(IDxDiagContainer* This, DWORD dwIndex, LPWSTR pwszPropName, DWORD cchPropName);
    HRESULT(__stdcall* GetChildContainer)(IDxDiagContainer* This, LPCWSTR pwszContainer, IDxDiagContainer** ppInstance);
    HRESULT(__stdcall* GetNumberOfChildContainers)(IDxDiagContainer* This, DWORD* pdwCount);
    HRESULT(__stdcall* GetNumberOfProps)(IDxDiagContainer* This, DWORD* pdwCount);
    HRESULT(__stdcall* GetProp)(IDxDiagContainer* This, LPCWSTR pwszPropName, VARIANT* pvarProp);
};

struct DXDIAG_INIT_PARAMS
{
    DWORD dwSize;
    DWORD dwDxDiagHeaderVersion;
    BOOL bAllowWHQLChecks;
    void* pReserved;
};
struct IDxDiagProviderVtbl;
struct IDxDiagProvider { IDxDiagProviderVtbl* lpVtbl; };
struct IDxDiagProviderVtbl
{
    HRESULT(__stdcall* QueryInterface)(IDxDiagProvider* This, const IID* const riid, void** ppvObject);
    ULONG(__stdcall* AddRef)(IDxDiagProvider* This);
    ULONG(__stdcall* Release)(IDxDiagProvider* This);
    HRESULT(__stdcall* Initialize)(IDxDiagProvider* This, DXDIAG_INIT_PARAMS* pParams);
    HRESULT(__stdcall* GetRootContainer)(IDxDiagProvider* This, IDxDiagContainer** ppInstance);
};

Создание этого и вызов Initialize в нативном C ++ прекрасно работает:

int main()
{
    GUID clsid;
    GUID iid;
    CLSIDFromString(L"{A65B8071-3BFE-4213-9A5B-491DA4461CA7}", &clsid);
    CLSIDFromString(L"{9C6B4CB0-23F8-49CC-A3ED-45A55000A6D2}", &iid);

    CoInitialize(NULL);
    IDxDiagProvider* pDxDiagProvider;
    HRESULT hr = CoCreateInstance(clsid, NULL, 1, iid, (LPVOID*)&pDxDiagProvider); // S_OK

    DXDIAG_INIT_PARAMS params;
    params.dwSize = sizeof(DXDIAG_INIT_PARAMS);
    params.dwDxDiagHeaderVersion = 111;
    params.bAllowWHQLChecks = 0;
    params.pReserved = 0;

    // Sorry for the C-like access, I don't have anything better than the structs above.
    hr = pDxDiagProvider->lpVtbl->Initialize(pDxDiagProvider, &params); // S_OK
}

Управляемый код (не работает)

Итак, я продолжил и перенес это в консольную программу STAThread x86 .NET 4.6.1 C # со следующими определениями:

[ComImport]
[Guid("A65B8071-3BFE-4213-9A5B-491DA4461CA7")]
public class DxDiagProvider { }
[Guid("9C6B4CB0-23F8-49CC-A3ED-45A55000A6D2")]
public interface IDxDiagProvider
{
    void Initialize(ref DXDIAG_INIT_PARAMS pParams);
    void GetRootContainer(ref IDxDiagContainer ppInstance);
}
[StructLayout(LayoutKind.Sequential)]
public struct DXDIAG_INIT_PARAMS
{
    public uint dwSize;
    public uint dwDxDiagHeaderVersion;
    public bool bAllowWHQLChecks;
    public IntPtr pReserved;
};

[Guid("7D0F462F-4064-4862-BC7F-933E5058C10F")]
public interface IDxDiagContainer
{
    void EnumChildContainerNames(uint dwIndex, string pwszContainer, uint cchContainer);
    void EnumPropNames(uint dwIndex, string pwszPropName, uint cchPropName);
    void GetChildContainer(string pwszContainer, ref IDxDiagContainer ppInstance);
    void GetNumberOfChildContainers(ref uint pdwCount);
    void GetNumberOfProps(ref uint pdwCount);
    void GetProp(string pwszPropName, ref IntPtr pvarProp);
}

Теперь, когда создание класса и приведение интерфейса работает, вызов Initialize прерывается на COMException: Exception from HRESULT: 0xFFFFFFFF:

[STAThread]
static void Main(string[] args)
{
    // Working fine.
    DxDiagProvider dxDiagProviderClass = new DxDiagProvider();
    IDxDiagProvider dxDiagProvider = (IDxDiagProvider)dxDiagProviderClass;

    DXDIAG_INIT_PARAMS initParams = new DXDIAG_INIT_PARAMS
    {
        dwSize = (uint)Marshal.SizeOf<DXDIAG_INIT_PARAMS>(),
        dwDxDiagHeaderVersion = 111
    };
    dxDiagProvider.Initialize(ref initParams); // causes COMException
}

Я не уверен, что я сделал не так.До сих пор я проверял и проверял следующие вещи:

  • Убедитесь, что структура DXDIAG_INIT_PARAMS имеет правильный размер, выравнивание, расположение
  • Убедитесь, что мои методы в правильном порядке иЯ не добавил IUnknown методы
  • Попытка добавления DispIdAttribute к интерфейсным методам, хотя я думаю, что это актуально только при размещении COM-объектов и не их использовании.
  • Попытка передачипараметры как не-ref, хотя они должны быть ref, поскольку параметр является указателем.

Я действительно надеюсь, что это не имеет ничего общего с COM-объектом, который не работает правильно при использовании в управляемомприложение (это определенно сделало бы этот вопрос слишком широким).Поэтому я хочу убедиться, что мой подход и мои определения по крайней мере верны.Есть ли что-нибудь еще, что я должен сделать и чего мне не хватает?

1 Ответ

0 голосов
/ 22 февраля 2019

Я обнаружил, чего мне не хватало, и смог исправить:

Очевидно, я забыл украсить интерфейсы C # с помощью InterfaceTypeAttribute и указать ComInterfaceType.InterfaceIsIUnknown.По крайней мере для этих COM-объектов это необходимо, так как по умолчанию ComInterfaceType.InterfaceIsDual вызывает выброс COMException s.

Например, определения интерфейса теперь выглядят так в C # (плюс я исправил некоторые ref до out параметров, которые, однако, не были причиной этой проблемы):

[Guid("9C6B4CB0-23F8-49CC-A3ED-45A55000A6D2")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] // <-- need this
public interface IDxDiagProvider
{
    void Initialize(ref DXDIAG_INIT_PARAMS pParams);
    void GetRootContainer(out IDxDiagContainer ppInstance);
}

[Guid("7D0F462F-4064-4862-BC7F-933E5058C10F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] // <-- need this
public interface IDxDiagContainer
{
    void EnumChildContainerNames(uint dwIndex, string pwszContainer, uint cchContainer);
    void EnumPropNames(uint dwIndex, string pwszPropName, uint cchPropName);
    void GetChildContainer(string pwszContainer, out IDxDiagContainer ppInstance);
    void GetNumberOfChildContainers(out uint pdwCount);
    void GetNumberOfProps(out uint pdwCount);
    void GetProp(string pwszPropName, out IntPtr pvarProp);
}
...