3

分配空BSTR时,无论是 bySysAllocString(L"")还是 bySysAllocStringLen(str, 0)你总是会得到一个新的BSTR(至少通过我所做的测试)。BSTRs 通常不共享(如 Java/.NET interment),因为它们是可变的,但空字符串对于所有意图和目的来说都是不可变的。

我的问题(终于)是为什么 COM 不使用在创建空时总是返回相同字符串的琐碎优化BSTR(并在 中忽略它SysFreeString)?是否有令人信服的理由不这样做(因为我的推理有缺陷)或者只是它被认为不够重要?

4

3 回答 3

4

我无法谈论 COM 中的惯用语,但在 C++、Java 等中,期望如果您new是一个对象,它不会与任何其他对象比较相等(就地址/对象身份而言)。当您使用基于身份的映射时,此属性很有用,例如,作为IdentityHashMapJava 中的键。出于这个原因,我认为空字符串/对象不应该是这条规则的例外。

NULL编写良好的 COM 对象将允许您传递BSTR参数并将其视为等效于空字符串。(不过,这不适用于 MSXML,因为我已经学到了很多东西。:-P)

于 2009-11-30T19:42:41.343 回答
2

我猜(是的,这只是一个猜测)这种优化被认为不够重要而无法执行。

虽然 Windows 过去的内存消耗是 API 设计中的一个主要因素(参见 Raymond Chen 的文章),但与 Java 或 .NET 的字符串实习不同,它的好处相当小,因为它们只适用于只有六个字节的单个字符串长。一个程序在任何一个时间点必须在内存中保存多少个空字符串?这个数字是否足够大以保证优化,还是实际上可以忽略不计?

于 2009-11-30T19:43:24.930 回答
2

它不是 COM 分配 BSTR,而是提供它的 windows 子系统。

空 BSTR 不能共享静态实例,因为有些函数可以重新分配/调整 BSTR 的大小。请参阅 SysReAllocString。虽然没有提到乐观的分配行为,但不能假设调用者在调用后永远不会收到原始的 BSTR。

SysReAllocString @ MSDN

编辑:

经过一番思考,我意识到即使考虑到 SysReAllocString,也可以从一个共享的空 BSTR 开始,调用 SysReAllocString,然后在没有任何破坏行为的情况下接收新的 BSTR。因此,为了争论,可以打折扣。我的错。

然而,我认为空 BSTR 的想法比人们想象的要承载更多的包袱。我编写了一些测试程序,看看是否能得到一些矛盾或有趣的结果。在运行我的测试并计算结果之后,我认为对您的问题的最佳答案是,如果所有请求都获得自己的 BSTR,那么对于所有相关人员来说都是最安全的。有很多时髦的方法可以让 BSTR 报告不同风格的零长度,包括面向字符串和面向字节的。即使在某些地方进行了一些返回共享实例的优化,在口头描述空 BSTR 与具有空字符串长度和实际分配长度的实际 BSTR 时,仍有很大的混淆空间。例如,可能会忘记诸如“没有字符串分配长度的 BSTR ”之类的语句",可能会导致一些严重的内存泄漏(请参阅下面有关字节分配的 BSTR 的测试)。

此外,尽管某些 COM 组件允许 NULL 指针(0 值)BSTR 作为参数,但假设所有 COM 组件都支持它是不安全的。这只有在调用者和被调用者都同意允许的情况下才是安全的。对每个人来说最安全的行为是假设如果 BSTR 被移交,它可能具有零定义长度,需要处理零定义长度的情况,并且需要一些不是 NULL 指针的值。至少,这使得编写代理/存根代码和其他棘手的任务变得更加容易。

我的第一个测试程序尝试了一些不常见的分配方法。请注意,您可以获得报告的 SysStringLen 长度为 0 的 BSTR,但具有实际字节分配。另外,我承认 bstr5 和 bstr6 不是干净的分配方法。

这是来源:

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"");
  BSTR bstr2 = SysAllocStringLen(NULL, 0);
  BSTR bstr3 = SysAllocStringLen(NULL, 1);
  *bstr3 = '\0';
  BSTR bstr4 = SysAllocStringLen(L"some string", 0);
  BSTR bstr5 = SysAllocStringByteLen((LPCSTR)L"", 1);
  BSTR bstr6 = SysAllocStringByteLen((LPCSTR)L"", 2);
  BSTR bstr7 = SysAllocStringByteLen("", 1);
  BSTR bstr8 = SysAllocStringByteLen("\0\0", 2);
  BSTR bstr9 = SysAllocStringByteLen(NULL, 0);
  BSTR bstr10 = SysAllocStringByteLen(NULL, 1);

  _tprintf(_T("L\"\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));
  _tprintf(_T("NULL BSTR with no alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr2, SysStringLen(bstr2), SysStringByteLen(bstr2));
  _tprintf(_T("NULL BSTR with 1 OLECHAR alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr3, SysStringLen(bstr3), SysStringByteLen(bstr3));
  _tprintf(_T("L\"some string\"-sourced BSTR with no alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr4, SysStringLen(bstr4), SysStringByteLen(bstr4));
  _tprintf(_T("L\"\"-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr5, SysStringLen(bstr5), SysStringByteLen(bstr5));
  _tprintf(_T("L\"\"-sourced BSTR with 2 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr6, SysStringLen(bstr6), SysStringByteLen(bstr6));
  _tprintf(_T("\"\"-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr7, SysStringLen(bstr7), SysStringByteLen(bstr7));
  _tprintf(_T("\"\\0\\0\"-sourced BSTR with 2 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr8, SysStringLen(bstr8), SysStringByteLen(bstr8));
  _tprintf(_T("NULL-sourced BSTR with 0 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr9, SysStringLen(bstr9), SysStringByteLen(bstr9));
  _tprintf(_T("NULL-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr10, SysStringLen(bstr10), SysStringByteLen(bstr10));

  SysFreeString(bstr1);
  SysFreeString(bstr2);
  SysFreeString(bstr3);
  SysFreeString(bstr4);
  SysFreeString(bstr5);
  SysFreeString(bstr6);
  SysFreeString(bstr7);
  SysFreeString(bstr8);
  SysFreeString(bstr9);
  SysFreeString(bstr10);

  return 0;
}

这是我收到的结果。

L""-sourced BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
NULL BSTR with no alloc length
        BSTR=0x00175c04, length 0, 0 bytes
NULL BSTR with 1 OLECHAR alloc length
        BSTR=0x00175c2c, length 1, 2 bytes
L"some string"-sourced BSTR with no alloc length
        BSTR=0x00175c54, length 0, 0 bytes
L""-sourced BSTR with 1 byte alloc length
        BSTR=0x00175c7c, length 0, 1 bytes
L""-sourced BSTR with 2 byte alloc length
        BSTR=0x00175ca4, length 1, 2 bytes
""-sourced BSTR with 1 byte alloc length
        BSTR=0x00175ccc, length 0, 1 bytes
"\0\0"-sourced BSTR with 2 byte alloc length
        BSTR=0x00175cf4, length 1, 2 bytes
NULL-sourced BSTR with 0 byte alloc length
        BSTR=0x00175d1c, length 0, 0 bytes
NULL-sourced BSTR with 1 byte alloc length
        BSTR=0x00175d44, length 0, 1 bytes

我的下一个测试程序显示,缩小尺寸可能会返回相同的 BSTR。这是一个简短的片段,可以为您演示这一点,以及我收到的输出。我也将它增加到原来的长度之外,并且仍然收到相同的 BSTR。这至少表明,不能假设没有长度的 BSTR 不能增加大小。

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"hello world!");

  _tprintf(_T("L\"hello world!\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"\"\r\n"));
  SysReAllocString(&bstr1, L"");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello!\"\r\n"));
  SysReAllocString(&bstr1, L"hello!");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!+\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!+");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"),
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  SysFreeString(bstr1);

  return 0;
}

在我的工作站 (Windows XP) 上运行该程序,返回以下结果。我很想知道是否有人在此过程中获得了新的 BSTR。

L"hello world!"-sourced BSTR
        BSTR=0x00175bdc, length 12, 24 bytes
resizing bstr1 to source L""
L""-sourced reallocated BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
resizing bstr1 to source L"hello!"
L"hello!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 6, 12 bytes
resizing bstr1 to source L"hello world!+"
L"hello world!+"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 13, 26 bytes

我再次尝试了这个程序,但这次从一个空的 Widechar 字符串 (L"") 开始。这应该涵盖以未定义字符串长度的 BSTR 开始并查看它是否实际上具有隐式大小的情况。当我运行它时,我发现我仍然收到了相同的 BSTR。不过,我希望结果可能会有所不同。

这是来源:

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"");

  _tprintf(_T("L\"\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!");
  _tprintf(_T("L\"hello world!\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello!\"\r\n"));
  SysReAllocString(&bstr1, L"hello!");
  _tprintf(_T("L\"hello!\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!+\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!+");
  _tprintf(_T("L\"hello world!+\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"),
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  SysFreeString(bstr1);

  return 0;
}

结果:

L""-sourced BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
resizing bstr1 to source L"hello world!"
L"hello world!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 12, 24 bytes
resizing bstr1 to source L"hello!"
L"hello!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 6, 12 bytes
resizing bstr1 to source L"hello world!+"
L"hello world!+"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 13, 26 bytes
于 2009-11-30T19:52:50.357 回答