2013-09-13 2 views
2

Я использую C++/CLI, и я хочу вызвать функцию WNetAddConnection2 из Windows Networking.
Во-первых, я знаю, что C++/CLI не является языком выбора для моей работы, но у меня нет возможности изменить это прямо сейчас и, например, вместо этого используйте C#.pin_ptr & PtrToStringChars против StringToHGlobalAnsi: Почему PtrToStringChars var потерял свою ценность?

Проблема в том, что эта функция принимает wchar_t *, поэтому мне нужно преобразовать System :: String^в wchar_t *.
Решение 1): использовать pin_ptr и PtrToSTringChars от vcclr.h
Решение 2): используйте StringToHGlobalUni. (Название упоминает StringHToGlobalAnsi, потому что больше людей ищут это, чтобы они могли найти этот пост, и это ответы быстрее).

Я выяснил, что оба решения работают. Но №1 на самом деле. Я поставил Wnet-функцию в исй класс CWNetShare со следующим конструктором:

CWNetShare::CWNetShare (String^ i_sLocalDrive, ...) { 
    pin_ptr<const wchar_t> wszTemp; 
    wszTemp = PtrToStringChars(i_sLocalDrive); 
    m_wszLocalDrive = const_cast<wchar_t*>(wszTemp); 

где m_wszLocalDrive является частным CWNetShare члена типа wchar_t*.

Настоящая проблема: при вызове конструктора по m_oWNetShare = gcnew CWNetShare из конструктора класса Winform (я знаю, C++/CLI и Winforms ...) все кажется прекрасным. Строка i_sLocalDrive и другие преобразованы и назначены правильно. Но при доступе к m_oWNetShare позже значения во всех переменных m_wsz ... теряются. Похоже, объект был перемещен GC.
Поэтому я сделал тест:

ref class CManaged { 
public: 
    wchar_t* m_wszNothing; 
    wchar_t* m_wszPinned; 
    wchar_t* m_wszMarshal; 
    System::String^ m_sTest; 

    CManaged() 
    { 
    m_sTest = "Hello"; 
    m_wszNothing = L"Test"; 

    pin_ptr<const wchar_t> wszTemp; 
    wszTemp = PtrToStringChars(m_sTest); 
    m_wszPinned = const_cast<wchar_t*>(wszTemp); 
    m_wszMarshal = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni (m_sTest).ToPointer()); 
    } 
}; 

Опять WinForm с m_oManaged = gcnew CManaged; в конструктор. При доступе к m_oManaged позже, если m_oManaged не был перемещен, m_wszPinned в порядке.
enter image description here
Но после GCing это проявляет ерунду. НО m_wsznothing сохраняет свою ценность, поэтому это не проблема wchar_t*, а pin_ptr как-то. Адрес m_oManaged изменился, но адрес m_wszPinned тот же, так почему же тогда потеряно значение?
enter image description here

Что пошло не так?
Использует ли pin_ptr и PtrToSTringChars?

Я использую marshalling now, который работает.

+0

Не забывайте называть 'FreeHGlobal()' в 'IntPtr', который возвращается из' StringToHGlobalUni() ', как описано в разделе [Как преобразовать систему :: String в стандартную строку] (http://msdn.microsoft. com/en-us/library/1b4az623.aspx) " – skst

ответ

2

PtrToStringChars буквально это: указатель на массив символов, который String^удерживает внутри.

Когда вы сохраняете этот указатель, это указатель на управляемый объект, с которым сборщик мусора может перемещаться. Вы гарантированы только тем, что он не будет двигаться, пока существует pin_ptr, которого вы не держите. Как только pin_ptr больше не существует, сборщик мусора может перемещать управляемый объект, и ваш указатель теперь указывает на какой-то другой объект, где-то в управляемой куче.

Используйте PtrToStringChars, если вы собираетесь вызвать неуправляемую функцию, и вам не понадобится, чтобы строка сохранялась за пределами одного вызова API (а неуправляемая функция не поддерживает ссылку на строку).Используйте StringToHGlobalUni, если вам нужно держать неуправляемую строку в долгосрочной перспективе.

+0

ОК, понял. Большое спасибо за объяснение. –

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