【问题标题】:Can you cast a LPTSTR to a BSTR?您可以将 LPTSTR 转换为 BSTR 吗?
【发布时间】:2011-07-12 19:15:34
【问题描述】:

将 LPTSTR 直接转换为 BSTR 是否合法?

根据我的understanding of BSTR,直接将 LPTSTR 转换为 BSTR 会留下损坏的长度前缀。示例代码明确指出不能将字符串文字存储到 BSTR。谁能帮我确认 LPTSTR/LPCTSTR 不能直接转换为 BSTR 而不会破坏长度前缀?

编辑:

我的困惑是因为在调用 COM 对象时看到了这个。事实证明,在编译 COM dll 时,会生成一个创建中间方法的 .tli 文件。此方法采用_bstr_t 类型。 _bstr_t 可以在其构造函数中使用LPTSTR,因此一切顺利。

【问题讨论】:

    标签: c++ windows com string-conversion


    【解决方案1】:

    如果您的程序是 unicode 并且您的 LPTSTR 因此是 LPWSTR,您可以使用 SysAllocString 将指向宽字符串的指针转换为 BSTR

    直接转换是不可能的,因为两者具有不同的内存表示。

    如果你使用C++,你可以使用_bstr_t类来简化BSTR字符串的使用。

    【讨论】:

      【解决方案2】:

      如果您尝试将LPCTSTRs 传递为BSTRs,您会发现您将随机炸毁您接近的任何互操作编组代码。编组将提取 LPCTSTR 的 4 字节前缀(这是随机数据)并尝试编组字符串。

      您会发现 4 字节前缀原来是缓冲区之前的堆栈内容,甚至是之前字符串中更好的字符数据。然后,您将尝试编组兆字节的数据... :-)

      如果您需要保留 BSTR 指针,请使用 CComBSTR 包装器并使用 .Detach()

      如果编译器对此发出警告,那就太好了。

      【讨论】:

      • 刚刚被这个咬了,将三个 L"My String" 参数传递给一个期望 3 个 BSTR 的 .Net COM 方法,并且只有一个在互操作编组中幸存下来。编译器 VS2008 /W4 没有警告
      • 4字节前缀表示BSTR的长度。
      【解决方案3】:

      LPTSTR 是指向 char 数组的指针(准确地说是 TCHAR) BSTR 是一个结构(或复合数据),由 * 长度前缀 * 数据字符串 * 终结者

      所以投射不起作用

      【讨论】:

        【解决方案4】:

        不,你不能 - 如果认为它是 BSTR 的代码调用 SysStringLen(),它将遇到未定义的行为,因为该函数依赖于一些特定于实现的服务数据。

        【讨论】:

          【解决方案5】:

          不可能,因为内存中LPTSTR 之前的四个字节将被视为结果BSTR 的长度。这可能会导致当场内存保护错误(不太可能),但肯定会导致BSTR 的长度可能比原始LPTSTR 的长度大得多。因此,当有人试图从中读取或写入时,他们可能会访问无效内存。

          【讨论】:

          • 字符串数据不会被解释为长度。紧接在前面的字节是。 BSTR 指向长度+字符串结构的字符串部分。
          • @Ben:谢谢你的澄清,我不知道。我会相应地修改答案。
          【解决方案6】:

          你不能施放,你必须转换。您可以使用内置编译器内在 _bstr_t(来自 comutil.h)来帮助您轻松完成此操作。示例:

          #include <Windows.h>
          #include <comutil.h>
          
          #pragma comment( lib, "comsuppwd.lib")
          
          int main()
          {
              LPTSTR p = "Hello, String";
              _bstr_t bt = p;
              BSTR bstr = bt;
              bstr;
          }
          

          【讨论】:

          • @John Dibling:嗨。我举的例子行不通吗?如果不是,你能解释一下为什么吗?
          • @Luther:两件事。 1)当MS已经为你提供了一个类来为你做这件事时,为什么还要写一堆代码(_bstr_t)? 2)您编写的代码可能有效,也可能无效;我不知道。您应该调用SysAllocString 来创建一个BSTR,但您正试图利用您对BSTR 内部工作原理的了解来自己构建一个。此外,BSTR 的字符串部分是 Unicode。我认为你的不是。
          • @John Dibling:感谢您的回复。我不是想重新发明轮子,代码只是为了说明一点。我们这里的人问的是铸造,而不是构造和复制 BSTR。他可能有充分的理由进行强制转换而不是重构(严格的内存要求,内部循环内的许多调用,不想接触堆等),而我在那里绘制的类应该实现这一点。如果 BSTR 是 UniCode,则将 char* 替换为 wchar_t*。有时,如果标准方法不能按照您希望的方式完成工作,最好自己编写一些代码。
          • @Luther:你草绘的类没有实现强制转换。它通过各种调用 int eh 构造函数来实现转换。实际上,您的构造函数所做的正是我们认为SysAllocString 根据我们对 BSTR 表示的了解可能会做的事情。此外,OP没有询问如何投射。他们询问将 LPSTR 转换为 BSTR 是否“合法”。如果我们将“合法”重新解释为“好主意”,那么答案显然是“不”。你的班级似乎同意我的评估,因为你不自己投——你皈依了。 Albiet 我同意有时你必须编写代码。:)
          • 当我说铸造时,我指的是我提供的访问器函数。这只是为了表明您可以,给定一个在内存中构造字符串的类,并且两边都有正确的缓冲区空间,从内存中的同一区域投射一个 c 字符串和一个 BSTR。这就是我试图展示的一切。但是,您是对的:我不知道 SysAllocString 是否会进行一些引用计数或其他恶作剧,并且在生产代码中人们绝对应该使用最佳实践方法。然而,这永远不会阻止人们试图了解幕后发生的事情。
          【解决方案7】:

          不,您不能直接施放它们。但是,您可以制作一个两者兼而有之的字符串。 C-String 没有 4 字节的标头。但是,中间的位是相同的,因此如果您发现自己需要两种表示形式,请创建一个包装类,该类构造一个带有 4 字节标头和空终止符的字符串,但可以返回 BSTR 部分和 C-String 的访问器。

          这个代码是一个不完整的例子,我没有编译这个!

          class YetAnotherStringType //just what the world needs
          {
            public:
            YetAnotherStringType(const char *str)
            { 
               size_t slen = strlen(str);
               allocate(slen);
               set_size_dword(slen);  
               copy_cstr(str, slen);     
            }
          
            const char *get_cstr() const
            {
               return &m_data[4];
            }
          
            const BSTR get_bstr() const
            {
               return (BSTR*)m_data;
            }
          
            void copy_cstr(const char *cstr, int size = -1)
            {
                if (size == -1)
                   size = strlen(cstr);
                memcpy(&m_data[4], cstr, size + 1); //also copies first null terminator
                m_data[5 + size] = 0; //add the second null terminator
            }
          
            void set_size_dword(size_t size)
            {
               *((unsigned int*)m_data) = size;
            }
          
            void allocate(size_t size)
            {
               m_data = new char[size + 6]; //enough for double terminator
            }
          
            char *m_data;
          };
          

          【讨论】:

          • BSTRs 有一个双空终止符。
          • @Jon:啊,对不起。我在这里阅读了 msdn.microsoft.com/en-us/library/ms221069.aspx> 页面并读到他们没有 - 我误读了它。他们似乎在说标头不包含空终止符(!?)
          • 已经有一个字符串可以做到这两个:_bstr_t from &lt;comutil.h&gt;
          • 查看 Ben Voigt 对 Jon 的回答的评论。 4字节的偏移量是你的误解。
          【解决方案8】:

          一般来说不会,但有一些方法可以通过辅助类和宏使它们在某种程度上兼容(见下文)。

          1:1 映射永远不可能的主要原因是BSTR(因此CComBSTR 可以在字符串中包含'\0',因为最终它是一个计数字符串类型。


          使用 C++ 时,您最好的选择是使用 ATL 类 CComBSTR 代替 BSTR 正确的。无论哪种情况,您都可以使用 ATL/MFC 转换宏 CW2A 和朋友。

          还要注意文档 (MSDN) 说:

          推荐的转换方式 而来自BSTR 的字符串是使用 CComBSTR 班级。要转换为BSTR, 将现有字符串传递给 CComBSTR 的构造函数。转换 从 BSTR,使用 COLE2[C]DestinationType[EX],如 COLE2T.

          ...适用于您的用例。

          请参阅 John Dibling 的替代答案 (_bstr_t)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-25
            • 1970-01-01
            • 1970-01-01
            • 2020-06-27
            • 2011-04-08
            • 2010-09-15
            • 2013-05-12
            • 1970-01-01
            相关资源
            最近更新 更多