У меня есть теория, почему это происходит.Я подозреваю, что сортировка StringBuilder
включает в себя создание копии данных, передачу ее в вызов P / Invoke, а затем копирование обратно в StringBuilder
. Хотя на самом деле я не смог проверить это .
Единственная альтернатива этому - сначала сплющить StringBuilder
(это внутренне связанный список char[]
).и char[]
закреплено, и даже тогда это будет работать только для маршалинга в строки указателя на Unicode-chars, но не в строки ANSI или COM.
Таким образом, когда вы передаете StringBuilder
в качестве аргумента для .NET есть очевидное место, чтобы скопировать любые изменения обратно: сразу после возврата P / Invoke.
То же самое нельзя сказать, когда вы передаете делегат, возвращающий StringBuilder
.В этом случае .NET необходимо создать оболочку, которая преобразует функцию int => StringBuilder
в функцию int => char*
.Эта оболочка создаст буфер char*
и заполнит его, но, очевидно, пока не может скопировать какие-либо изменения обратно.Он также не может сделать это после функции, которую принимает , которую возвращает делегат: еще слишком рано!
На самом деле, нет никакого очевидного места, где могла бы произойти обратная копия.
Таким образом, я предполагаю, что именно это и происходит: при маршалинге StringBuilder
возвращающегося делегата .NET может выполнять только одностороннее преобразование, поэтому любые сделанные вами изменения не отражаются в StringBuilder
.Это немного лучше, чем полная неспособность маршалировать таких делегатов.
Что касается решений: я бы рекомендовал сначала спросить у нативного кода, насколько большим должен быть буфер, а затем передать буфер соответствующегоразмер во втором вызове.Или, если вам нужна более высокая производительность, угадать достаточно большой буфер, но позвольте нативному методу сообщить, что требуется больше места.Таким образом, большинство вызовов будет включать только один переход P / Invoke.
Это можно превратить в более удобную функцию, которую можно просто вызывать из управляемого мира, не беспокоясь о буферах.