C ++ / CLI marshal_context собственные строки повреждены - PullRequest
0 голосов
/ 01 мая 2020

Ниже приведен код C ++ / CLI, который компилируется в DLL и вызывается приложением C#:

void Foo(String^ strManaged) {
    marshal_context^ context = gcnew marshal_context();
    FooUnmanaged(context->marshal_as<const char*>(strManaged));
}

FooUnmanaged() читает const char*, выполняет некоторую обработку, которая занимает около секунду, а затем снова читает const char*, например:

void FooUnmanaged(const char* str) {
    // 1
    Log(str);

    // Process things unrelated to 'str'
    // ...

    // 2
    Log(str);
}

В некоторых случаях содержимое str изменяется между первым и вторым чтением внутри FooUnmanaged(), как будто эта память был повторно использован для некоторых других целей. Это происходит независимо от обработки, выполняемой в FooUnmanaged(), если это занимает заметное количество времени (я полагаю, достаточно долго, чтобы у G C была возможность сработать).

Этого не происходит, если Foo написано так или иначе

void Foo(String^ strManaged) {
    marshal_context^ context = gcnew marshal_context();
    FooUnmanaged(context->marshal_as<const char*>(strManaged));
    delete context; // addded
}

или так

void Foo(String^ strManaged) {
    marshal_context context; // created on the stack
    FooUnmanaged(context.marshal_as<const char*>(strManaged));
}

Неверный исходный код? Почему он неправильно резервирует память const char* на время жизни context? Или время жизни context может быть короче, чем я думаю (область действия Foo())?

1 Ответ

0 голосов
/ 05 мая 2020

Ответ @HansPassant:

Да, это проблема на всю жизнь. . NET использует агрессивный сборщик, он не знает, что нативный код опирается на контекст. Первый фрагмент требует GC::KeepAlive(context); в конце. Последний фрагмент - это то, как он должен был использоваться, семантика стека эмулирует RAII, автоматически сгенерированный вызов Dispose() поддерживает его таким же образом. И избегает временной утечки памяти. Если FooUnmanaged() хранит переданный указатель, вы не можете использовать marshal_context.

Это подтверждается этой статьей :

Время жизни [локальных переменных] может зависеть от способа построения программы. В отладочных сборках локальная переменная сохраняется до тех пор, пока метод находится в стеке. В выпусках сборки JIT может посмотреть на структуру программы, чтобы определить последнюю точку в пределах выполнения, которую переменная может использовать в методе, и отбросит ее, когда она больше не требуется.

...