Я пишу программу, которая принимает входные данные из командной строки и использует эти входные данные для подключения к удаленному серверу WMI и выполнения методов. Мой код выглядит следующим образом:
#define _WIN32_DCOM
#include <comdef.h>
#include <WbemIdl.h>
#include <stdio.h>
#pragma comment(lib, "wbemuuid.lib")
int wmain(int argc, wchar_t* argv[]) {
// <SNIP>
/* Build dynamic string containing the hostname of the target and namespace.
Example: \\DSKT-390047\ROOT\CIMV2 */
wchar_t cimv2[256] = { 0 };
swprintf_s(cimv2, 256, L"\\\\%ls\\ROOT\\CIMV2", argv[1]);
IWbemServices* pSvc = NULL;
hr = pLoc->ConnectServer(BSTR(cimv2), BSTR(argv[2]), BSTR(argv[3]), NULL,
0, NULL, NULL, &pSvc);
if (FAILED(hr)) {
pLoc->Release();
CoUninitialize();
return 1;
}
// <SNIP>
}
Метод ConnectServer()
требует 8 аргументов, 5 из которых имеют тип BSTR
. Microsoft уточняет, что для создания типов BSTR
мне нужно сделать следующее:
BSTR MyBstr = SysAllocString(L"foo");
Однако я видел, как некоторые разработчики также использовали BSTR(variable)
, где переменная имеет тип wchar_t*
, поэтому мне интересно, в чем разница между ними? Я не могу найти много информации о последнем.
Хорошо ли мне поступать так, как сейчас? Или мне следует сначала выделить BSTR
типы, использовать их, а затем освободить?
Я думаю, что то, что я делаю сейчас, позволяет избежать необходимости освобождать эти выделенные объекты, но что-то не так.
🤔 А знаете ли вы, что...
C++ обладает возможностью шаблонного программирования, что позволяет создавать обобщенные алгоритмы и структуры данных.
(BSTR)
приведение типа wchar_t*
— это серьезная программная ошибка, которая, хотя и будет компилироваться и может работать, приведет к сбоям, когда функция, которая принимает BSTR, попытается обработать его, потому что тип BSTR имеет размер, передаваемый перед указателем и поэтому может содержать нулевые символы. Подробнее здесь. Он был создан таким образом, чтобы быть совместимым с wchar_t*
там, где необходим wchar_t*
, т. е. вы можете передать BSTR функции, которая ожидает wchar_t*
. Но не наоборот.
BSTR на самом деле представляет собой такую структуру:
struct BSTR
{
unsigned int sz = <the_size>;
wchar_t str[the_size];
wchar_t nullchar = 0;
};
где вы фактически получаете указатель на str, а не на начало структуры, чтобы сделать ее совместимой с wchar_t*, чтобы приведение к wchar_t* работало в таких функциях, как wcslen
, если BSTR не содержит нулевых символов. Это может эффективно помешать программисту обнаружить свои ошибки.
Просто попробуйте, например, использовать SysStringLen для wchar_t*. Функции, которые работают с BSTR, вычитают 4 из переданного значения и ожидают, что там будет размер.
Правильный способ — использовать _bstr_t для переноса, который внутренне создаст SysAllocString при создании и SysFreeString при разрушении.
_bstr_t f(L"Hello");
// pass f to a function that expects a BSTR.
В большинстве случаев BSTR
действительно содержит строку с нулевым завершением, и ничего страшного не произойдет, если вы выполните преобразование между BSTR и wchar_t*
без использования API автоматизации.
Однако эти типы строк часто участвуют в маршалинге, а это означает, что за такими строками существует соглашение о распределителе памяти, и тогда вся строка представляет собой скорее блок памяти, выделенный с помощью данного распределителя, который обычно интерпретируется как строка.
Когда вы используете SysString*
API, подсистема COM может использовать тот же API, чтобы правильно определить размер блока памяти и точно маршалировать ваш вызов, доставляя строковый аргумент точно так, как он находится где-то еще (и это «где-то еще» может быть другим потоком, процессом и даже удаленная система). Точность передачи аргументов будет потеряна, если вы приведете wchar_t*
к BSTR
и не будете использовать API, предназначенные для типа BSTR
. Однако если маршалинг не используется, то зачастую разницы нет.
В общем, вам следует использовать связанные с BSTR
API и распределения, чтобы оставаться в безопасности и не гадать, действительно ли это требуется в данном контексте или нет. Для этого у вас также есть хорошие помощники, такие как bstr_t
и wil::unique_bstr
и другие.