|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Делаю на позднем связывании коллбэк из дотнетовский библиотеки в VBA. На VBA делаю класс с произвольным методом - Class1.OnComplete(x as String, y as Variant) Передаю его в библиотеку: obj.Execute New Class1 Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.
Сам по себе _callback.OnComplete("xxx", 123) работает, в VBA код исполняется, но GetMethod(methodName) ничего не возвращает. Соответственно, при попытке выполнить несуществующий метод вываливается exception "System.__ComObject" не содержит определения". А если сигнатура не соответствует передаваемым аргументам, то всякие другие ошибки. Но нужно проверить наличие метода и его сигнатуру до вызова. ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 16:45 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy GetMethod(methodName) ничего не возвращает Конечно не возвращает. Потому что если можно сделать Код: c# 1. 2.
то это еще не значит, что у объекта foo действительно есть метод Bar. Дайнемики могут использовать рефлекшен как механизм связывания, но совсем не обязательно. Можешь, например, посмотреть, как это в случае ExpandoObject делается. Ключевой момент там это интерфейс IDynamicMetaObjectProvider - когда объект его реализует, то динамическое связывание происходит с его помощью. Рефлекшен используется только как фоллбек если такого интерфейса у объекта нет. ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 17:06 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
ЯННП. Как проверить наличие метода и его сигнатуру у COM-объекта? ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 17:08 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Нашел такой класс: https://gist.github.com/AlbertoMonteiro/d2d25923cc2aa2941a93 Вроде делает что нужно, но заточен под fw 1.1, в более поздних нет TypeToTypeInfoMarshaler ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 18:17 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Разобрался с TypeToTypeInfoMarshaler, но это ничем не помогло. В тестовом примере создаются системные com-объекты, для которых создается соответствующий .net-тип, а в случае с объектом VBA создается typeof(object). ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 19:20 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy Как проверить наличие метода и его сигнатуру у COM-объекта? В случае позднего связывания - практически никак. Runtime callable wrapper генерится на лету при вызове метода какими очень глубоко спрятанными средствами CLR, причем там есть отличия от раннего связывания. ... |
|||
:
Нравится:
Не нравится:
|
|||
04.12.2020, 21:07 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Кстати, есть один вариант. Раз эта штука работает с поздним связыванием, то, скорее всего, она имплементирует IDispatch. Можно импортировать в код объявление этого интерфейса, привести к нему объект, а потом дернуть у результата GetTypeInfoCount и GetTypeInfo (см. https://docs.microsoft.com/en-us/windows/win32/api/oaidl/nn-oaidl-idispatch): Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42.
В консоли: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28.
Чтобы приведенный код работал, в референсы нужно добавить сборку ComMarshalers. Она нужна для использования TypeToTypeInfoMarshaler - стандартно IDispatch::GetTypeInfo возращает ссылку на COM-интерфейс ITypeInfo , но с ним возиться не особенно интересно, а этот маршаллер по данным ITypeInfo возращает нормальный дотнетовский Type. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.12.2020, 08:10 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Мда, по ссылке выше я и не ходил. TypeToTypeInfoMarshaler живет в отдельной сборке, см. выше. Прекрасно работает под фреймворком 4.7.2 (код выше). Antonariy а в случае с объектом VBA создается typeof(object) Т.е. реализации IDispatch нет, только IUnknown? Что за объект? COM-интерфейс у него вообще какой-нибудь реализован? ... |
|||
:
Нравится:
Не нравится:
|
|||
05.12.2020, 08:24 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны Мда, по ссылке выше я и не ходил. TypeToTypeInfoMarshaler живет в отдельной сборке, см. выше. Прекрасно работает под фреймворком 4.7.2 (код выше). Antonariy а в случае с объектом VBA создается typeof(object) Т.е. реализации IDispatch нет, только IUnknown? Что за объект? COM-интерфейс у него вообще какой-нибудь реализован? Это голый VBA-класс в экселе. И IDispatch он реализовывает, DispatchUtility.ImplementsIDispatch возвращает true. ... |
|||
:
Нравится:
Не нравится:
|
|||
06.12.2020, 18:06 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны Чтобы приведенный код работал, в референсы нужно добавить сборку ComMarshalers. Она нужна для использования TypeToTypeInfoMarshaler - стандартно IDispatch::GetTypeInfo возращает ссылку на COM-интерфейс ITypeInfo , но с ним возиться не особенно интересно, а этот маршаллер по данным ITypeInfo возращает нормальный дотнетовский Type. Однако dynamic способен вызывать их методы без каких-либо плясок с бубном. То есть способ получить имя метода VBA-объекта таки есть. Максимум, чего я пока добился - получил имя VBA-класса через TypeDescriptor.GetClassName. Но инфу о методах TypeDescriptor не возвращает (даже функционала такого нет), только о свойствах и событиях. ... |
|||
:
Нравится:
Не нравится:
|
|||
06.12.2020, 18:18 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy, А если так подумать, то зачем тебе нужен сам метод? Просто вызывай его и перехватывай ексепшен на случай если его нет. Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
... |
|||
:
Нравится:
Не нравится:
|
|||
06.12.2020, 19:30 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
fkthat Antonariy, А если так подумать, то зачем тебе нужен сам метод? Просто вызывай его и перехватывай ексепшен на случай если его нет. Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
Смысл в том, что это асинхронная обертка для синхронных COM-методов, то есть ВБА запускает в обертке метод, передавая коллбэк, и завершает работу, можно заняться в экселе другими делами. Метод долго что-то делает, а когда завершает, обертка дергает коллбэк и передает в него возвращаемое значение метода (если есть) или текст ошибки, а коллбэк тоже должен что-то сделать с ответом в экселе. Сейчас, если коллбэк реализован криво, то будет перехваченная ошибка, но выходит, что метод работал впустую, эксель коллбэк не получит. Поэтому нужно проверять сигнатуру заранее. Знаю, что можно реализовать интерфейс, о котором знает обертка, и он есть, но в задании сказано ясно: позднее связывание тоже должно работать. И оно работает, но я хочу, чтобы работало лучше. Следующая фича, которая не требуется, но которую хотелось бы сделать - передавать имя метода коллбэка. А вызов Invoke, который понимает только net-типы, обломается, точно так же не найдя у object указанного метода. ... |
|||
:
Нравится:
Не нравится:
|
|||
06.12.2020, 21:08 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy С этим я разобрался, но в качестве дотнетовского типа возвращается object. Я полагаю это из-за того, что VBA-объекты существуют только в памяти и не зарегистрированы в реестре. Однако dynamic способен вызывать их методы без каких-либо плясок с бубном. То есть способ получить имя метода VBA-объекта таки есть. Максимум, чего я пока добился - получил имя VBA-класса через TypeDescriptor.GetClassName. Но инфу о методах TypeDescriptor не возвращает (даже функционала такого нет), только о свойствах и событиях. Понятно. Да, этот TypeToTypeInfoMarshaler просто вызывает Marshal.GetTypeForITypeInfo , а там внутри делается попытка загрузки библиотеки типов, если попытка неудачна - возвращается typeof(object). Для пользовательского экселевского класса никакой библиотеки типов нет, поэтому возвращает object. Тогда остается хардкор с ITypeInfo. Отправная точка: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/681c87bf-cffc-4b18-8dc9-c23b31877880 Получаем вот такое: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98.
В экселе сделал класс TestClass с одной процедуркой: Код: vbnet 1. 2. 3.
и вызываю метод библиотечки (разумеется, предварительно её зарегистрировав): Код: vbnet 1. 2. 3. 4. 5.
В окошке Immediate VBA получаю: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9.
Последняя строка - от метода класса. При наличии у метода параметров их имена будут следовать после названия метода. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 10:39 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны, Теоретически, проверить наличие метода/свойства/события можно и без typelib, каким-либо образом вызвав IDispatch::GetIDsOfNames . Но как это сделать не прибегая к хакам с unmanaged кодом у меня никаких идей нет. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 11:02 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
fkthat Сон Веры Павловны, Теоретически, проверить наличие метода/свойства/события можно и без typelib, каким-либо образом вызвав IDispatch::GetIDsOfNames . Но как это сделать не прибегая к хакам с unmanaged кодом у меня никаких идей нет. Ну, если имя метода известно (а GetIDsOfNames мапит массив уже известных имен на массив с их DispId), то проще вызвать IDispatch::GetDispId. И по ссылке выше (на github) класс DispatchUtility уже содержит утильный метод TryGetDispId, который, если передать ему имя существующего метода, вернёт true и dispId этого метода, а если несуществующего - false. Есть один нюанс: при вызове из C# IDispatch::GetDispId для несуществующего имени выбрасывает COMException, т.к. возвращает, по сути, HRESULT - фреймворк сам делает проверку на S_OK, и в случае неудачной проверки сам выбрасывает исключение. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 11:39 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны И по ссылке выше (на github) класс DispatchUtility уже содержит утильный метод TryGetDispId Antonariy но заточен под fw 1.1, в более поздних нет TypeToTypeInfoMarshaler В том-то и засада, что, похоже, просто так привести __ComObject к IDispatch нельзя. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 11:57 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
fkthat Сон Веры Павловны И по ссылке выше (на github) класс DispatchUtility уже содержит утильный метод TryGetDispId Antonariy но заточен под fw 1.1, в более поздних нет TypeToTypeInfoMarshaler В том-то и засада, что, похоже, просто так привести __ComObject к IDispatch нельзя. 22242905 (в самом конце) - TypeToTypeInfoMarshaler перенесен в сборку CustomMarshallers. При её подключении всё прекрасно маршаллится и работает. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 12:03 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны 22242905 - всё прекрасно приводится. Код: c# 1.
TypeToTypeInfoMarshaler не поддерживается в .NET Core ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 12:31 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
fkthat Сон Веры Павловны 22242905 - всё прекрасно приводится. Код: c# 1.
TypeToTypeInfoMarshaler не поддерживается в .NET Core ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 12:41 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
fkthat TypeToTypeInfoMarshaler не поддерживается в .NET Core А класс Marshal там поддерживается? В частности, с методом GetTypeForITypeInfo? ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 12:42 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy В нем вообще COM поддерживается разве? Core же кроссплатформенный, где COM на других платформах?. Да вроде как вполне: https://docs.microsoft.com/en-us/dotnet/standard/native-interop/cominterop ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 12:44 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy В нем вообще COM поддерживается разве? Core же кроссплатформенный, где COM на других платформах?. Там даже класс Registry поддерживается изначально, и WinForms начиная с 3.1, просто оно на не-Win платформах работать не будет, хотя даже и вполне соберется. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 13:26 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Сон Веры Павловны Antonariy С этим я разобрался, но в качестве дотнетовского типа возвращается object. Я полагаю это из-за того, что VBA-объекты существуют только в памяти и не зарегистрированы в реестре. Однако dynamic способен вызывать их методы без каких-либо плясок с бубном. То есть способ получить имя метода VBA-объекта таки есть. Максимум, чего я пока добился - получил имя VBA-класса через TypeDescriptor.GetClassName. Но инфу о методах TypeDescriptor не возвращает (даже функционала такого нет), только о свойствах и событиях. Понятно. Да, этот TypeToTypeInfoMarshaler просто вызывает Marshal.GetTypeForITypeInfo , а там внутри делается попытка загрузки библиотеки типов, если попытка неудачна - возвращается typeof(object). Для пользовательского экселевского класса никакой библиотеки типов нет, поэтому возвращает object. Тогда остается хардкор с ITypeInfo. Отправная точка: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/681c87bf-cffc-4b18-8dc9-c23b31877880 Получаем вот такое: Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98.
В экселе сделал класс TestClass с одной процедуркой: Код: vbnet 1. 2. 3.
и вызываю метод библиотечки (разумеется, предварительно её зарегистрировав): Код: vbnet 1. 2. 3. 4. 5.
В окошке Immediate VBA получаю: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9.
Последняя строка - от метода класса. При наличии у метода параметров их имена будут следовать после названия метода. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.12.2020, 20:55 |
|
Проверить наличие метода в dynamic + System.__ComObject
|
|||
---|---|---|---|
#18+
Antonariy А типы аргументов как получить? Да всё так же, вознёй с ITypeInfo. Класс в аттаче - много строк, используется как-то так: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31.
Для нормальных зарегистрированных типов выдается вполне вменяемая информация - например, для метода Scripting.FileSystemObject.CreateTextFile Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
(информация о типе может быть рекурсивной, т.к. если используется ссылочный тип, то это VT_PTR, т.е. ссылка на другой тип). А вот для методов класса из VBA выдаётся фигня: Код: vbnet 1. 2. 3.
Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
Поэтому, по-моему, в данном случае уместнее было бы в дотнетовской библиотеке объявить COM-интерфейс для коллбэка: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
и реализовывать его в VBA-классе: Код: vbnet 1. 2. 3. 4. 5. 6. 7. 8.
Тогда, если метод, принимающий объект с коллбэком, объявит в сигнатуре тип аргумента как ICallback: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9.
то при попытке передать в этот метод из VBA объект, не реализующий ICallback, на рантайме вылетит ошибка "Run-time error '13': Type mismatch". А если даже оставить сигнатуру с типом dynamic, то фреймворк вполне может проверить, реализует ли объект ICallback, и привести его к интерфейсу: Код: sql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
- в окошке Immediate будет Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.
... |
|||
:
Нравится:
Не нравится:
|
|||
08.12.2020, 13:29 |
|
|
start [/forum/topic.php?fid=20&msg=40024824&tid=1398406]: |
0ms |
get settings: |
11ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
41ms |
get topic data: |
12ms |
get forum data: |
3ms |
get page messages: |
58ms |
get tp. blocked users: |
1ms |
others: | 267ms |
total: | 415ms |
0 / 0 |