Можете ли вы привести LPTSTR к BSTR? - PullRequest
9 голосов
/ 10 марта 2011

Законно ли передавать LPTSTR непосредственно в BSTR?

Исходя из моего понимания BSTR , приведение LPTSTR к BSTR напрямую оставит вам префикс поврежденной длины. Пример кода явно заявляет, что строковый литерал не может быть сохранен в BSTR. Может ли кто-нибудь подтвердить для меня, что LPTSTR / LPCTSTR нельзя привести непосредственно к BSTR без повреждения префикса длины?

EDIT:

Моя путаница заключается в том, что я вижу, что это используется при вызове COM-объекта. Оказывается, что при компиляции COM DLL создается файл .tli, который создает промежуточный метод. Этот метод принимает тип _bstr_t. _bstr_t может принимать LPTSTR в своем конструкторе, поэтому все работает гладко.

Ответы [ 8 ]

8 голосов
/ 10 марта 2011

Если ваша программа в кодировке Unicode, а ваша LPTSTR, следовательно, LPWSTR, вы можете использовать SysAllocString для преобразования указателя в строку широких символов в BSTR.

Прямое приведение невозможно, потому что оба имеют разные представления памяти.

Если вы используете C ++, вы можете использовать класс _bstr_t , чтобы упростить использование BSTR строк.

4 голосов
/ 12 мая 2011

Если вы попытаетесь передать LPCTSTR s как BSTR s, вы обнаружите, что случайным образом взорвут любой код взаимодействия, который вы получите. Маршаллинг извлечет 4-байтовый префикс (который является случайными данными) для LPCTSTR и попытается Маршалл строку.

Вы обнаружите, что 4-байтовый префикс оказывается содержимым стека до вашего буфера или даже лучше символьных данных из строки перед ним. Затем вы попытаетесь упорядочить мегабайты данных ...: -)

используйте оболочки CComBSTR и используйте .Detach(), если вам нужно сохранить указатель BSTR.

Хорошо, если компилятор выдал предупреждение об этом.

1 голос
/ 10 марта 2011

LPTSTR - это указатель на массив символов (или, если быть точным, TCHAR) BSTR - это структура (или составные данные), состоящая из * Длина префикса * Строка данных * Терминатор

поэтому кастинг не будет работать

1 голос
/ 10 марта 2011

Этого не может быть, потому что тогда четыре байта в памяти, предшествующие LPTSTR, будут рассматриваться как длина результирующего BSTR. Это может вызвать сбой защиты памяти на месте (не очень вероятно), но это, безусловно, приведет к BSTR с длиной, которая может быть намного больше, чем длина исходного LPTSTR. Поэтому, когда кто-то пытается читать или писать, он может получить доступ к недействительной памяти.

0 голосов
/ 10 марта 2011

Вообще говоря, нет, но есть способы сделать их совместимыми в некоторой степени с помощью вспомогательных классов и макросов (см. Ниже).

Основная причина, по которой отображение 1: 1 никогда не будет возможным, заключается в том, что BSTR (и, следовательно, CComBSTR может содержать '\0' в строке, поскольку в конечном итоге это подсчитываемый тип строки.


Наилучшим выбором при использовании C ++ будет использование класса ATL CComBSTR вместо BSTR. В любом случае вы можете использовать макросы преобразования ATL / MFC CW2A и друзья.

Также обратите внимание, что в документации (MSDN) написано:

Рекомендуемый способ конвертации в и из BSTR строк использовать CComBSTR класс. Чтобы преобразовать в BSTR, передать существующую строку в конструктор CComBSTR. Преобразовать от BSTR, используйте COLE2 [C] DestinationType [EX], например COLE2T.

... что относится к вашему случаю использования.

См. Ответ Джона Диблинга для альтернативы (_bstr_t).

0 голосов
/ 10 марта 2011

Вы не можете разыграть, вы должны преобразовать.Вы можете использовать встроенный компилятор, встроенный _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;
}
0 голосов
/ 10 марта 2011

Нет, вы не можете - если код, который полагает, что это BSTR, вызывает SysStringLen () , это приведет к неопределенному поведению, поскольку эта функция полагается на некоторые специфичные для реализации данные службы.

0 голосов
/ 10 марта 2011

Нет, вы не можете разыграть их напрямую. Тем не менее, вы можете сделать строку, которая делает оба. C-строка не имеет 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;
};
...