CPP Windows string conversions confusion CStringA and LPCWSTR - c++

I need to use WinAPI function to restart a windows service, I am not familiar with strings in C++.
My function receive as parameter: const CStringA& serviceName:
bool MyClassName::RestartServer(const CStringA& serviceName)
When I obtain SC Handle via OpenService(..) I need to provide type LPCWSTR :
SC_HANDLE SHandle = OpenService(hSCManager, LPCWSTR serviceNameAsWideString, SC_MANAGER_ALL_ACCESS);
How do I convert CStringA to LPCWSTR?
I tried to following:
CA2W(serviceName, CP_UTF8);
CString str("MyServiceName"); CStringW strw(str); LPCWSTR ptr = strw;
Both did not work properly, they compiled, but when I tried to execute the code.
It failed to OpenService().
What worked:
LPCWSTR newString = serviceName.AllocSysString();
What am I missing here? Why 1 and 2 did not work? Why 3 worked?
How do I properly deallocate newString?

Your code requires a conversion because you are calling the TCHAR-based OpenService() macro, which maps to either OpenServiceW() or OpenServiceA() depending on whether UNICODE is defined:
__checkReturn
WINADVAPI
SC_HANDLE
WINAPI
OpenServiceA(
__in SC_HANDLE hSCManager,
__in LPCSTR lpServiceName,
__in DWORD dwDesiredAccess
);
__checkReturn
WINADVAPI
SC_HANDLE
WINAPI
OpenServiceW(
__in SC_HANDLE hSCManager,
__in LPCWSTR lpServiceName,
__in DWORD dwDesiredAccess
);
#ifdef UNICODE
#define OpenService OpenServiceW
#else
#define OpenService OpenServiceA
#endif // !UNICODE
In your case, UNICODE is clearly being defined in your project, so your code is really calling OpenServiceW(), which is why it expects an LPCWSTR as input.
Your RestartServer() method takes a CStringA (char-based ANSI) string as input, so you should use OpenServiceA() explicitly to match the same character type, no conversion needed:
bool MyClassName::RestartServer(const CStringA& serviceName)
{
...
SC_HANDLE SHandle = OpenServiceA(hSCManager, serviceName, SC_MANAGER_ALL_ACCESS);
...
}
Otherwise, if you are going to continue using TCHAR-based functionality in your code 1, then you should change your RestartServer() method to take a CString instead of a CStringA so it adopts the same ANSI/Unicode mapping that OpenService() does (and other TCHAR-based functions do), again avoiding a conversion:
1: which you should not do, since there is rarely a need to ever write code for Win9x/ME nowadays. Windows has been a Unicode-based OS since NT4.
bool MyClassName::RestartServer(const CString& serviceName)
If that is not an option for you, then CA2W() will work just fine:
bool MyClassName::RestartServer(const CStringA& serviceName)
{
USES_CONVERSION;
...
SC_HANDLE SHandle = OpenService(hSCManager, ATL::CA2W(serviceName), SC_MANAGER_ALL_ACCESS);
...
}
Though, you might consider just using CString internally and let it handle a conversion if needed:
bool MyClassName::RestartServer(const CStringA& serviceName)
{
...
SC_HANDLE SHandle = OpenService(hSCManager, CString(serviceName), SC_MANAGER_ALL_ACCESS);
...
}
Or, make the code conditional:
bool MyClassName::RestartServer(const CStringA& serviceName)
{
...
SC_HANDLE SHandle = OpenService(hSCManager,
#ifdef UNICODE
CStringW(serviceName)
#else
serviceName
#endif
, SC_MANAGER_ALL_ACCESS);
...
}

CStringA and CStringW have constructors taking both const char* and const wchar_t* C strings.
Write following:
CStringW serviceNameW( serviceName );
About AllocSysString, it creates a copy in BSTR, they’re more complex than C strings, they’re null-terminated too but they also have length at negative offset. If you want to do manual memory management, call SysFreeString on the pointer. Or if you want BSTR but don’t want manual memory management, use CComBSTR class.

Related

How to convert CComBSTR to LPCSTR

I have CComBSTR in my code and have to pass it to function with argument type LPCSTR.
How to convert CComBSTR to LPCSTR?
There are many ways to do this, but the ATL way would be using Using MFC MBCS/Unicode Conversion Macros:
void SomeCode()
{
USES_CONVERSION;
CComBSTR bstr(L"hello world");
LPCSTR lp = W2CA(bstr); // bstr is a LPWSTR
}

dereferencing std::shared_ptr<T[]>?

In bellow function I need to dereference shared pointer to an array of TCHAR
however none of the operands available in std::share_ptr seem to work:
The FormatMessage API expects PTSTR which is in case of UNICODE wchar_t*
How to dereference the given pointer (see comment in the code)?
If you think the same thing could be achieved with more elegant sintax that it would be great you provide example code.
const std::shared_ptr<TCHAR[]> FormatErrorMessage(const DWORD& error_code)
{
constexpr short buffer_size = 512;
std::shared_ptr<TCHAR[]> message = std::make_shared<TCHAR[]>(buffer_size);
const DWORD dwChars = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
error_code,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
*message, // no operator "*" matches these operands
buffer_size,
nullptr);
return message;
}
EDIT
Thanks to answers and commnts (the only) way to make it work with Microsoft compiler is this:
const std::shared_ptr<std::array<WCHAR, buffer_size>>
FormatErrorMessageW(const DWORD& error_code, DWORD& dwChars)
{
const std::shared_ptr<std::array<WCHAR, buffer_size>> message =
std::make_shared<std::array<WCHAR, buffer_size>>();
dwChars = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM,
nullptr, // The location of the message definition.
error_code,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
message.get()->data(),
buffer_size,
nullptr);
return message;
}
*message returns TCHAR&, whereas FormatMessage requires TCHAR* there. Instead of *message do message.get().
Also, since this function doesn't keep a reference to the formatted message, it should return std::unique_ptr<TCHAR[]> to document the fact that the caller is now the sole owner.

EnumWindows not working

I'm creating a dll file.
My code:
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
void test() {
EnumWindows(EnumWindowsProc, NULL);
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char class_name[80];
char title[80];
GetClassName(hwnd, (LPWSTR) class_name, sizeof(class_name));
GetWindowText(hwnd, (LPWSTR) title,sizeof(title));
std::string titlas(title);
std::string classas(class_name);
Loggerc(titlas);
Loggerc("Gooing");
return TRUE;
}
Then I just call test().
In the log, titlas is empty and code stops.
When I try this code in a Win32 app with CodeBlock, everything works, all of the titles show. But in a dll, it does not work.
Where is the problem?
char class_name[80];
char title[80];
GetClassName(hwnd, (LPWSTR) class_name, sizeof(class_name));
GetWindowText(hwnd, (LPWSTR) title,sizeof(title));
std::string titlas(title);
std::string classas(class_name);
Considering that since VS2005 the default has been building in Unicode mode (instead of ANSI/MBCS) and that you have those (ugly C-style) (LPWSTR) casts, I'm assuming that you got compile-time errors when passing your char-based string buffers to APIs like GetClassName() and GetWindowText(), and you tried to fix those errors with casts.
That's wrong. The compiler was actually helping you with those errors, so please follow its advice instead of casting the compiler errors away.
Assuming Unicode builds, you may want to use wchar_t and std::wstring instead of char and std::string, and _countof() instead of sizeof() to get the size of buffers in wchar_ts, not in bytes (chars).
E.g.:
// Note: wchar_t used instead of char
wchar_t class_name[80];
wchar_t title[80];
// Note: no need to cast to LPWSTR (i.e. wchar_t*)
GetClassName(hwnd, class_name, _countof(class_name));
GetWindowText(hwnd, title, _countof(title));
// Note: std::wstring used instead of std::string
std::wstring titlas(title);
std::wstring classas(class_name);
If other parts of your code do use std::string, you may want to convert from UTF-16-encoded text stored in std::wstring (returned by Windows APIs) to UTF-8-encoded text and store it in std::string instances.

Passing native string type from CLI to native and back again

I am trying to write a CLI wrapper around some low-level COM-related calls. One of the operations that I need to do specifically is to get a specific value from a PROPVARIANT, i.e.:
pwszPropName = varPropNames.calpwstr.pElems[dwPropIndex];
where pwszPropName is documented to be an LPWSTR type and dwPropIndex is a DWORD value passed into the function by the user.
I have a native function defined as follows:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, wchar_t *PropertyName)
I would like to return the value of pwszPropName via *PropertyName.
Is the wchar_t* type the best way to do this, and would I need to pin *PropertyName in my CLI to ensure it does not move in memory? Do I need to define the length of *PropertyName before passing it to native code (buffer)?
If wchar_t* is the right variable type to pass into the native function, what is the proper conversion of LPWSTR to whar_t*, and how then would you convert that value to System::String?
I have tried a number of different techniques over the past few days and can't seem to get anything right.
------------UPDATE------------
Here is my full code. First, the CLI:
String^ MetadataEditor::GetPropertyNameByID(unsigned int ID)
{
LPWSTR mPropertyName = L"String from CLI";
m_pCEditor->GetPropertyNameByID(ID, mPropertyName);
//Convert return back to System::String
String^ CLIString = gcnew String(mPropertyName);
return CLIString;
}
And the native code:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, LPWSTR PropertyName)
{
HRESULT hr = S_OK;
LPWSTR myPropName;
PROPVARIANT varNames;
PropVariantInit(&varNames);
hr = m_pMetadata->GetAllPropertyNames(&varNames);
if(hr != S_OK)
{
PropVariantClear(&varNames);
return hr;
}
myPropName = varNames.calpwstr.pElems[ID];
PropertyName = myPropName;
PropVariantClear(&varNames);
return hr;
}
It doesn't seem like the value (myPropName) is set properly and/or sustained back into the CLI function because the CLI returns the value I set on mPropertyName before calling the native function.. I'm not sure why or how to fix this.
UPDATE!!!!
I suspected my problem had something to do with variables going out of scope. So I changed the C++ function definition as follows:
LPWSTR GetPropertyNameByID(DWORD ID, HRESULT ErrorCode);
After adjusting the CLI as well, I now get a value returned, but the first character is incorrect, and in fact can be different with every call. I tried using ZeroMemory() in the native class before assigning the output of the PROPVARIANT to the variable (ZeroMemory(&myPropName, sizeof(myPropName +1)); but still no luck.
You can design unmanaged function by the following way:
HRESULT CMetadataEditor::GetPropertyNameByID(DWORD ID, LPWSTR PropertyName, size_t size)
{
....
wcscpy(PropertyName, varNames.calpwstr.pElems[ID]); // or wcsncpy
...
}
PropertyName is the buffer allocated by caller, size is its size. Inside the function wcscpy or wcsncpy the string varNames.calpwstr.pElems[ID] to PropertyName. Client code:
WCHAR mPropertyName[100];
m_pCEditor->GetPropertyNameByID(ID, mPropertyName, sizeof(mPropertyName)/sizeof(mPropertyName[0]));
Think, for example, how GetComputerName API is implemented, and do the same

convert BSTR to LPCWSTR

Here is my need
BSTR l_strArgs;
LPCWSTR sth;
//----
//---
OutputDebugStringW(sth);
How to convert BSTR to LPCWSTR ?
Is there any header only library that coverts any string type(microsoft) to LPCWSTR type ?
Just cover NULL scenario and you're good to go
BSTR l_strArgs;
LPCWSTR sth = strArgs ? strArgs : L"";
As you mentioned ATL in the tag, here is ATL-style one-liner:
OutputDebugString(CString(l_strArgs));
or, to make sure you are staying in Unicode domain:
OutputDebugStringW(CStringW(l_strArgs));
I just found this one
BSTR l_strArgs;
LPCWSTR sth;
CString cs(_com_util::ConvertBSTRToString(l_strArg));
sth = cs;
OutputDebugStringW(sth);
BSTRs become easier to handle when you use a wrapper like _bstr_t instead. Here's the microsoft documentation on them
http://msdn.microsoft.com/en-us/library/zthfhkd6%28v=VS.100%29.aspx
As you would expect, one of the _bstr_t constructors takes a BSTR parameter. There is also an operator to return a const wchar_t* which you should be able to cast to LPCWSTR.
Hope this helps