【问题标题】:Why doesn't COM use a static empty BSTR?为什么 COM 不使用静态的空 BSTR?
【发布时间】:2010-12-21 18:28:48
【问题描述】:

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

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

【问题讨论】:

    标签: com bstr string


    【解决方案1】:

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

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

    【讨论】:

    • 字符串实习适用于:1. 字符串常量,以及 2. 调用了 intern() 的字符串。但是在你使用new 的地方,对象不等式仍然适用:String a = new String("foo"), b = new String("foo"); assert a != b; assert a.equals(b);
    • 另外:assert a.intern() == "foo"; assert b.intern() == "foo";
    • 啊,好的。学到了一些新东西。对不起,那么:-)
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      它不是 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。这是一个简短的 sn-p,可以为您演示这一点,以及我收到的输出。我也将它增加到原来的长度之外,并且仍然收到相同的 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
      

      【讨论】:

      • 但是SysReAllocString创建了一个新指针,所以空字符串仍然是不可变的,你只需分配一个足够大的新字符串。
      • 它可以创建一个新指针,但它也可以为原始 BSTR 提供一个新的分配长度。最新版本的 windows 中的当前实现可能默认使用新的 BSTR,但较旧的实现可能会以不同的方式运行,可能是早于 32 位 Windows/COM 的 windows 版本。
      • 当我重新分配一个空的 BSTR(使用 SysAllocString(L"") 创建)时,我总是得到一个新指针(即使调用 SysReAllocString(&bs, L"")) - 这发生在 32 位 Vista 上
      • 我开始在 Vista 上尝试这些。我得到了与 XP 相同的结果。但是,我根据您的评论测试了新场景: SysAllocString(NULL) 后跟 SysReAllocString(&bstr, L"") 产生了一个新指针,从 NULL 到一个好的 BSTR。反过来,SysAllocString(L"") 后跟 SysReAllocString(&bstr, NULL) 会产生一个新指针,从一个好的 BSTR 指向一个 NULL。我现在有点忙,但一旦有机会,我会用结果和更具体的 Vista / XP 测试来更新我的答案。
      猜你喜欢
      • 2014-06-22
      • 2015-08-15
      • 2012-05-14
      • 1970-01-01
      • 2011-01-14
      • 2012-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多