Понимание поведения COM / WSH - IDispatch _Default и Item с поздним связыванием?

Я пытаюсь воспроизвести поведение, которое я наблюдаю в JScript, на C #. Я использую IDispatch для перечисления членов и вызова их для объектов с поздним связыванием. Я полный новичок в C ++ и знаю о COM достаточно, чтобы быть очень опасным. Вот мои вопросы:

  • Всегда ли DISPID_VALUE равен нулю (0)? (Кажется, да)
  • Когда при вызове COM-объектов мне следует обращаться к члену DISPID_VALUE? (что-то вроде, когда сам интерфейс индексируется или вызывается ...?)
  • Есть ли какие-то правила / подсказки, когда вызывать .Item?
  • Почему в приведенном ниже примере BindingFlags.SetProperty работает с .Cells (x, x) (в отличие от BindingFlags.InvokeMethod)? Он вызывает _Default (x, x)? Пункт (x, x)? Откуда он знает, как это сделать? Как я могу узнать, куда он звонит?
  • Есть ли хорошая документация по вызову COM-объектов IDispatch с поздним связыванием?

В приведенном ниже примере в ячейке 1,1 электронной таблицы Excel задано значение некоторого текста и выделено жирным шрифтом.

Рассмотрим следующий WSH JScript:


var objExcel = new ActiveXObject("Excel.Application");
objExcel.Workbooks.Add();
objExcel.Visible = true;
objExcel.Cells(1,1).Value = "some test value";
objExcel.Cells(1,1).Font.Bold = true;

Этот код C # дает тот же результат (да, извините, он очень подробный):


Type axType = Type.GetTypeFromProgID("Excel.Application");
object objExcel = Activator.CreateInstance(axType);
object workbooks = objExcel.GetType().InvokeMember("Workbooks", System.Reflection.BindingFlags.GetProperty, null, objExcel, null);
objExcel.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, objExcel, new object[] { true });
workbooks.GetType().InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, workbooks, new object[] { true });
object cell = objExcel.GetType().InvokeMember("Cells", System.Reflection.BindingFlags.GetProperty, null, objExcel, new object[] { 1, 1 });
cell.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, cell, new object[] { "some test value" });
object font = cell.GetType().InvokeMember("Font", System.Reflection.BindingFlags.GetProperty, null, cell, null);
font.GetType().InvokeMember("Bold", System.Reflection.BindingFlags.SetProperty, null, font, new object[] { true });

Когда у меня появляется время, я планирую попытаться узнать больше об этом, если JScript вызовет класс C # COM, который я бы создал с ведением журнала / отладкой.


person aikeru    schedule 18.06.2012    source источник


Ответы (1)


Да, для параметра DISPID_VALUE # определено значение 0 в oaidl.idl. Если присвоить свойству dispip значение 0, оно станет свойством по умолчанию. Многие языки позволяют опускать имя свойства по умолчанию. Эквивалент индексатора C #.

Это просто соглашение, и конечно же, не требуется, чтобы интерфейс, производный от COM IDispatch, предоставлял свойство по умолчанию. Также свойство не должно называться «Value». Это обычное дело. Индексатор интерфейса [ComVisible] C # получит dispid 0, но с именем «Item». Вокруг много других вариаций, вы не можете ничего предположить.

BindingFlags.SetProperty работает, потому что член Cells является свойством, а не методом. Это немного похоже на метод только потому, что это свойство проиндексировано. Это практически не поддерживается в C # (только для индексатора), но не ограничено в COM или VB.NET. В вашем примере кода используется интерфейс COM IDispatch, он позволяет искать членов по имени. IDispatch :: GetIDsOfNames () выполняет это, сопоставляя строку с числом (dispid), когда затем может использоваться для вызова свойства или метода с помощью IDispatch :: Invoke (). Обратите внимание, что это работает только в одном направлении, от имени к номеру. IDispatch не поддерживает аналог Reflection.

Избавьтесь от уродливого кода C #, написав его на VB.NET или используя ключевое слово dynamic в C # версии 4.

person Hans Passant    schedule 18.06.2012
comment
Спасибо, Ганс! Это мой второй вопрос по COM, на который вы ответили :) Я тоже видел вашу работу по другим вопросам! Я желаю усвоить все ваши знания ... Мне все еще интересно, как ведет себя WSH. IE: в Scripting.FileSystemObject .Drives выполнение WScript.Echo (x); на конкретном диске выдает букву диска, которую, как я полагаю, он знает через DISPID_VALUE. Мне было интересно, задокументировано ли где-нибудь подобное поведение, чтобы я мог иметь максимально широкую совместимость с COM-объектами. Вы знаете что-нибудь об этом аспекте или где я могу узнать о нем больше? - person aikeru; 18.06.2012
comment
Кроме того, я, кажется, обнаружил, что может существовать какой-то стандарт для перечисления с использованием диспидов после выполнения поиска в Google, но я не нашел подробностей ... так что я тоже заинтересован в этом, если вы знаете. Я ценю вашу помощь :) Надеюсь, что и другие тоже. - person aikeru; 18.06.2012
comment
Это не задокументировано для функции Echo. Но конечно, вполне вероятно, что он использует свойство по умолчанию. Свойство по умолчанию для объекта Drive также не задокументировано, но вы можете узнать, просмотрев библиотеку типов. Из командной строки VS запустите oleview.exe c: \ windows \ system32 \ cscript.exe - person Hans Passant; 18.06.2012
comment
Правильно, у меня есть код C #, который просматривает cFuncs и cVars TYPEATTR в поисках того, что соответствует dispid 0. Думаю, я как-то надеялся найти документацию по этому соглашению ... - person aikeru; 18.06.2012
comment
Спасибо за совет по oleview - мне нужно будет в Google, как получить эту настройку, и проверить ее .. - person aikeru; 18.06.2012
comment
Я нашел инструмент здесь: microsoft.com/en-us/ download / confirm.aspx? id = 7007 и это очень круто! - person aikeru; 18.06.2012