Передайте буфер символов из .NET в COM и обновите его.

У меня есть следующий COM-метод, вызываемый из C#, который возвращает строку в предоставленном буфере pchText (который не обязательно завершается нулем) и количество символов, скопированных в pcch:

HRESULT Next([in, out] long* pcch, [out, size_is(*pcch)] OLECHAR* pchText);

Как определить подпись C# для взаимодействия?

До сих пор я пробовал это:

void Next(ref int pcch,
    [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)]
    System.Text.StringBuilder pchText);

Кажется, это работает, но я не уверен, влияет ли SizeParamIndex на StringBuilder.


person avo    schedule 27.07.2014    source источник
comment
Это метод, который вы определили, или что-то, что вы должны использовать?   -  person acelent    schedule 01.08.2014
comment
@PauloMadeira, подпись метода COM - это то, что я должен использовать. У меня есть собственный макет этого COM-объекта для модульного тестирования. Но теперь я использую char[] pchText в С#: stackoverflow.com/q/25039290/2674222   -  person avo    schedule 01.08.2014
comment
Очень жаль, более надежной и удаленной подписью будет HRESULT Next([in] cch, [out] long* pcch, [out, size_is(cch), length_is(*pcch)] OLECHAR* pchText);. Какой интерфейс/объект имеет этот метод?   -  person acelent    schedule 01.08.2014
comment
@PauloMadeira, это COM-библиотека DLL, предоставляемая сторонним поставщиком. Он используется только в процессе.   -  person avo    schedule 02.08.2014


Ответы (1)


Ну, это, безусловно, сложная функция для правильного вызова. Ваше объявление примерно в порядке, вам просто нужно применить атрибут [PreserveSig] и сделать тип возвращаемого значения int, чтобы вы могли обнаружить возвращаемое значение S_FALSE, указывающее, что следующего элемента нет.

Трудность заключается в том, чтобы заранее угадать, насколько велик StringBuilder для передачи. Собственный код получает необработанный указатель в кучу сборщика мусора, указывающий на буфер компоновщика, поэтому несчастные случаи довольно фатальны. Вы должны заранее определить правильную емкость для компоновщика и передать ее в качестве начального аргумента pcch.

Маршаллер делает обращает внимание на SizeParamIndex после возврата из функции. Будет скопировано ровно столько символов, сколько указывает ppch. Если по какой-либо причине он записывает больше, чем может поместиться в буфер, программа немедленно прервется с ExecutionEngineException, поскольку это указывает на то, что куча GC была повреждена.

Имейте в виду, что если вы догадаетесь о слишком низкой емкости, вы не обязательно сможете это обнаружить. Вы можете просто получить усеченную строку, когда функция копирует столько символов, сколько подходит, и не возвращает код ошибки. Лучший способ выяснить, является ли это проблемой, - просто протестировать это и намеренно передать небольшой билдер. Обратите внимание на возвращаемое значение.

Стоит отметить одну странность: сигнатура функции использует хак, который был обычным явлением в первые дни COM, фактически возвращая двоичные данные вместо текста через OLECHAR*. Сильный намек на то, что это так, поскольку строка не обязательно заканчивается нулем. Это не приведет к хорошему концу в .NET, данные будут повреждены при нормализации строки. И сбой вашей программы, когда данные совпадают с одним из суррогатных символов utf-16. Если это так, вам нужен короткий [] вместо StringBuilder.

person Hans Passant    schedule 27.07.2014
comment
Спасибо, хорошее замечание о возможных нескольких символах \0x0 в буфере. Могу ли я использовать такую ​​подпись [PreserveSig] int Next(ref int pcch, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)], char[] pchText)? Если на то пошло, есть ли принципиальная разница между char[] и short[]? - person avo; 27.07.2014
comment
Если это на самом деле двоичные данные, то использование char нецелесообразно. Маршаллер pinvoke также будет принимать 8-битные символы ansi, что не соответствует OLECHAR. - person Hans Passant; 27.07.2014
comment
Не было бы более разумно создать несколько оболочек C++/CLI в своем собственном небольшом проекте, чтобы догадаться, как это маршалировать? - person dbc; 30.07.2014