none
CLR Hosting in C++ : how to use Type.InvokeMember with out Parameter RRS feed

  • Общие обсуждения

  • использую пример отсюда 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
  • выложил сюда , проект для VS 2012, смотреть RuntimeHostV4.cpp в проекте CppHostCLR.

    Добавлю: вызывается метод "TestOutParameter" объекта CSSimpleObject из второго проекта.

    • Изменено medig 13 августа 2014 г. 5:39
    13 августа 2014 г. 5:35
  • Похоже, параметр 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 можно найти описание такой методики. Подумайте.


    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

    Этот способ я знаю, и он мне очень нравится, но у него есть минус, что его нужно устанавливать. Суть в том, что эта dll которую я делаю, это фактические плагин к 1С: Предприятие, 1С его умеет использовать без установки. Проблема в том, 1С умеет подключать плагины только из нативных библиотек. Вот захотелось сделать плагин, с помощью которого можно будет использовать .NET сборки в 1С. В 1С также можно использовать com-объекты, в т.ч. и сделанные на платформе .NET, но 1С сама не сможет их зарегистрировать, кроме того есть проблемы при использовании com-объектов - она их не умеет отпускать почему-то, поэтому чтобы обновить com-объект, нужно завершить процесс 1С, а часто это целый сервер приложений, что не всегда получается сделать.


    • Изменено medig 15 августа 2014 г. 11:43
    15 августа 2014 г. 11:41
  • вот что вычитал:

    _Type - интерфейс :

    Предоставляет неуправляемому коду доступ к открытым элементам класса System.Type.

    _Type.InvokeMember :

    modifiers
    Тип: System.Reflection.ParameterModifier[]
    Массив объектов ParameterModifier, представляющих атрибуты, связанные с соответствующим элементом в массиве args. Атрибуты, связанные с параметром, хранятся в сигнатуре члена. Связыватель по умолчанию не обрабатывает этот параметр.

    а мы ведь как раз используем:

    _TypePtr spType
    т.е. получается и не должно что ли было ничего работать?



    • Изменено medig 15 августа 2014 г. 12:02
    15 августа 2014 г. 12:01
  • "Связыватель по умолчанию не обрабатывает", а свой Binder может. Но я повторяю: проблема в маршалинге (конкретно, возврате) данных из управляемого кода в неуправляемый. Может массив аргументов по другому нужно формировать - не знаю :(

    Если сообщение помогло Вам, пожалуйста, не забудьте отметить его как ответ данной темы. Удачи в программировании!

    15 августа 2014 г. 12:05