可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is it legal to cast a LPTSTR directly to a BSTR?
Based on my understanding of BSTR, casting a LPTSTR to a BSTR directly will leave you with a corrupted length prefix. The example code explicitly states that a string literal cannot be stored to a BSTR. Can anyone confirm for me that a LPTSTR/LPCTSTR cannot be cast directly to a BSTR without corrupting the length prefix?
EDIT:
My confusion is from seeing this used in a call to a COM object. It turns out that when compiling the COM dll, a .tli file is generated that creates an intermediate method. This method takes type _bstr_t
. The _bstr_t
can take LPTSTR
in its constructor, so everything works smoothly.
回答1:
If your program is unicode and your LPTSTR
therefore is a LPWSTR
, you can use SysAllocString to convert from a pointer to a wide character string to BSTR
.
A direct cast is not possible because the two have different memory representations.
If you use C++, you can use the _bstr_t class to simplify the usage of BSTR
strings.
回答2:
If you are trying to pass LPCTSTR
s as BSTR
s you will find that you will randomly blow up any interop marshalling code you get near. The marshalling will pull the 4 byte prefix (which is random data) for an LPCTSTR
and try and Marshall the string.
You will find that the 4 byte prefix turns out to be the stack contents prior to your buffer, or even better character data from the string before it. You will then try to marshall megabytes of data... :-)
use the CComBSTR
wrappers and use .Detach()
if you need to keep the BSTR
pointer.
Be nice if the compiler spat out a warning on this one.
回答3:
A LPTSTR is a pointer to a char array (or TCHAR to be exact)
BSTR is a structur (or composite data) consist of
* Length prefix
* Data string
* Terminator
so casting will not work
回答4:
No, you cannot - if the code that believes it is a BSTR calls SysStringLen() it will run into undefined behavior since that function relies on some implementation-specific service data.
回答5:
It cannot be, because then the four bytes in memory preceding the LPTSTR
would be considered as the length of the resulting BSTR
. This might cause a memory protection fault on the spot (not very likely), but it would certainly result in a BSTR
with a length that could be way larger than the length of the original LPTSTR
. So when someone tries to read or write from that they may access invalid memory.
回答6:
You cannot cast, you must convert. You can use the built-in compiler intrinsic _bstr_t
(from comutil.h
) to help you do this easily. Sample:
#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;
}
回答7:
No, you cannot cast them directly. However, you can make a string that does both.
A C-String doesn't have a 4-byte header. However, the bit in the middle is the same so if you find yourself needing both representations, make a wrapper class that constructs a string with a 4byte header and a null terminator but can return accessors to the BSTR portion and the C-String.
This code is intended as an incomplete example, I haven't compiled this!
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;
};
回答8:
Generally speaking no, but there are ways to make them compatible to some extent via helper classes and macros (see below).
The main reason why a 1:1 mapping will never be possible is that a BSTR
(and consequently CComBSTR
can contain '\0'
in the string, because ultimately it is a counted string type.
Your best choice when using C++ would be to go for the ATL class CComBSTR
in place of BSTR
proper. In either case you can make use of the ATL/MFC conversion macros CW2A
and friends.
Also note that the documentation (MSDN) says:
The recommended way of converting to
and from BSTR
strings is to use the
CComBSTR
class. To convert to a BSTR
,
pass the existing string to the
constructor of CComBSTR
. To convert
from a BSTR, use
COLE2
[C
]DestinationType[EX
], such as
COLE2T
.
... which applies to your use case.
Please see John Dibling's answer for an alternative (_bstr_t
).