2015-02-02 1 views
-1

У меня есть буфер, который я выделяю в C# и переходя к собственному коду. Я затем сохранить указатель на этот буфер:Использование управляемого буфера в нативном коде после вызова возвращает

Заголовок Определения

// called when a meter is attached 
typedef void(__cdecl * lpfnMeterAttachCallback)(); 

extern lpfnMeterAttachCallback frMeterAttachCallback; 

// called when a meter is detached 
typedef void(__cdecl * lpfnMeterDetachCallback)(); 

extern lpfnMeterAttachCallback frMeterDetachCallback; 


// called when a meter is detached 
typedef void(__cdecl * lpfnMeterReadCompleteCallback)(); 

extern lpfnMeterReadCompleteCallback frMeterReadCompleteCallback; 

extern char* frbuffer; 

Main

lpfnMeterAttachCallback frMeterAttachCallback; 
lpfnMeterDetachCallback frMeterDetachCallback; 
lpfnMeterReadCompleteCallback frMeterReadCompleteCallback; 
char* frbuffer; 

extern "C" __declspec(dllexport) void __cdecl InitDriver(
    lpfnMeterAttachCallback meterAttachCallback, 
    lpfnMeterDetachCallback meterDetachCallback, 
    lpfnMeterReadCompleteCallback meterReadCompleteCallback, char* buffer) 
{ 
    frMeterAttachCallback = meterAttachCallback; 
    frMeterDetachCallback = meterDetachCallback; 
    frMeterReadCompleteCallback = meterReadCompleteCallback; 
    frbuffer = buffer; 
... 
} 

, а затем в фоновом режиме вызова, заполнить его, как так:

JSONValue testSS = ss->GetJSON(); 
std::string help = *testSS; 
strcpy(frbuffer, help.c_str()); 
if (frMeterReadCompleteCallback) frMeterReadCompleteCallback(); 

Howeve r моя строка возвращается пустой или как поврежденные данные. В идеале я хотел, чтобы строка передавалась моему делегату, а затем использовала ее, но столкнулась с проблемами InterOp, пытаясь это сделать.

C#

[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern void InitDriver(MeterAttachCallback meterAttachCallback, 
     MeterDetachCallback meterDetachCallback, 
     MeterReadCompleteCallback meterReadCompleteCallback, StringBuilder sb); 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void MeterAttachCallback(); 

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void MeterDetachCallback(); 


[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void MeterReadCompleteCallback(); 

static public void OnMeterAttach() 
{ 
    Console.WriteLine("OnMeterAttach"); 
} 

static public void OnMeterDetach() 
{ 
    Console.WriteLine("OnMeterDetach"); 
} 

static StringBuilder sb = new StringBuilder(10 * 1024 * 1024); //10MB 

static public void OnMeterReadComplete() 
{ 
    Console.WriteLine("OnMeterReadComplete; JSON:", sb.ToString()); 
} 

static void Main(string[] args) 
{ 
    InitApolloCppDriver(OnMeterAttach, OnMeterDetach, OnMeterReadComplete, sb); 

    Console.ReadLine(); 
} 

я пишу/чтение буфера неправильно, или я должен быть реализации этого по-другому?

+0

Маршаллер в .NET будет хранить память, используемую 'sb' pinned (так что неуправляемый код может получить к ней доступ) в течение всего времени вызова InitEriver, так что это не сработает. Есть способы сделать это вручную, но это сложно. Передача строки в обратных вызовах выглядит лучше (и проще). Он будет работать, если вы получите правильную сортировку. – Chris

+0

Вы не можете надеяться получить ссылку на эту память и ожидать, что она будет означать что-либо после функции, которая была передана ссылкой. Думаю об этом. Как управляемый код освобождает память, если он не знает, что неуправляемый код будет содержать ссылку на него. Вам нужно переосмыслить это. Начните с удаления буфера.Это бесполезно. –

+0

Метод InitDriver постоянно запускается после вызова, действуя как сервер. Обратный вызов возникает, когда выполняется «сервер», используя буфер, переданный функции сначала, заполняя его, а затем вызывает обратный вызов в ожидании следующего запроса. –

ответ

3

Да, это не может работать должным образом. Маршрутизатор pinvoke имеет встроенные знания о StringBuilder. Когда вы обычно используете его, он сначала связывает базовый буфер, поэтому он не может двигаться, пока работает собственный код. Затем он вызывает вызов собственного кода, и после завершения этого вызова он непосредственно манипулирует членами объекта StringBuilder, чтобы сделать его jive с собственной строкой.

Ничего из этого не происходит в вашем случае, вечеринка на буфере после возвращенный маркерщик pinvoke. Другими словами, вы пишете буфер, адрес которого не гарантированно стабилен. Это очень, очень плохой, отладка поврежденной GC-кучи очень несправедлива. В этом конкретном случае вам удается уйти от него, буфер слишком велик, в следующий раз вам не повезет.

И, конечно, внутренние члены StringBuilder не обновляются, получение пустой строки - ожидаемый результат.

И основной причиной, по которой вы не должны этого делать, не выполняется преобразование из 8-разрядных символов C-строки, которые вы пишете (предполагается, что кодировка ANSI), в кодовые точки Unicode utf-16, хранящиеся в StringBuilder. Другими словами, это вовсе не оптимизация, строка всегда должна быть скопирована. Повреждение данных, которое вы видите, является побочным эффектом другого кода, повторно использующего память, ранее занятую временным неуправляемым буфером. Вы еще не поняли, где вы, в свою очередь, испортили память, скажем, растрового изображения. Разумеется, вполне естественно.

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

+0

«Тебе нужно выбросить это». Это так смешно. С этого момента это будет стандартная фраза. – usr

Смежные вопросы