Спрашивающий
CLR Hosting in C++ : how to use Type.InvokeMember with out Parameter

Общие обсуждения
-
использую пример отсюда http://code.msdn.microsoft.com/CppHostCLR-e6581ee0/sourcecode?fileId=21953&pathId=1366553273
мне нужно вызвать:
spType->InvokeMember()
с указанием параметра modifiers, используется самая расширенная версия:
virtual HRESULT __stdcall InvokeMember (
какой бы тип массива я не указывал, выходит ошибка:
/*[in]*/ BSTR name,
/*[in]*/ enum BindingFlags invokeAttr,
/*[in]*/ struct _Binder * Binder,
/*[in]*/ VARIANT Target,
/*[in]*/ SAFEARRAY * args,
/*[in]*/ SAFEARRAY * modifiers,
/*[in]*/ struct _CultureInfo * culture,
/*[in]*/ SAFEARRAY * namedParameters,
/*[out,retval]*/ VARIANT * pRetVal ) = 0;0x80131533 : Обнаружено несоответствие между типом массива среды выполнения и подтипом, записанным в метаданных.
также нашел что есть тип ParameterModifier вот его описание:
struct __declspec(uuid("11d31042-14c0-3b5c-87d0-a2cd40803cb5"))
ParameterModifier
{
SAFEARRAY * _byRef;
};по идее modifiers - это массив из одного элемента типа ParameterModifier, получается мне нужно создать SAFEARRAY и добавить в него ParameterModifier, не пойму как это нужно сделать.
http://msdn.microsoft.com/en-us/library/system.reflection.parametermodifier%28v=vs.110%29
вот такой же вопрос без ответа(может тут будет понятнее описано): http://stackoverflow.com/questions/19073661/clr-hosting-in-c-how-to-use-type-invokemember-with-out-parameter
12 августа 2014 г. 6:42
Все ответы
-
Соответствующий параметр метода Type::InvokeMember является массивом структур ParameterModifier. Следовательно SAFEARRAY должен содержать элементы такого типа (ParameterModifier). В приведенной мной статье есть пример заполнения этой структуры (на C#), сделайте нечто подобное на C++.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
12 августа 2014 г. 9:58 -
Соответствующий параметр метода Type::InvokeMember является массивом структур ParameterModifier. Следовательно SAFEARRAY должен содержать элементы такого типа (ParameterModifier). В приведенной мной статье есть пример заполнения этой структуры (на C#), сделайте нечто подобное на C++.
как это сделать на c# и так понятно, мне нужно это сделать на native c++. Кажется я уже приближаюсь к решению, сделать нужно как-то так:
IRecordInfo *RecInfo = NULL; // надо эту переменную заполнить на основании структуры ParameterModifier Modifiers = SafeArrayCreateVectorEx(VT_RECORD, 0, 1, (PVOID)RecInfo);
осталось понять как заполнить IRecordInfo, есть функция GetRecordInfoFromGuids, но не понятно что передавать в качестве первого параметра, пробовал подставлять __uuidof(__mscorlib), но пишет что:
0x8002801d: Библиотека не зарегистрирована
- Изменено medig 12 августа 2014 г. 17:05
12 августа 2014 г. 17:04 -
Это, видимо, из-за того, что Вы передаете неверный номер версии библиотеки типа. Нужно вот так:
IRecordInfo *ri; HRESULT hr = ::GetRecordInfoFromGuids(__uuidof(__mscorlib), 2, 0, 0, __uuidof(ParameterModifier), &ri);
Это для .NET FW 2.0, для 4.0 задайте версию 2.4. Так, по крайней мере, выглядит регистрационная информация в реестре.Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
12 августа 2014 г. 18:06 -
Да так работает, но параметр все равно почему-то не возвращается.
Метод реализован так:
public void Test(ref int Param) { Param = 666; }
проверил вызовом из c#, параметр возвращается.
теперь на с++ пишу так:
SAFEARRAY *psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
// далее заполнение этого массива
в метод параметр передается, а вот обратно не возвращается, может нужно использовать как-то VT_BYREF?
вот так заполняю modifiers:
ParameterModifier *ParamModifier = NULL; IRecordInfo *RecInfo = NULL; hr = GetRecordInfoFromGuids(__uuidof(__mscorlib), 2, 4, 0, __uuidof(ParameterModifier), &RecInfo); if (FAILED(hr)) { goto Clean; } Modifiers = SafeArrayCreateVectorEx(VT_RECORD, 0, 1, (PVOID)RecInfo); SafeArrayAccessData(Modifiers, (PVOID*)&ParamModifier); ParamModifier[0]._byRef = SafeArrayCreateVector(VT_BOOL, 0, SizeArray); bool *b = NULL; SafeArrayAccessData(ParamModifier[0]._byRef, (PVOID*)&b); b[0] = true; SafeArrayUnaccessData(ParamModifier[0]._byRef); SafeArrayUnaccessData(Modifiers);
- Изменено medig 12 августа 2014 г. 18:17
12 августа 2014 г. 18:13 -
Покажите полный код вызова метода, т.е. что конкретно Вы передаете InvokeMember и как формируете эти параметры.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
12 августа 2014 г. 18:16 -
теперь на с++ пишу так:
SAFEARRAY *psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
в метод параметр передается, а вот обратно не возвращается, может нужно использовать как-то VT_BYREF?
Это Вы о самом параметре говорите, а модификаторы (modifiers) как передаете. Признак, что параметр передается по ссылке, функция получила?
Флаг VT_BYREF используется для передачи указателей. Думаю, здесь он не уместен.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
12 августа 2014 г. 18:23 -
параметры заполняются так:
variant_t* var = NULL; VariantInit(var); V_VT(var) = VT_I4; V_I4(var) = 5; SafeArrayPutElement(MethodArgs, 0, var);
выполнение метода:
hr = NETType->InvokeMember( MethodName, static_cast<BindingFlags>(BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public), NULL, NETObject, MethodArgs, Modifiers, NULL, NULL, &RetVal);
завершается S_OK
- Изменено medig 12 августа 2014 г. 18:31
12 августа 2014 г. 18:30 -
Явных ошибок не вижу, кроме bool (это тип C++, а не OLE). Лучше использовать VARIANT_BOOL. Выложите куда-нибудь проект, проще разбираться на готовом коде, а не писать самому пример :)
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
12 августа 2014 г. 18:39 -
Явных ошибок не вижу, кроме bool (это тип C++, а не OLE). Лучше использовать VARIANT_BOOL. Выложите куда-нибудь проект, проще разбираться на готовом коде, а не писать самому пример :)
Спасибо, попробую тип VARIANT_BOOL.
Этот проект - это dll для 1С, чтобы можно можно было расширять её функциональность написанием на языках .NET. После 1С и c# очень уж тяжко писать на с++)). Если и сейчас не получится, тогда завтра подготовлю тестовый проект.
12 августа 2014 г. 18:45 -
Похоже, параметр modifiers у InvokeMember вообще не используется. Я добавил еще один перегруженный метод TestOutParameter(int Param), т.е. без ref. Стабильно вызывается он, а другой игнорируется. Аргумент передавал по разному (с VT_BYREF и без) - никакой реакции :(
Попробую "покопать" дальше. О результатах отпишусь.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
13 августа 2014 г. 15:13 -
тут написано:
modifiers Тип: array<System.Reflection::ParameterModifier> Массив объектов ParameterModifier, представляющих атрибуты, связанные с соответствующим элементом в массиве args. Атрибуты, связанные с параметром, хранятся в сигнатуре члена. Связыватель по умолчанию обрабатывает этот параметр только при вызове COM-компонента.
интересно, в данном случае наш объект является COM-компонентой или нет? Если нет, то в таком случае должно работать без параметра modifiers, или может есть другой способ получение out-параметров...
и вот что еще там написано:
ParameterModifier используется только при вызове посредством COM-взаимодействия. При этом обрабатываются только параметры, переданные по ссылке.
т.е. всё таки кажется нужно использовать VT_BYREF, не подскажете в каком месте его надо использовать?
- Изменено medig 13 августа 2014 г. 15:51
13 августа 2014 г. 15:47 -
Я тоже думал об этом. Формально не является. Речь, видимо, идет о вызове метода COM-компонента из .net кода. В этом случае "связыватель" по умолчанию уже не подходит. Нужен свой?
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
13 августа 2014 г. 15:54 -
тоже попробовал передавать по ссылке:
V_VT(&vtArg) = VT_I4 | VT_BYREF; V_I4REF(&vtArg) = new LONG(8);
в метод приходит то что нужно, после вызова InvokeMember хранится тоже самое значение, что и до вызова.
буду теперь юзать параметр binder, правда я пока не понял, что с ним нужно делать.
13 августа 2014 г. 16:54 -
почитал про binder: нужно наследоваться от объекта System.Reflection.Binder и переопределять его методы, но как это сделать из с++, я этого не понимаю. Возможно этот объект нужно описывать в той же компоненте NET, откуда и создается объект.13 августа 2014 г. 18:11
-
Можно попробовать реализовать его на стороне Вашей библиотеки (на C#), а в C++ создать экземпляр CreateInstance-ом и "подсунуть" его InvokeMember.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
13 августа 2014 г. 18:20 -
Это не подходит, потому что библиотеки .NET будет писать кто-то другой. Мне проще тогда написать на c# свой прокси-объект, в котором уже будет загружаться .NET сборка и выполняться её методы, изменяться свойства и т.д.14 августа 2014 г. 6:20
-
Реализовал Binder на C#. Не помогает. Видимо, его основное назначение - выбор правильной версии перегруженного метода и (или) преобразование типов аргументов, если требуется. Самое интересное, что после того, как метод Вашего класса изменил ref-параметр в Binder попадает измененное значение аргумента, а в C++ не возвращается. Судя по всему маршалинг данных назад не выполняется, почему - не ясно.
Может Вам обойтись возвращаемым значением вместо ref-параметра? Это, к счастью, работает.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
14 августа 2014 г. 18:32 -
Может Вам обойтись возвращаемым значением вместо ref-параметра? Это, к счастью, работает
в каких-то случаях этого будет достаточно, а в каких то и нет, потому что есть ограничение по типам.
Я пробовал на c# использовать InvokeMember, там все работает и без всяких modifiers (как и написано в документации). Кажется и тут должно работать, тем более возвращаемое значение умеет обратно конвертироваться в Variant.
14 августа 2014 г. 19:33 -
Спасибо Вам за помощь!14 августа 2014 г. 19:34
-
Пожалуйста. Самому было интересно "пощупать" такой способ взаимодействия неуправляемого и управляемого кода.
В принципе, есть другой вариант: зарегистрировать библиотеку C# как компонент COM. Больших изменений в коде не потребуется. Нужно будет просто расставить "правильные" атрибуты у класса, методов, параметров (если потребуется). Тогда из C++ с ней можно будет общаться стандартным COM-API без использования хостинга CLR. В документации MSDN можно найти описание такой методики. Подумайте.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
15 августа 2014 г. 11:31 -
Пожалуйста. Самому было интересно "пощупать" такой способ взаимодействия неуправляемого и управляемого кода.
В принципе, есть другой вариант: зарегистрировать библиотеку C# как компонент COM. Больших изменений в коде не потребуется. Нужно будет просто расставить "правильные" атрибуты у класса, методов, параметров (если потребуется). Тогда из C++ с ней можно будет общаться стандартным COM-API без использования хостинга CLR. В документации MSDN можно найти описание такой методики. Подумайте.
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
- Изменено medig 15 августа 2014 г. 11:43
15 августа 2014 г. 11:41 -
вот что вычитал:
Предоставляет неуправляемому коду доступ к открытым элементам класса System.Type.
- modifiers
- Тип:
System.Reflection.ParameterModifier[]
Массив объектов ParameterModifier, представляющих атрибуты, связанные с соответствующим элементом в массиве args. Атрибуты, связанные с параметром, хранятся в сигнатуре члена. Связыватель по умолчанию не обрабатывает этот параметр.
а мы ведь как раз используем:
_TypePtr spType
т.е. получается и не должно что ли было ничего работать?
- Изменено medig 15 августа 2014 г. 12:02
15 августа 2014 г. 12:01 -
"Связыватель по умолчанию не обрабатывает", а свой Binder может. Но я повторяю: проблема в маршалинге (конкретно, возврате) данных из управляемого кода в неуправляемый. Может массив аргументов по другому нужно формировать - не знаю :(
Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!
15 августа 2014 г. 12:05