Я пытаюсь создать COM-сервер в C# для предоставления объекта автоматизации OLE. Я хотел бы реализовать интерфейс IDispatch вручную, так как мне нужно сделать некоторое количество динамического поиска c для членов. Когда я вызываю свой пользовательский GetIDsOfNames
, запускается метод C#, а затем клиент выдает ошибку E_POINTER
. Но давайте начнем с самого начала
После нескольких часов чтения и немалых проб и ошибок мне удалось собрать что-то вместе, я думаю, что это должно сработать. У меня очень простой интерфейс
[Guid("7B95C174-9320-4400-88AB-6960B019CEDE")]
[ComVisible(true)]
public interface ISimpleObject {
EchoObject testNest();
}
, который может вернуть другой COM-объект. Этот первый COM-объект использует реализацию IDispatch C# по умолчанию и работает нормально. EchoObject
- это та, в которой реализована пользовательская реализация IDispatch, поскольку она не является частью C# SDK, давайте посмотрим, как я ее определил
[ComImport]
[Guid("00020400-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDispatch {
int GetTypeInfoCount(out uint pctinfo);
int GetTypeInfo(uint iTInfo, int lcid, out IntPtr info);
int GetIDsOfNames(
ref Guid iid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] names,
int cNames,
int lcid,
[Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId);
int Invoke(
int dispId,
ref Guid riid,
int lcid,
System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags,
ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
[Out][MarshalAs(UnmanagedType.LPArray)] object[] result,
IntPtr pExcepInfo,
IntPtr puArgErr);
}
Я использую определенный интерфейс IDispatch в определении интерфейса IEchoObject.
[Guid("D3E9B530-DFD5-4B54-88B9-4FBC8B0926E2")]
[ComVisible(true)]
public interface IEchoObject: IDispatch {
string testint();
}
Затем реализация этого интерфейса должна реализовать ICustomQueryInterface, чтобы разрешить приведение IDispatch.
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IDispatch))]
[Guid("F625A833-3B4E-455D-90A9-89B1953147CD"), ComVisible(true)]
public class EchoObject : ReferenceCountedObject, IEchoObject, ICustomQueryInterface {
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv) {
Console.WriteLine("Are we a: " + iid + "?");
ppv = IntPtr.Zero;
if (typeof(IDispatch).GUID == iid)
{
Console.WriteLine("Yes");
ppv = Marshal.GetComInterfaceForObject(this, typeof(IDispatch), CustomQueryInterfaceMode.Ignore);
return CustomQueryInterfaceResult.Handled;
}
return CustomQueryInterfaceResult.NotHandled;
}
public int GetIDsOfNames(
ref Guid iid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] names,
int cNames,
int lcid,
[Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId
) {
bool unknown = false;
if(cNames == 1) {
Console.WriteLine("Getting id of " + names[0]);
if (names[0] == "testman") {
Console.WriteLine("Member found");
rgDispId[0] = 1;
} else {
rgDispId[0] = -1;
}
}
return unknown ? HRESULT.DISP_E_UNKNOWNNAME : HRESULT.S_OK;
}
}
Я, конечно, реализовал другие методы IDispatch, но я не верю, что они важны для моей проблемы. Обратите внимание, что GetIDsOfNames печатает на консоль.
Я могу создать начальный SimpleObject
просто отлично. Я также могу вызвать метод testNest
и получить экземпляр EchoObject
. Однако при поиске идентификатора testman
метода для этого EchoObject
клиент возвращает E_POINTER
(неверный указатель) из win32 вместо результата.
Я проверил его из Excel , VBScript и пользовательский COM-клиент C ++. Все получают ошибку Invalid Pointer , и клиент C ++ показывает, что это из GetIDsOfNames
. Как ни странно, хотя метод C# запускается, он с радостью выводит строку в консоль, возвращает, а затем C ++ COM-клиент получает ошибку.
Если отключить все мои пользовательские IDispatch и вместо этого использовать значение по умолчанию C# Реализация (вызов метода testint вместо testman) работает нормально. Я подозреваю, что мой Marshaling может быть где-то не так, но я смотрел на него часами, и я не вижу проблемы.
РЕДАКТИРОВАТЬ: я сделал еще один тест. Если я изменяю тип возвращаемого значения GetIDsOfNames с int на void, это работает. Что там происходит? Согласно MSDN тип возвращаемого значения - HRESULT (32-разрядное целое число со знаком), но когда я возвращаю это, я получаю ошибку.