Я изучаю пример использования 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, ¶ms); // 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-объектом, который не работает правильно при использовании в управляемомприложение (это определенно сделало бы этот вопрос слишком широким).Поэтому я хочу убедиться, что мой подход и мои определения по крайней мере верны.Есть ли что-нибудь еще, что я должен сделать и чего мне не хватает?