My product is targeted to a Portuguese audience where the comma is the decimal symbol. I usually use CString::Format to input numbers into strings, and it takes into account the computer's regional settings. While in general this is a good approach, I'm having problems in formatting SQL queries, for instance:
CString szInsert;
szInsert.Format("INSERT INTO Vertices (X, Y) VALUES (%f, %f)", pt.X, pt.Y);
When values are passed I get this string which is an incorrect query:
INSERT INTO Vertices (X, Y) VALUES (3,56, 4,67)
How do I enforce the dot as the decimal symbol in these strings, without changing the regional settings and without having to make specialized strings for each float value?
Note: this is intended as a general question, not a SQL one.
Bad idea, you really should be using prepared statements. It's not really trivial to do SQL injection with just numbers, but CString::Format is just not the correct way to do parameter binding.
(MFC and SQL has been a while - turns out this is bloody well hidden. I'm starting to see how we ended up with SQL injection bugs, thanks Microsoft. With raw ODBC you create a statement (once) with SQLPrepare. Pass ? for the 2 parameters you want to fill in. Subsequently, for each INSERT call SQLBindParameter(stmt, 1, &X); SQLBindParameter(stmt, 2, &Y) /*extra parameters omitted, see http://msdn.microsoft.com/en-us/library/ms710963(VS.85).aspx */. Finally, call SQLExecute to preform the operation. )
A comment about Pukku's suggestion with ostringstream: For this to be locale-independent, one should explicitely imbue() the stream with the desired locale:
std::ostringstream s;
s.imbue(std::locale::classic());
s << "INSERT INTO Vertices (X, Y) VALUES (" << pt.X << ", " << pt.Y << ")";
Otherwise, the current global locale is used.
Parameterized queries should avoid this issue altogether. You should look into those. That said, you should be able to use setlocale or similar to change the decimal separator.
Use
_create_locale( LC_NUMERIC, "C" )
to create an 'English' (C default) locale and then pass this to one of the _sprintf_l group of functions.
e.g.
_locale_t locale = _create_locale( LC_NUMERIC, "C" );
_sprintf_l( pszValue, "%f", locale, 3.141 );
_free_locale(locale);
This is thread-safe. The locale can be stored in a static variable to avoid creating it every time you need to format a value.
Here's what I did.
CString FormatQuery(LPCTSTR pszFormat, ...)
{
CString szLocale = setlocale(LC_NUMERIC, NULL);
setlocale(LC_NUMERIC, "English");
va_list args;
va_start(args, pszFormat);
CString szFormatted;
int nSize = (_vscprintf(pszFormat, args) + 1) * sizeof(char);
_vsnprintf_s(szFormatted.GetBuffer(nSize), nSize, nSize, pszFormat, args);
szFormatted.ReleaseBuffer();
va_end(args);
setlocale(LC_NUMERIC, szLocale);
return szFormatted;
}
You should use it like sprintf. You must #include <locale.h> in order for it to work.
I'm a bit stubborn so I didn't use prepared statements/parametrized queries. If you have a similar problem, I suggest you do that. Meanwhile, if your problem is not SQL-related, my answer should help.
Edit: Here's a thread safe version:
CString FormatQuery(LPCTSTR pszFormat, ...)
{
_locale_t locale = _create_locale(LC_NUMERIC, "English");
va_list args;
va_start(args, pszFormat);
CString szFormatted;
int nSize = (_vscprintf_l(pszFormat, locale, args) + 1) * sizeof(char);
_vsnprintf_s_l(szFormatted.GetBuffer(nSize), nSize, nSize, pszFormat, locale, args);
szFormatted.ReleaseBuffer();
va_end(args);
return szFormatted;
}
Related
I have a timer, after which a local html file should be executed, but I hit some kind of error:
int delay = 120;
delay *= CLOCKS_PER_SEC;
clock_t now = clock();
while (clock() - now < delay);
string strWebPage = "file:///D:/project/site/scam.html";
strWebPage = "file:///" + strWebPage;
ShellExecute(NULL, NULL, NULL, strWebPage, NULL, SW_SHOWNORMAL);
return 0;
E0413 no suitable conversion function from "std::string" to "LPCWSTR" exists
I'm new to C++, so it might be an obvious solution.
Could anyone point me to how I can fix it?
You have two problems.
But first, you should always take the time to read the documentation. For Win32 functions, you can get to a known function by typing something like “msdn ShellExecute” into your favorite search engine and clicking the “Lucky” button.
Problem One
ShellExecute() is a C function. It does not take std::string as argument. It needs a pointer to characters. Hence:
std::string filename = "birds.html";
INT_PTR ok = ShellExecute(
NULL, // no window
NULL, // use default operation
filename.c_str(), // file to open
NULL, // no args to executable files
NULL, // no start directory
SW_SHOWNORMAL );
if (ok <= 32)
fooey();
Notice that we pass a const char * to the function as the file to <default verb>.
Problem Two
From your image it would appear that you have your application declared as a Unicode application. In other words, somewhere there is a #define UNICODE.
This makes ShellExecute() expect a WIDE character string (const wchar_t *)as argument, not a narrow string (const char *).
You can still use a narrow string by simply specifying that you want the narrow version:
INT_PTR ok = ShellExecuteA(
...
I recommend you look at how you set up your project to figure out how you got things to think you were using wide strings instead of narrow strings.
I have an error logging mechanism, which constructs a buffer using vsntprintf_s.
Unfortunately it so happens that if it sees a "%" symbol, then there is an exception while constructing the buffer.
TCHAR szBuffer[2000];
if (lpszFormat != NULL)
_vsntprintf_s(szBuffer, _countof(szBuffer), lpszFormat, args);
Whole function -
bool someFunction::TraceLog (LPCTSTR lpszFormat, ...)
{
va_list args = nullptr;
va_start (args, lpszFormat);
TCHAR szBuffer[2000];
if (lpszFormat != NULL)
_vsntprintf_s(szBuffer, _countof(szBuffer), lpszFormat, args);
else
_tcsncpy_s (szBuffer, _T("NULL format for TraceGen"), _TRUNCATE);
}
where, if the input string, lpszFormat contains a '%' it fails. The "%" is not meant to be an operator, but is rather for something within the string itself. E.g. Test%Test
What can be the best way to handle this?
The best way to handle this is to always have format string under your control (and by you I mean the code that you write). You must not have a format string like "Test%Test", because that is against the rules for a format string.
If you want to print that exact string then corresponding format string should be "Test%%Test".
If the contents of the string is not under your control then the format string should just be "%s" and the actual string should be given as function's next parameter.
I'm developing a tiny Win32 app in C++.
I've studied C++ fundamentals long time ago, so now I completely confused because of character strings in C++. There were no WCHAR or TCHAR only char and String.
After a little investigation I've decided not to use TCHAR.
My issue is very simple I think, but I can't find clear guide how to manipulate strings in C++. Affected by PHP coding last few years I've expected something simple with strings manipulations and was wrong!
Simply, all I need is to put new data to a character string.
WCHAR* cs = L"\0";
swprintf( cs, "NEW DATA" );
This was my first attempt. When debugging my app I've investigated that swprintf puts only first 2 chars to my cs var. I've resolved my problem this way:
WCHAR cs[1000];
swprintf( cs, "NEW DATA" );
But generally this trick could fail, because in my case new data is not constant value but another variable, that could potentialy be wider, than 1000 chars long. And my code is looks like this:
WCHAR cs[1000];
WCHAR* nd1;
WCHAR* nd2;
wcscpy(nd1, L"Some value");
wcscpy(nd2, L"Another value"); // Actually these vars stores the path for user selected folder
swprintf( cs, "The paths are %s and %s", nd1, nd2);
In this case there is possibility than nd1 and nd2 total character count could be greater than 1000 chars so critical data will be lost.
The question is how can I copy all data I need to WCHAR string declared this way WCHAR* wchar_var; without losing anything?
P.S. Since I'm Russian the question may be unclear. Let me now about that, and I'll try to explain my issue more clear and complex.
In modern Windows programming, it's OK to just ignore TCHAR and instead use wchar_t (WCHAR) and Unicode UTF-16.
(TCHAR is a model of the past, when you wanted to have a single code base, and produce both ANSI/MBCS and Unicode builds changing some preprocessor switches like _UNICODE and UNICODE.)
In any case, you should use C++ and convenient string classes to simplify your code. You can use ATL::CString (which corresponds to CStringW in Unicode builds, which are the default since VS2005), or STL's std::wstring.
Using CString, you can do:
CString str1 = L"Some value";
CString str2 = L"Another value";
CString cs;
cs.Format(L"The paths are %s and %s", str1.GetString(), str2.GetString());
CString also provides proper overloads of operator+ to concatenate strings (so you don't have to calculate the total length of the resulting string, dynamically allocate a buffer for the destination string or check existing buffer size, call wcscpy, wcscat, don't forget to release the buffer, etc.)
And you can simply pass instances of CString to Win32 APIs expecting const wchar_t* (LPCWSTR/PCWSTR) parameters, since CString offers an implicit conversion operator to const wchar_t*.
When you're using a WCHAR*, you are invoking undefined behavior because you have a pointer but have not made it point to anything valid. You need to find out how long the resulting string will be and dynamically allocate space for the string. For example:
WCHAR* cs;
WCHAR* nd1;
WCHAR* nd2;
nd1 = new WCHAR[lstrlen(L"Some value") + 1]; // +1 for the null terminator
nd2 = new WCHAR[lstrlen(L"Another value") + 1];
cs = new WCHAR[lstrlen(L"The paths are and ") + lstrlen(nd1) + lstrlen(nd2) + 1];
wcscpy(nd1, L"Some value");
wcscpy(nd2, L"Another value"); // Actually these vars stores the path for user selected folder
swprintf( cs, L"The paths are %s and %s", nd1, nd2);
delete[] nd1;
delete[] nd2;
delete[] cs;
But this is very ugly and error-prone. As noted, you should be using std::wstring instead, something like this:
std::wstring cs;
std::wstring nd1;
std::wstring nd2;
nd1 = L"Some value";
nd2 = L"Another value";
cs = std::wstring(L"The paths are ") + nd1 + L" and " + nd2;
Suggest to use ATL CStringW class instead of raw WCHAR, it's much more handy. CString is wrapper for dynamically allocated C-string. It will manage string length & allocated memory buffer appropriately after each operation so you wouldn't care on it.
Typical usage:
#include <atlstr.h>
CStringW s;
s.Format(L"The paths are %s and %s", L"Some value", L"Another value");
const WCHAR* wstr = s.GetString(); // To pass to some API that need WCHAR
or
#include <atlstr.h>
CStringW s(L"The paths are ");
s += L"Some value";
s += L" and ";
s += L"Another value";
const WCHAR* wstr = s.GetString(); // To pass to some API that need WCHAR
How can I compare a wstring, such as L"Hello", to a string? If I need to have the same type, how can I convert them into the same type?
Since you asked, here's my standard conversion functions from string to wide string, implemented using C++ std::string and std::wstring classes.
First off, make sure to start your program with set_locale:
#include <clocale>
int main()
{
std::setlocale(LC_CTYPE, ""); // before any string operations
}
Now for the functions. First off, getting a wide string from a narrow string:
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cwchar>
#include <cerrno>
// Dummy overload
std::wstring get_wstring(const std::wstring & s)
{
return s;
}
// Real worker
std::wstring get_wstring(const std::string & s)
{
const char * cs = s.c_str();
const size_t wn = std::mbsrtowcs(NULL, &cs, 0, NULL);
if (wn == size_t(-1))
{
std::cout << "Error in mbsrtowcs(): " << errno << std::endl;
return L"";
}
std::vector<wchar_t> buf(wn + 1);
const size_t wn_again = std::mbsrtowcs(buf.data(), &cs, wn + 1, NULL);
if (wn_again == size_t(-1))
{
std::cout << "Error in mbsrtowcs(): " << errno << std::endl;
return L"";
}
assert(cs == NULL); // successful conversion
return std::wstring(buf.data(), wn);
}
And going back, making a narrow string from a wide string. I call the narrow string "locale string", because it is in a platform-dependent encoding depending on the current locale:
// Dummy
std::string get_locale_string(const std::string & s)
{
return s;
}
// Real worker
std::string get_locale_string(const std::wstring & s)
{
const wchar_t * cs = s.c_str();
const size_t wn = std::wcsrtombs(NULL, &cs, 0, NULL);
if (wn == size_t(-1))
{
std::cout << "Error in wcsrtombs(): " << errno << std::endl;
return "";
}
std::vector<char> buf(wn + 1);
const size_t wn_again = std::wcsrtombs(buf.data(), &cs, wn + 1, NULL);
if (wn_again == size_t(-1))
{
std::cout << "Error in wcsrtombs(): " << errno << std::endl;
return "";
}
assert(cs == NULL); // successful conversion
return std::string(buf.data(), wn);
}
Some notes:
If you don't have std::vector::data(), you can say &buf[0] instead.
I've found that the r-style conversion functions mbsrtowcs and wcsrtombs don't work properly on Windows. There, you can use the mbstowcs and wcstombs instead: mbstowcs(buf.data(), cs, wn + 1);, wcstombs(buf.data(), cs, wn + 1);
In response to your question, if you want to compare two strings, you can convert both of them to wide string and then compare those. If you are reading a file from disk which has a known encoding, you should use iconv() to convert the file from your known encoding to WCHAR and then compare with the wide string.
Beware, though, that complex Unicode text may have multiple different representations as code point sequences which you may want to consider equal. If that is a possibility, you need to use a higher-level Unicode processing library (such as ICU) and normalize your strings to some common, comparable form.
You should convert the char string to a wchar_t string using mbstowcs, and then compare the resulting strings. Notice that mbstowcs works on char */wchar *, so you'll probably need to do something like this:
std::wstring StringToWstring(const std::string & source)
{
std::wstring target(source.size()+1, L' ');
std::size_t newLength=std::mbstowcs(&target[0], source.c_str(), target.size());
target.resize(newLength);
return target;
}
I'm not entirely sure that that usage of &target[0] is entirely standard-conforming, if someone has a good answer to that please tell me in the comments. Also, there's an implicit assumption that the converted string won't be longer (in number of wchar_ts) than the number of chars of the original string - a logical assumption that still I'm not sure it's covered by the standard.
On the other hand, it seems that there's no way to ask to mbstowcs the size of the needed buffer, so either you go this way, or go with (better done and better defined) code from Unicode libraries (be it Windows APIs or libraries like iconv).
Still, keep in mind that comparing Unicode strings without using special functions is slippery ground, two equivalent strings may be evaluated different when compared bitwise.
Long story short: this should work, and I think it's the maximum you can do with just the standard library, but it's a lot implementation-dependent in how Unicode is handled, and I wouldn't trust it a lot. In general, it's just better to stick with an encoding inside your application and avoid this kind of conversions unless absolutely necessary, and, if you are working with definite encodings, use APIs that are less implementation-dependent.
Think twice before doing this — you might not want to compare them in the first place. If you are sure you do and you are using Windows, then convert string to wstring with MultiByteToWideChar, then compare with CompareStringEx.
If you are not using Windows, then the analogous functions are mbstowcs and wcscmp. The standard wide character C++ functions are often not portable under Windows; for instance mbstowcs is deprecated.
The cross-platform way to work with Unicode is to use the ICU library.
Take care to use special functions for Unicode string comparison, don't do it manually. Two Unicode strings could have different characters, yet still be the same.
wstring ConvertToUnicode(const string & str)
{
UINT codePage = CP_ACP;
DWORD flags = 0;
int resultSize = MultiByteToWideChar
( codePage // CodePage
, flags // dwFlags
, str.c_str() // lpMultiByteStr
, str.length() // cbMultiByte
, NULL // lpWideCharStr
, 0 // cchWideChar
);
vector<wchar_t> result(resultSize + 1);
MultiByteToWideChar
( codePage // CodePage
, flags // dwFlags
, str.c_str() // lpMultiByteStr
, str.length() // cbMultiByte
, &result[0] // lpWideCharStr
, resultSize // cchWideChar
);
return &result[0];
}
So before diving into this its just that feeling that my knowledge of C(which is plain 0) will again prove painful when messing with winapi, my question is...
What is the most practical way to safely return function as TCHAR array, what i have is basically enumeration of listview items, i currently save them in the txt file and now i want to pass them all to array like
// SIMPLE VERSION I CURRENTLY HAVE
for (int nItem = 0; nItem < nMaxItems; nItem++) {
// Get the name and position of a ListView item.
for ( int subitem = 1; subitem < columns; subitem++)
{
ListView_GetItemText(hWndLV, nItem, subitem, szName, _countof(szName));
wprintf(L"%s, ", szName);
}
wprintf(L"%s", "\n");
}
Its not a problem to solve the syntax and enumeration, its just that i want to make sure i do everything right when it comes to transfering this to array, it would be best if i could perhaps cast it to wstr type somehow, how would i go around that?
I need to return the array to use it with Autoit(which gives me worries as i am not sure if when i return tchar array it will work allright, so wstr should perhaps be better)
you are using wprint which means you are either using unicode already or you are doming something really wrong.
first you should decide what you want to use: ANSI or UNICODE?
if you decide to use ANSI, you should replace your wprintf calls with printf calls like so:
printf("%s, ", szName);
printf("\n");
if you want to use unicode you should do it like this:
include wchar.h in your cpp file(s)
'wchar_t szName[200]; // or wszName if you want to be consistent on naming
ListView_GetItemTextW(hWndLV, nItem, subitem, szName, _countof(szName)); // note the W which means unicode explicitly
wprintf( L"\n"); // or wprintf(L"%s", L"\n"); if you insist on using %s'