1

我正在使用 C++/CLI,我想WNetAddConnection2从 Windows Networking 调用该函数。
首先,我知道 C++/CLI 不是我工作的首选语言,但我现在无法更改它,例如改用 C#。

现在的问题是,这个函数需要 wchar_t*,所以我需要将 System::String^ 转换为 wchar_t*。
解决方案 1):使用pin_ptrPtrToSTringCharsvcclr.h
解决方案 2):使用StringToHGlobalUni. (标题提到StringHToGlobalAnsi是因为更多的人正在搜索它,所以他们可能会找到这篇文章并且它的答案更快)。

我发现这两种解决方案都有效。但是#1并非如此。我已将 WNet 函数放入CWNetShare具有以下构造函数的 ref 类中:

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

其中m_wszLocalDrive是 type 的私有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());
  }
};

m_oManaged = gcnew CManaged;再次在其构造函数中添加一个 winform 。以后访问m_oManaged的时候,如果m_oManaged没有被移动,m_wszPinned就可以了。
在此处输入图像描述
但是在GCing之后,它显示出胡说八道。但是 m_wsznothing 保持它的价值,所以这不是问题wchar_t*,而是 pin_ptr 的问题。的地址m_oManaged变了,但是地址m_wszPinned还是一样的,那为什么值会丢失呢?
在此处输入图像描述

这里出了什么问题?
那么 pin_ptr 和 PtrToSTringChars 还有用吗?

我现在正在使用编组,这很有效。

4

1 回答 1

3

PtrToStringChars 字面意思是:指向 String^ 内部保存的字符数组的指针。

当您保存该指针时,它是指向允许垃圾收集器移动的托管对象的指针。您只能保证只要 pin_ptr 存在,它就不会移动,而您不会保留它。一旦 pin_ptr 不再存在,垃圾收集器就可以自由地移动托管对象,并且您的指针现在指向托管堆中某处的某个其他对象。

如果要调用非托管函数,则使用 PtrToStringChars,并且您不需要字符串在该 API 调用之后持续存在(并且非托管函数不保留对字符串的引用)。如果您需要长期保留非托管字符串,请使用 StringToHGlobalUni。

于 2013-09-13T16:29:24.893 回答