System.String interop BSTR
От: TopSpace  
Дата: 07.12.10 12:05
Оценка:
Есть C# dll, предназначенная для вызовов из неуправляемого кода через COM (интерфейс и класс):

interface IFoo
{
    string GetResult(string str);
}

public class Foo : IFoo
{
    public string GetResult(string str)
    {
        string result = ....  // создаем объект типа string и возвращаем его как результат
        return result;  
    }
}


При импорте типов метод превращается в следующее:

virtual HRESULT FooNamespace::IFoo::GetResult(BSTR str, BSTR* pRetVal) = 0;



Соответственно, в С++ дергается так:

#import "Foo.tlb" named_guids raw_interfaces_only
....

FooNamespace::IFooPtr pFoo;
HRESULT hRes = pFoo.CreateInstance(FooNamespace::CLSID_Foo);

if (hRes == S_OK)
{
    CComBSTR val = ....;

    BSTR strResult = NULL;  
    pFoo->GetResult(val, &strResult);
    CString result = strResult;
  ...
}
...


Все работает.

Вопрос в том, что происходит с памятью, выделяемой внутри C#-метода. Объект string создается и возвращается в С++, где "превращается" в BSTR. Происходит глубокое копирование, или этот BSTR есть тот же адрес, что и оригинальный string? Но ведь за очистку с одной стороны отвечает GC, с другой — на стороне С++ нужно явно вызвать какую-то очистку..
В общем вопрос, как правильно вернуть строку из C# в С++, чтобы не было никаких ликов и прочих граблей...

заранее спасибо за помощь.
Re: System.String interop BSTR
От: Jolly Roger  
Дата: 07.12.10 12:30
Оценка: 4 (1)
Здравствуйте, TopSpace, Вы писали:

Блок памяти, переданный по ссылке клиенту, переходит под ответственность клиента, и им должен быть освобождён. Это общее правило COM, так как сервер не может знать, когда эту память можно будет освободить. Ну а память из управляемой кучи тем более не может быть отдана в распоряжение нативного кода.
"Нормальные герои всегда идут в обход!"
Re[2]: System.String interop BSTR
От: TopSpace  
Дата: 07.12.10 12:45
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, TopSpace, Вы писали:


JR>Блок памяти, переданный по ссылке клиенту, переходит под ответственность клиента, и им должен быть освобождён. Это общее правило COM, так как сервер не может знать, когда эту память можно будет освободить. Ну а память из управляемой кучи тем более не может быть отдана в распоряжение нативного кода.



Спасибо за ответ,
но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи.. Как все-таки правильно вернуть string из C# в C++? Получив через BSTR как-то его удалить?
Re[3]: System.String interop BSTR
От: hardcase Пират http://nemerle.org
Дата: 07.12.10 13:01
Оценка:
Здравствуйте, TopSpace, Вы писали:

TS>Спасибо за ответ,

TS>но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи..

Указатели на управляемые объекты могут уходить в неуправляемый код только когда вызывающая сторона — управляемый код. В остальных случаях память копируется рантаймом.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: System.String interop BSTR
От: Jolly Roger  
Дата: 07.12.10 13:02
Оценка:
Здравствуйте, TopSpace, Вы писали:

TS>Спасибо за ответ,

TS>но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи.. Как все-таки правильно вернуть string из C# в C++? Получив через BSTR как-то его удалить?

Да всё просто, по-моему. Вы получили копию строки в блоке неуправляемой памяти, выделенном через CoTaskMemAlloc, и должны её освободить с помощью CoTaskMemFree

Если конечно NET придерживается правил COM, но это наверняка так.
"Нормальные герои всегда идут в обход!"
Re[4]: System.String interop BSTR
От: TopSpace  
Дата: 07.12.10 13:22
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Здравствуйте, TopSpace, Вы писали:


TS>>Спасибо за ответ,

TS>>но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи..

H>Указатели на управляемые объекты могут уходить в неуправляемый код только когда вызывающая сторона — управляемый код. В остальных случаях память копируется рантаймом.


Ну и делать-то чего?) Конкретно в моем примере?
Re[4]: System.String interop BSTR
От: TopSpace  
Дата: 07.12.10 13:32
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, TopSpace, Вы писали:


TS>>Спасибо за ответ,

TS>>но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи.. Как все-таки правильно вернуть string из C# в C++? Получив через BSTR как-то его удалить?

JR>Да всё просто, по-моему. Вы получили копию строки в блоке неуправляемой памяти, выделенном через CoTaskMemAlloc, и должны её освободить с помощью CoTaskMemFree


JR>Если конечно NET придерживается правил COM, но это наверняка так.




Добавил:

if (hRes == S_OK)
{
    CComBSTR val = ....;

    BSTR strResult = NULL;  
    pFoo->GetResult(val, &strResult);
    CString result = strResult;

    CoTaskMemFree(strResult); 
  ...
}


Исключение Microsoft C++: ibpp_internals::ExceptionImpl по адресу 0x0012d404..
Поток 'Поток Win32' (0x170c) завершился с кодом 0 (0x0).
Draw counter ticks: 968190
HEAP[foo.exe]: Invalid Address specified to RtlFreeHeap( 00150000, 24560024 )
Re[5]: System.String interop BSTR
От: HowardLovekraft  
Дата: 07.12.10 13:38
Оценка:
Здравствуйте, TopSpace, Вы писали:

По идее, вот это:
CString result = strResult

при выходе из области должно освобождать память, а вы это раньше делаете руками с помощью CoTaskMemFree.
Re[6]: System.String interop BSTR
От: Аноним  
Дата: 07.12.10 14:20
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, TopSpace, Вы писали:


HL>По идее, вот это:

HL>
HL>CString result = strResult
HL>

HL>при выходе из области должно освобождать память, а вы это раньше делаете руками с помощью CoTaskMemFree.


Т.е. фактически ничего дописывать не надо, все само очистится? Как проверить, есть ли утечка?
Re[5]: System.String interop BSTR
От: k.o. Россия  
Дата: 07.12.10 14:36
Оценка:
Здравствуйте, TopSpace, Вы писали:

TS>Здравствуйте, Jolly Roger, Вы писали:


JR>>Здравствуйте, TopSpace, Вы писали:


TS>>>Спасибо за ответ,

TS>>>но что-то не улавливаю решение) Получается какое-то противоречие: должен освободить клиент (С++), но при этом в его распоряжение не отдается память из управляемой кучи.. Как все-таки правильно вернуть string из C# в C++? Получив через BSTR как-то его удалить?

JR>>Да всё просто, по-моему. Вы получили копию строки в блоке неуправляемой памяти, выделенном через CoTaskMemAlloc, и должны её освободить с помощью CoTaskMemFree


JR>>Если конечно NET придерживается правил COM, но это наверняка так.


Строки нужно освобождать через SysFreeString, а не CoTaskMemFree.
Re[5]: System.String interop BSTR
От: Jolly Roger  
Дата: 07.12.10 14:37
Оценка:
Здравствуйте, TopSpace, Вы писали:

А так
TS>
TS>if (hRes == S_OK)
TS>{
TS>    CComBSTR val = ....;

TS>    BSTR strResult = NULL;  
    pFoo->>GetResult(val, &strResult);
TS>    CString result = strResult;

TS>    SysFreeString(strResult); 
TS>  ...
TS>}
TS>
"Нормальные герои всегда идут в обход!"
Re[6]: System.String interop BSTR
От: k.o. Россия  
Дата: 07.12.10 14:38
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, TopSpace, Вы писали:


HL>По идее, вот это:

HL>
HL>CString result = strResult
HL>

HL>при выходе из области должно освобождать память, а вы это раньше делаете руками с помощью CoTaskMemFree.

для CString — BSTR это просто указатель на wchar_t, т.е. обычная строка, правильно освободить её он, в принципе, не может.
Re[6]: System.String interop BSTR
От: TopSpace  
Дата: 08.12.10 04:24
Оценка:
TS>>
TS>>if (hRes == S_OK)
TS>>{
TS>>    CComBSTR val = ....;

TS>>    BSTR strResult = NULL;  
    pFoo->>>GetResult(val, &strResult);
TS>>    CString result = strResult;

TS>>    SysFreeString(strResult); 
TS>>  ...
TS>>}
TS>>



Это уже другое дело) Очищает) Причем SysFreeString не падает, даже если ему передать NULL. Спасибо!
Re: System.String interop BSTR
От: _d_m_  
Дата: 08.12.10 11:38
Оценка:
Здравствуйте, TopSpace, Вы писали:

TS>Есть C# dll, предназначенная для вызовов из неуправляемого кода через COM (интерфейс и класс):


BStrWrapper
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.