The following line results in GetLastError() return error code 122 (=ERROR_INSUFFICIENT_BUFFER)
CString str = CString("'") + _T("%s") + CString("'");
But this happens only under VS2005 and doesn't happen in VS2015. Still I see no memory corruption or anything in VS2005 and the str variable does contain the correct value. Is that still an issue to be concerned about give the error code?
The reason this seems to happen is because of concatenation of wide character and simple character strings and the fix is to simply sorround both remaining strings with _T("") so the code line would look like:
CString str = CString(_T("'")) + _T("%s") + CString(_T("'"));
But what does the error code 122 really means in original line when only one string was Unicode? What wrong has really happened or is it more like a warning in this case?
GetLastError() is only meaningful after some system call has returned an error. Since your code does not have any system call, GetLastError() can return anything.
Maybe the value you see is the last error from the last system call that failed. Or maybe it is some error that happened from inside the CString class, but it is handled there.
TL;DR; No error here.
In VS 2015 you can reproduce the error with CString("a") (if Unicode is set) or just CStringW("a")
#include <iostream>
#include <atlstr.h>
int main()
{
CStringW("a");
DWORD err = GetLastError();
std::cout << err << "\n"; //<= error 122, ERROR_INSUFFICIENT_BUFFER
return 0;
}
This happens because CString uses WinAPI MultiByteToWideChar to convert ANSI "a" to Unicode L"a". Debugging through the source code in "atlmfc\include\cstringt.h", we see that at some point it calls the following function:
static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSrc) throw()
{
// Returns required buffer size in wchar_ts
return ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0 )-1;
}
For some reason there is a -1 at the end. I don't know why that's there, it might be necessary for other CString functions but in this case it ends up causing ERROR_INSUFFICIENT_BUFFER error in the next call to MultiByteToWideChar. The conversion can be roughly simplified to following:
int main()
{
int nDestLength = MultiByteToWideChar(CP_ACP, 0, "a", -1, NULL, 0) - 1;
wchar_t *pszDest = new wchar_t[32];
//ERROR_INSUFFICIENT_BUFFER occurs here because nDestLength is short by 1:
MultiByteToWideChar(CP_ACP, 0, "a", -1, pszDest, nDestLength);
DWORD err = GetLastError();
std::cout << err << "\n";
return 0;
}
nDestLength is too small because it doesn't account for null terminator. CString sorts this out later but the error remains. That's a good reason not to pay attention to GetLastError unless the function fails.
As you noted, this error can be avoided by using the _T macro, because CString would no longer need MultiByteToWideChar. Or better yet, use the L prefix, or CString::Format
CString str = CString(L"'") + L"%s" + CString(L"'");
Related
I'm trying to create a function that will convert string into wstring.
I made a loop with the most fitting codepages (in my opinion), and then I started getting this error
I've had lots of issues with codepages, and spent a lot of time resolving Each of them, and I don't pretend to look like I really understand how to deal with them, but that is the idea I got about how to handle codepage conversion(cause I don't have any idea on how to determine which codepage I use, and how to change it to a preferred one, even after weeks googling it), but now I got this error, and I don't understand why it occurs and how to fix it
Any help with my code, codepages, and how to make this function better will be highly appreciated
namespace FurConHelper
{
// to avoid adding #include <fcntl.h> header just for one value
constexpr int _O_U16TEXT = 0x20000;
}
// converts string to wstring
std::wstring stringToWstring(const std::string& stringToConvert)
{
if (stringToConvert.empty())
return wstring();
static const vector<int32_t> codepages{ CP_UTF8, CP_ACP, CP_THREAD_ACP, CP_MACCP, CP_OEMCP, CP_UTF7, CP_SYMBOL }; //MB_ERR_INVALID_CHARS CP_UTF8 CP_UTF7 CP_THREAD_ACP CP_SYMBOL CP_OEMCP CP_MACCP CP_ACP
uint32_t stringSize{};
wstring output(stringSize + 1, 0);
for (uint32_t index{}; index < codepages.size(); index++) // trying to find codepage needed to convert
{
stringSize = MultiByteToWideChar(codepages[index], MB_ERR_INVALID_CHARS, stringToConvert.c_str(), -1, NULL, 0);
if (stringSize > 0) // if size is greater than 0 - everything shall be good, hopefully
{
if (MultiByteToWideChar(codepages[index], MB_ERR_INVALID_CHARS, stringToConvert.c_str(), -1, output.data(), stringSize) > 0)
{
return output; // at this point 'stringSize' becomes 2097271 somewhy. And the 'wstring output' becomes unreadable
}
}
}
stringSize = MultiByteToWideChar(CP_UTF8, NULL, stringToConvert.c_str(), -1, NULL, 0) ;
MultiByteToWideChar(CP_UTF8, NULL, stringToConvert.c_str(), -1, output.data(), stringSize);
return output;
} // at this point I get 'Run-Time Check Failure #2 - Stack around the variable 'output' was corrupted.' error
int main()
{
UNREFERENCED_PARAMETER(_setmode(_fileno(stdout), FurConHelper::_O_U16TEXT));
UNREFERENCED_PARAMETER(_setmode(_fileno(stdin), FurConHelper::_O_U16TEXT));
UNREFERENCED_PARAMETER(_setmode(_fileno(stderr), FurConHelper::_O_U16TEXT));
if (!_wsetlocale(LC_ALL, L".utf8"))
{
wcout << L"setlocale invalid arg";
}
std::string wooffywoof{ "This string shall be converted без проблем :3 " };
wcout << stringToWstring(wooffywoof);
wcout << L"\nstill работает";
return 0;
}
Thanks for comments, and as Richard Critten, 1201ProgramAlarm and user4581301 said - I made a silly mistake with "output" variable. It was all fine, before I made a loop, and replaced "output" and "stringSize" above the first MultiByteToWideChar call. I just didn't notice that I broke the main logic of my function by this.
Thanks for pointing me into this mistake
I've been looking around the internet for some solutions however they all convert it from a constant string. Here's a piece of code I took to convert strings to wchar_t without additional libraries. What I'm trying to do is, I want to change the background of my windows computer with my background. Now I can't assume that the folder that I downloaded is in C:\Downloads because some people change their downloads folder or maybe they moved the whole folder to another location. So in the first code, I'm trying to get the path of the .exe file.
string GetExePath() {
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
string::size_type pos = string(buffer).find_last_of("\\/");
return string(buffer).substr(0, pos + 1);//gets the first character in path up to the final backslash
}
Next I'm going to grab the picture that I want to make as my background in the same folder as the .exe file.
//error on the third parameter
int return_value = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, L(string)(GetExePath() + "\\picture.png"), SPIF_UPDATEINIFILE);
Error (active) E0040 expected an identifier
Error (active) E0020 identifier "L" is undefined
Error (active) E0254 type name is not allowed
Error (active) E0018 expected a ')'
After a while, I replaced the return type of the function and so it would return wchar_t*.
const wchar_t* GetExePath() {
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
string::size_type pos = string(buffer).find_last_of("\\/");
string path = string(buffer).substr(0, pos + 1);
path += "\\HandleCamWallpaperwithgradient.png";
cout << path << endl;
wstring wide;
for (int i = 0; i < path.length(); ++i){
wide += wchar_t(path[i]);
}
const wchar_t* result = wide.c_str();
return result;
}
However, the third parameter is showing an error saying
Error E0167 argument of type "const wchar_t *" is incompatible with parameter of type "PVOID"
So how can I fix it?
Edit: Someone thought that this was a duplicate and it isn't. How to convert string to wstring in C++ is NOT correlated with this question as the one who is asking on that thread is asking help for special characters.
Call the Unicode version GetModuleFileNameW() in the first place so you don't have to convert.
Also, never return a pointer to a string that is a local variable of a function (unless it is static)! Otherwise you will be returning a dangling pointer. Instead, return a std::wstring similar to your first version. You can use std::wstring directly as the buffer by using the "pointer-to-first-character" trick.
std::wstring GetExePath() {
std::wstring buffer(MAX_PATH, L'\0'); // reserve buffer
int len = GetModuleFileNameW(NULL, &buffer[0], buffer.size() );
buffer.resize(len); // resize to actual length
string::size_type pos = buffer.find_last_of(L"\\/");
return buffer.substr(0, pos + 1);//gets the first character in path up to the final backslash
}
The second error can be fixed like this:
std::wstring path = GetExePath() + L"picture.png";
int return_value = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, &path[0], SPIF_UPDATEINIFILE);
The pvParam parameter of SystemParametersInfoW is a pointer to non-const data, so we have to use the "pointer-to-first-character" trick here again (to avoid ugly const_cast).
With C++17, this could be written as a one-liner:
int return_value = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (GetExePath() + L"picture.png").data(), SPIF_UPDATEINIFILE);
Other things to improve, left as an exercise:
Check for the error condition ERROR_INSUFFICIENT_BUFFER as described in the comments of GetModuleFileName() MSDN reference so you can support pathes longer than MAX_PATH.
I am having difficulty with the CreateDirectory function. In the following code I am getting a
"cannot convert argument 1 from 'const char *' to 'LPCWSTR'" compile error for the CreateDirectory call.
// make path to folder in program data
char szPath[MAX_PATH];
if ( ! SUCCEEDED( SHGetFolderPathA( NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath ) ) )
{
std::cout << "ERROR: Could not open server log - no common data folder " << std::endl;
exit(1);
}
std::string fname = szPath;
fname +="/Point";
CreateDirectory( fname.c_str(), NULL);
I am using Visual Studio 2015 and have "Character Set = Use Unicode Character Set".
In fileapi.h the following is defined:
#ifdef UNICODE
#define CreateDirectory CreateDirectoryW
#else
#define CreateDirectory CreateDirectoryA
#endif // !UNICODE
So I think the CreateDirectoryW function is being used
What do I need to do to get this to compile properly?
You need to use std::wstring instead of std::string in order to use wide character strings.
int main()
{
// make path to folder in program data
wchar_t szPath[MAX_PATH];
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath)))
{
std::cout << "ERROR: Could not open server log - no common data folder " << std::endl;
exit(1);
}
std::wstring fname = szPath;
fname += L"/Point";
CreateDirectory(fname.c_str(), NULL);
}
If you do not want to use wide character strings you need to explicitly call the narrow character versions of the windows API functions such as CreateDirectoryA instead of CreateDirectory.
Use CreateDirectoryA.
That said you'd be better off changing to Unicode in your application, wide text.
The original code has some problems:
// make path to folder in program data
↑ This comment is misleading: the code is about finding the path, not creating it.
char szPath[MAX_PATH];
↑ This buffer is unnecessary, instead, for this code, you should just declare the later variable std::string fname here, with specified buffer size.
if ( ! SUCCEEDED( SHGetFolderPathA( NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath ) ) )
↑ !SUCCEEEDED is a misleading rewrite of idiomatic FAILED. And SHGetFolderPath is deprecated. Instead you should be using SHGetKnownFolderPath.
{
std::cout << "ERROR: Could not open server log - no common data folder " << std::endl;
exit(1);
}
↑ The console output makes this failure handling of little value in a GUI program. Anyway, instead of cout you should be using cerr or clog (they both map to the standard error stream by default). In the exit call you should either be using a standard value such as EXIT_FAILURE, or supply the HRESULT that you got (this is Windows convention, in particular for crashing programs), or for some function, the value that you get from GetLastError. Anyway exit is far too drastic. You should be either throwing an exception or returning an optional.
std::string fname = szPath;
fname +="/Point";
↑ Forward slashes are generally supported but still the Windows convention is backslash as item separator.
CreateDirectory( fname.c_str(), NULL);
↑ The only problem that it doesn't compile with UNICODE defined before including windows.h. Use CreateDirectoryA. Or better, switch to Unicode.
I want to get the install date of product:
DWORD max = 255;
WCHAR buffer[255];
std::wstring guidWString = S::WstrToStr(subKeys[i]); //from array of std::string
LPCWSTR guid = guidWString.c_str();
int err = MsiGetProductInfo(guid, INSTALLPROPERTY_INSTALLDATE, buffer, &max);
if(err == ERROR_SUCCESS){ //never success :(
info.date = S::WstrToStr(std::wstring(buffer));
}
But I always get the error code 87 (*ERROR_INVALID_PARAMETER*).
I don't see anything "invalid" here, according to the documentation:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa370130%28v=vs.85%29.aspx
I have checked that:
All variables are of good types, buffer size (DWORD max) is not null, equal to the size of my buffer.
I use a function to convert std::string (I have my GUID in std::string) to std::wstring (under debugger, all looks good, conversion works in many other places in code that use WinAPI and std::wstring).
I have tried with different GUID, all of them exists and works for asking register "manually". MsiGetProductInfo() returns that error ALWAYS.
I have also tried to just write GUID in code (L"{GUID-GO-EXACTLY-HERE}"), with the same result.
I just don't know where the problem is?
The following piece of code seems to unreliably execute and after and undeterministic time it will fail with error code 234 at the RegEnumValue function.
I have not written this code, I am merely trying to debug it. I know there is an issue with doing RegEnumValue and then deleting keys in the while loop.
I am trying to figure out first, why it is throwing this 234 error at seemingly random points, as in, it is never after a consistent number of loop iterations or anything like that.
From what I have seen it fails to fill its name buffer, but this buffer is by no means too small for its purpose, so I don't understand how it could fail??
Could someone please advice on getting rid of this 234 error thrown by the RegEnumValue funciton?
HKEY key;
DWORD dw;
int idx;
char name[8192];
DWORD namesize=4096;
std::string m_path = "SOFTWARE\\Company\\Server 4.0";
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,m_path.c_str(),0,KEY_ALL_ACCESS,&key) == ERROR_SUCCESS)
{
bool error=false;
idx=0;
long result;
long delresult;
while (true)
{
result = RegEnumValue(key,idx,(char*)name,&namesize,NULL,NULL,NULL,NULL);
if (result == ERROR_SUCCESS && !error){
delresult = RegDeleteValue(key,name);
if (delresult != ERROR_SUCCESS)
error = true;
idx++;
}
else
{
break;
}
}
RegCloseKey(key);
}
There are some errors in your code:
The 4-th parameter of RegEnumValue (the namesize) is in-out parameter. So you have to reset namesize to sizeof(name)/sizeof(name[0]) (in case of the usage char type it is just sizeof(name)) inside the while loop before every call of RegEnumValue. It's the main error in your program.
If you don't want to have ERROR_MORE_DATA error any time you have the buffer having 32,767 characters. It is the maximum size of name the the regitry value (see documentation of RegEnumValue).
It is not good to use KEY_ALL_ACCESS in the RegOpenKeyEx. I'll recomend you to change it to KEY_QUERY_VALUE | KEY_SET_VALUE. It is not a real error, but depends on your environment it could be.
It if better to use UNICODE version of all this functions to speed-up a little the code.
UPDATED: Only small comment about the usage of the UNICODE version. Intern Windows work with UNICODE characters. So usage of non-Unicode version of RegEnumValue si more slow because at the evry call a new UICODE memeory block will be allocated and converted to ANSI/Multi-byte. Moreover if you will has a value name written in a language which can't be converted in you Windows ANSI code page (Chinese, Japanese and so on) and some characters will be replaced to '?' (see WC_DEFAULTCHAR flag of WideCharToMultiByte), then it can be that the function RegDeleteValue will fail with the error code like "the value with the name is not exist".
just change the value of your fourth parameter i.e namesize from 4096 to 8192 .Always MakeSure that it should be always equal to buffer size.
The answer is at the bottom of that page:
http://msdn.microsoft.com/en-us/library/ms724865(VS.85).aspx
Please read the answer of "ERROR_MORE_DATA: lpData too small, or lpValueName too small?" question.