.net 从托管C+;中的BSTR创建System::String对象+;-这样好吗?

.net 从托管C+;中的BSTR创建System::String对象+;-这样好吗?,.net,c++,visual-c++,managed-c++,.net,C++,Visual C++,Managed C++,我的同事正在通过以下方法使用非托管库中的双字节字符填充System::String对象: RFC_PARAMETER aux; Object* target; RFC_UNICODE_TYPE_ELEMENT* elm; elm = &(m_coreObject->m_pStructMeta->m_typeElements[index]); aux.name = NULL; aux.nlen = 0; aux.type = elm->type; aux.leng = e

我的同事正在通过以下方法使用非托管库中的双字节字符填充System::String对象:

RFC_PARAMETER aux;
Object* target;
RFC_UNICODE_TYPE_ELEMENT* elm;
elm = &(m_coreObject->m_pStructMeta->m_typeElements[index]);
aux.name = NULL;
aux.nlen = 0;
aux.type = elm->type;
aux.leng = elm->c2_length;
aux.addr = m_coreObject->m_rfcWa + elm->c2_offset;

GlobalFunctions::CreateObjectForRFCField(target,aux,elm->decimals);
GlobalFunctions::ReadRFCField(target,aux,elm->decimals);
其中GlobalFunctions::CreateObjectForRFCField创建一个System::String对象,该对象中填充了空格(用于填充),非托管库声明最大长度应为:

static void CreateObjectForRFCField(Object*& object, RFC_PARAMETER& par, unsigned dec)
{
    switch (par.type)
    {
        case TYPC:
            object = new String(' ',par.leng / sizeof(_TCHAR));
            break;
        // unimportant afterwards.
    }
}
和GlobalFunctions::ReadRFCField()将库中的数据复制到创建的字符串对象中,并保留空间填充:

static void ReadRFCField(String* target, RFC_PARAMETER& par)
{
    int lngt;
    _TCHAR* srce;
    switch (par.type)
    {
        case TYPC:
        case TYPDATE:
        case TYPTIME:
        case TYPNUM:
            lngt = par.leng / sizeof(_TCHAR);
            srce = (_TCHAR*)par.addr;
            break;

        case RFCTYPE_STRING:
            lngt = (*(_TCHAR**)par.addr != NULL) ? (int)_tcslen(*(_TCHAR**)par.addr) : 0;
            srce = *(_TCHAR**)par.addr;
            break;

        default:
            throw new DotNet_Incomp_RFCType2;
    }

    if (lngt > target->Length) lngt = target->Length;

    GCHandle gh = GCHandle::Alloc(target,GCHandleType::Pinned);
    wchar_t* buff = reinterpret_cast<wchar_t*>(gh.AddrOfPinnedObject().ToPointer());
    _wcsnset(buff,' ',target->Length);
    _snwprintf(buff,lngt,_T2WFSP,srce);
    gh.Free();
}
static void ReadRFCField(字符串*目标、RFC_参数和par)
{
int lngt;
_TCHAR*srce;
开关(标准型)
{
案例类型C:
个案日期:
案例时间:
案例类型:
LNGT= PAR.LeN/siZeof(γ-TCHAR);
srce=(_TCHAR*)部分地址;
打破
案例RFCTYPE_字符串:
lngt=(*(\u TCHAR**)段落地址!=NULL)?(int)段落地址(*(\u TCHAR**)段落地址):0;
srce=*(\u TCHAR**)段落地址;
打破
违约:
抛出新的不完整的DotNet RFCType2;
}
如果(lngt>目标->长度)lngt=目标->长度;
GCHandle gh=GCHandle::Alloc(目标,GCHandleType::pinted);
wchar_t*buff=重新解释投射(gh.AddrOfPinnedObject().ToPointer());
_wcsnset(buff',目标->长度);
_snwprintf(浅黄色、浅绿色、白色、黑色);
gh.Free();
}
现在,我们有时会看到访问冲突被抛出到_snwprintf调用中。我的问题是:创建一个填充到一定长度的字符串(理想情况下是预分配内部缓冲区),然后使用GCHandle::Alloc和上面的mess修改该字符串是否合适

是的,我知道System::String对象应该是不可变的——我在寻找一个明确的“这是错误的,这就是原因”

谢谢,
Eli.

我很惊讶这似乎能奏效。如果我能理解,您可以固定一个
字符串
对象,获取其地址,然后将其强制转换为一个字符缓冲区。它不是一个字符缓冲区。CLR对象以8字节的头开始(无论如何都是32位)。您可能正在垃圾收集中破坏CLR使用的内部数据

为什么不分配一个本机缓冲区(
std::vector
)来传递给本机API,然后从该缓冲区安全地构造一个CLR字符串呢

更新:

好的,这里有一个参考:

事实证明,所使用的固定API对
字符串的布局有专门的知识,并且知道如何查找和返回原始内部字符缓冲区。耶

但引用这篇文章:

最后一点很重要:在某些方面 这些例子,我正在展示如何 pin_cast可用于访问 托管字符串的专用数据缓冲区 和数组,可能在非常量中 态度。鉴于这些是密封式的, 整个实施过程是 不知道,假设你是不好的 可以安全地修改 这些缓冲区,即使内存是 别针


有趣的是,没有提到字符串的特殊行为。

事实上,问题不在于.NET字符串作为输出缓冲区,而在于输入缓冲区

sprintf(“%s”)类函数(包括wsprintf等)将对字符串上的任何参数执行strlen类型的操作,即使是snwprintf,“n”部分仅限制写入字符串的数量,而不是从输入缓冲区读取的字符

结果表明,输入缓冲区从未保证以null结尾。很多时候,我们很幸运,因为如果返回的数据很小,那么在到达坏内存之前,它会命中空值

但是,如果其中的数据足够大,它将进入内存页的末尾。当strlen继续运行时,它会离开页面,访问城市

幸运的是,我在使用附加的本机模式调试器测试其他东西时发现了这一点,并且已经准备好使用MS的所有调试符号进行C运行时调试

我们将snwprintf改为wcsncpy()(当我们必须进行ANSI->Unicode转换时,snwprintf是一个遗留操作——我永远也不知道他为什么不进行MultiByteToWideChar())


无论如何,谢谢你的建议。

@Earwicker:Yeesh确实如此。不过,很高兴知道这一点。@Earwicker-不要让我的同事知道。就我个人而言,我希望他已经用构造函数初始化了字符串(他可以很容易地安排事情)。