How to display national characters in a Windows desktop app - c++

A Windows C++ desktop application displays texts with Swedish national characters. I develop it in Visual Studio (VS), as a "C++ Windows Desktop" project. VS builds a basic application with many files: one .cpp file, two .ico files, three .h files, one .rc file, and some project files, so it is too much to present here, and I'm showing only the important parts.
In wWinMain, I added a console to display texts using cout.
AllocConsole();
SetConsoleOutputCP(1252);
FILE* stream;
freopen_s(&stream, "CONOUT$", "w", stdout);
I am trying several ways to display text in WndProc in response to the WM_PAINT signal.
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT rec;
GetClientRect(hWnd, &rec);
if (FIRST)
{
FIRST = false;
// Test with const char* s
const char* t1 = "Test 1: const char* s";
DrawTextA(hdc, (LPCSTR)t1, (int)strlen(t1), &rec, DT_TOP | DT_LEFT);
cout << t1 << endl;
rec.top += 16;
const char* s = "abcdef ååääöö ÅÅÄÄÖÖ\n";
DrawTextA(hdc, (LPCSTR)s, (int)strlen(s), &rec, DT_TOP | DT_LEFT);
cout << s << endl;
rec.top += 16;
// Test with string text
const char* t2 = "Test 2 string text";
DrawTextA(hdc, (LPCSTR)t2, (int)strlen(t2), &rec, DT_TOP | DT_LEFT);
cout << t2 << endl;
rec.top += 16;
string text = "abcdef ååääöö ÅÅÄÄÖÖ\n";
DrawTextA(hdc, (LPCSTR)&text, (int)text.length(), &rec, DT_TOP | DT_LEFT);
cout << text << endl;
rec.top += 16;
// Test with text (const char*) & string
const char* t3 = "Test 3 (const char*)& \"..\"";
DrawTextA(hdc, (LPCSTR)t3, (int)strlen(t3), &rec, DT_TOP | DT_LEFT);
cout << t3 << endl;
rec.top += 16;
text = "abcdef ååääöö ÅÅÄÄÖÖ\n";
const char* t = (const char*)&text;
DrawTextA(hdc, (LPCSTR)t, (int)strlen(t), &rec, DT_TOP | DT_LEFT);
cout << t << endl;;
rec.top += 16;
// Test with wide string text
const char* t4 = "Test 4 wstring text";
DrawTextA(hdc, (LPCSTR)t4, (int)strlen(t4), &rec, DT_TOP | DT_LEFT);
cout << t4 << endl;
rec.top += 16;
wstring wtext = L"abcdef ååääöö ÅÅÄÄÖÖ\n";
DrawTextW(hdc, (LPCWSTR)&wtext, (int)wtext.length(), &rec, DT_TOP | DT_LEFT);
wcout << wtext << endl;
rec.top += 16;
}
EndPaint(hWnd, &ps);
}
break;
The meaning of this is to verify how national characters are displayed using text sources in const char*, string, wstring, and const char* created from a string. The output in the desktop client window and in the console is presented below.
The output is correct when DrawTextA displays text as const char*. However, the displayed text is distorted when DrawTextA shows text from a string or const char* created from a string. Also, using wstring didn't help.
In my application, I must be able to display texts kept in strings. I don't care much about the console output; it is only there to make the testing easier. What am I missing?

Related

How can I get the full text at SDL? (Not console)

I am creating a menu for a game at C++ and I have a problem when I read a text file (.cfg), and I want to show it to the screen (With SDL,not console).
The problem is that in SDL, I only get the last line. Why is that?
Here is my code:
fstream characters;
characters.open("characters.cfg", ios::in);
while (getline(characters, line))
cout << line << endl;
And I create a string to show it with SDL_ttf:
void renderUI() {
SDL_Surface* textSurface;
SDL_Texture* textTexture;
SDL_Rect textRect;
SDL_Color white = { 255,255,255 };
textRect.w = 250;
textRect.h = 20;
textRect.x = 150;
textRect.y = 200;
string names = line;
textSurface = TTF_RenderText_Blended(gameFont, names.c_str(), white);
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (textSurface == NULL)
{
cout << "TTF_RenderText_Solid() Failed: " << TTF_GetError() << endl;
TTF_Quit();
SDL_Quit();
exit(1);
}
SDL_RenderCopy(renderer, textTexture, NULL, &textRect);
}
And here a photo to show that I get the full text in the console, but not in SDL:
EXAMPLE PHOTO:
I have the solution. Here is the code:
fstream characters;
string line;
string text;
characters.open("characters.cfg", ios::in);
while (getline(characters, line))
{
text = text + line + "\n";
}
characters.close();
And here´s the code of SDL_ttf and the solution to the line break problem with SDL:
SDL_Surface* textSurface;
SDL_Texture* textTexture;
SDL_Rect textRect;
SDL_Color white = { 255,255,255 };
textRect.w = 250;
textRect.h = 400;
textRect.x = 150;
textRect.y = 185;
textSurface = TTF_RenderText_Blended_Wrapped(gameFont, text.c_str(), white, 500);
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
// If TTF_RenderText_Solid() Fail
if (textSurface == NULL)
{
cout << "TTF_RenderText_Solid() Failed: " << TTF_GetError() << endl;
TTF_Quit();
SDL_Quit();
exit(1);
}
Used this for the line break problem with SDL_TTF:
TTF_RenderText_Blended_Wrapped for be able to use line break

WaitForSingleObject with FindFirstChangeNotification not behaving as expected [duplicate]

This question already has answers here:
FindFirstChangeNotification is notifying about changes twice
(2 answers)
Closed 1 year ago.
I have to find out if there is a new file in a directory on Windows. Following this MSDN example (Obtaining Directory Change Notifications), I came up with the following test program:
#include <iostream>
#include <Windows.h>
#include <vector>
#include <string>
std::string FindNewFile(std::vector<std::string>& vsNewFileList, std::vector<std::string>& vsOldFileList)
{
std::string sNewFileName;
int nScore = 0;
for (auto& newFile : vsNewFileList)
{
nScore = 0;
for (auto& oldFile : vsOldFileList)
if(!newFile.compare(oldFile))
nScore++;
if (nScore!=1)
{
sNewFileName = newFile;
break;
}
}
return sNewFileName;
}
void GetCurrentFilesInDir(std::string sDir, std::vector<std::string>& vsFileList)
{
WIN32_FIND_DATA ffd;
sDir += "\\*";
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
HANDLE hFind = FindFirstFile(lpcwsDir, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
std::cout << "Nope\n";
return;
}
vsFileList.clear();
do
{
int nSize = WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, 0, 0, 0, 0);
char* pcStr = new char[nSize];
WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, pcStr, nSize, 0, 0);
//std::cout << pcStr << "\n";
vsFileList.push_back(std::string(pcStr));
delete[] pcStr;
} while (FindNextFile(hFind, &ffd) != 0);
}
int main()
{
// watch the foo directory for new files
std::string sDir = "C:\\foo";
std::vector<std::string> vsOldFileList, vsNewFileList;
GetCurrentFilesInDir(sDir, vsOldFileList);
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
DWORD dwWaitStatus;
HANDLE dwChangeHandle;
dwChangeHandle = FindFirstChangeNotification(
lpcwsDir,
FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME);
while (TRUE)
{
// returns multiple times before and after new file appears!!
dwWaitStatus = WaitForSingleObject(dwChangeHandle, INFINITE);
switch(dwWaitStatus)
{
case WAIT_OBJECT_0:
GetCurrentFilesInDir(sDir, vsNewFileList);
std::string sNewFileName = FindNewFile(vsNewFileList, vsOldFileList);
std::cout << sNewFileName << "\n";
GetCurrentFilesInDir(sDir, vsOldFileList);
FindNextChangeNotification(dwChangeHandle);
break;
}
}
}
The problem is that, when I save a new file in C:\foo (for instance, using Notepad++ to "Save As" an open .txt file in C:\foo), the call to WaitForSingleObject() in the while loop will return 0 multiple times. Since my FindNewFile() method returns an empty string if there is no new file in the directory, I will get output like:
a.txt
or:
b.txt
Or even:
c.txt
c.txt
Can someone explain what I am missing here?
Using FindNextChangeNotification can not tell you what actually happened, and the operation of the file may involve multiple changes.
You can try to use ReadDirectoryChangesW and here is a sample:
#include <windows.h>
#include <iostream>
using namespace std;
wstring getname(FILE_NOTIFY_INFORMATION* tmp)
{
wstring s = L"";
for (int i = 0; i < tmp->FileNameLength / 2; i++)
s += tmp->FileName[i];
return s;
}
int main(int argc, const char* argv[])
{
HANDLE hDir;
char notify[1024];
DWORD cbBytes;
LPTSTR path;
FILE_NOTIFY_INFORMATION* pnotify = (FILE_NOTIFY_INFORMATION*)notify;
FILE_NOTIFY_INFORMATION* tmp;
// GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
path = (LPTSTR)L"D:\\test";
hDir = CreateFile(path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
wcout << L"===CreateFile complete===" << endl;
if (hDir == INVALID_HANDLE_VALUE)
{
wcout << L"invalid handle value" << endl;
return -1;
}
FILE_NOTIFY_INFORMATION buffer[1024];
FILE_NOTIFY_INFORMATION* pbuffer;
while (TRUE)
{
wcout << L"waiting..." << endl;
WaitForSingleObject(hDir, INFINITE);
if (ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME,
&cbBytes, NULL, NULL))
{
pbuffer = buffer;
do {
tmp = pbuffer;
switch (tmp->Action)
{
case FILE_ACTION_ADDED:
wcout << L"Directory/File added - " << getname(tmp) << endl;
break;
case FILE_ACTION_REMOVED:
wcout << L"Directory/File removed - " << getname(tmp) << endl;
break;
case FILE_ACTION_MODIFIED:
wcout << L"Directory/File modfied - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wcout << L"Directory/File old name - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wcout << L"Directory/File new name - " << getname(tmp) << endl;
break;
default:
wcout << L"unknown action\n" << endl;
break;
}
pbuffer += pbuffer->NextEntryOffset;
} while (pbuffer->NextEntryOffset);
}
else
{
wcout << "readChangesW failed now return" << endl;
return -1;
}
}
}
When you do the Save As operation, you will find:
Therefore, multiple file operations were triggered when actually saving as, and you also performed multiple comparisons when processing new file comparisons, so empty characters were output.
More reference: FindFirstChangeNotification is notifying about changes twice

Getting the color of a pixel at a specific x,y coordinate, then executing a click

I very recently got into C++, and am basically learning through watching videos and reverse-engineering other people's code. Needless to say, I'm not very good at this stuff yet.
Anyway, I'm working on a program that detects if a specific color (RGB) is at a specific coordinate in another window, and if it is, the code executes a click.
This is what I put together
#include <windows.h>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
void BigClick()
{
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; //push
::SendInput(1, &Input, sizeof(INPUT));
::ZeroMemory(&Input, sizeof(INPUT)); //or NULL?
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTUP; //release
::SendInput(1, &Input, sizeof(INPUT));
}
int main()
{
COLORREF colortosearch = RGB(0, 0, 255); // color to search
HDC hdcScreen = GetDC(NULL);
int x = 954;
int y = 540;
while (true)
{
if (::GetPixel(hdcScreen, x, y) == colortosearch)
{
// start code to click
cout << "one click completed";
BigClick();
}
}
::ReleaseDC(NULL, hdcScreen);
return 0;
}
The code compiles but it does not click even when the entire screen is blue or RGB(0,0,255). I know BigClick() clicks, since I tested it by itself to make sure. What am I missing here? I'm thinking I'm not giving GetPixel the coordinates to check in the right way, but since I'm so new it could be anything as far as I know.
I changed the fixed coordinates to follow the cursor position and your program seems to work fine. Clicks are also executed!
int main()
{
COLORREF colortosearch = RGB(0, 0, 255); // color to search
HDC hdcScreen = GetDC(NULL);
while (true)
{
POINT cursor;
GetCursorPos(&cursor);
COLORREF color = ::GetPixel(hdcScreen, cursor.x, cursor.y);
if (color == colortosearch) {
// start code to click
cout << "one click completed";
BigClick();
}
else {
int red = GetRValue(color);
int green = GetGValue(color);
int blue = GetBValue(color);
cout << "x: " << cursor.x << ", y:" << cursor.y << " --> ";
cout << "(" << red << ", " << green << ", " << blue << ")\r\n";
}
}
::ReleaseDC(NULL, hdcScreen);
return 0;
}

c++ renaming filename other language

I want to rename some of the files ,
The names of some of the files are Russian, Chinese, and German
The program can only modify files whose name is English.
What is the problem ? please guide me
std::wstring ToUtf16(std::string str)
{
std::wstring ret;
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
if (len > 0)
{
ret.resize(len);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len);
}
return ret;
}
int main()
{
const std::filesystem::directory_options options = (
std::filesystem::directory_options::follow_directory_symlink |
std::filesystem::directory_options::skip_permission_denied
);
try
{
for (const auto& dirEntry :
std::filesystem::recursive_directory_iterator("C:\\folder",
std::filesystem::directory_options(options)))
{
filesystem::path myfile(dirEntry.path().u8string());
string uft8path1 = dirEntry.path().u8string();
string uft8path3 = myfile.parent_path().u8string() + "/" + myfile.filename().u8string();
_wrename(
ToUtf16(uft8path1).c_str()
,
ToUtf16(uft8path3).c_str()
);
std::cout << dirEntry.path().u8string() << std::endl;
}
}
catch (std::filesystem::filesystem_error & fse)
{
std::cout << fse.what() << std::endl;
}
system("pause");
}
filesystem::path myfile(dirEntry.path().u8string());
Windows supports UTF16 and ANSI, there is no UTF8 support for APIs (not standard anyway). When you supply UTF8 string, it thinks there is ANSI input. Use wstring() to indicate UTF16:
filesystem::path myfile(dirEntry.path().wstring());
or just put:
filesystem::path myfile(dirEntry);
Likewise, use wstring() for other objects.
wstring path1 = dirEntry.path();
wstring path3 = myfile.parent_path().wstring() + L"/" + myfile.filename().wstring();
_wrename(path1.c_str(), path3.c_str());
Renaming the files will work fine when you have UTF16 input. But there is another problem with console's limited Unicode support. You can't print some Asian characters with font changes. Use the debugger or MessageBoxW to view Asian characters.
Use _setmode and wcout to print UTF16.
Also note, std::filesystem supports / operator for adding path. Example:
#include <io.h> //for _setmode
#include <fcntl.h>
...
int main()
{
_setmode(_fileno(stdout), _O_U16TEXT);
const std::filesystem::directory_options options = (
std::filesystem::directory_options::follow_directory_symlink |
std::filesystem::directory_options::skip_permission_denied
);
try
{
for(const auto& dirEntry :
std::filesystem::recursive_directory_iterator(L"C:\\folder",
std::filesystem::directory_options(options)))
{
filesystem::path myfile(dirEntry);
auto path1 = dirEntry;
auto path3 = myfile.parent_path() / myfile;
std::wcout << path1 << ", " << path3 << endl;
//filesystem::rename(path1, path3);
}
}
...
}

Displaying japanese characters in visual c++

Does anyone here have an idea how to work with japanese character in visual c++?
I'm trying to display a Japanese name in console with visual c++.
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
int main()
{
cout << "北島 美奈" << endl;
return 0;
}
Output in the console:
?? ??
Press any key to continue ...
Hope someone can help. Thank you.
I've tested with my own code both UTF-8 and EUC-KR(korean) on a console window using a cmd.exe.
This is my source code.
#include <string>
#include <iostream>
#include <windows.h>
int main()
{
int codepage = CP_ACP; //CP_ACP, CP_OEMCP
int conv_codepage = CP_UTF8; //CP_UTF8
char str[256];
char str1[256];
wchar_t tstr[256], tstr2[256];
memset(str, 0x00, 256);
memset(str1, 0x00, 256);
memset(tstr, 0x00, 256);
memset(tstr2, 0x00, 256);
memcpy(str, " 北島 美奈", sizeof(str));
int nLen = MultiByteToWideChar(codepage, 0, str, -1, 0, 0);
MultiByteToWideChar(codepage, 0, str, -1, tstr, nLen);
int len = WideCharToMultiByte( conv_codepage, 0, tstr, -1, NULL, 0, 0, 0 );
WideCharToMultiByte(conv_codepage, 0, tstr, -1, str1, len ,0 ,0);
cout << "2... " << str1 << endl;
return 0;
}
case 1 UTF-8: the result on a console
The output is reasonable because the str1 variable is an utf-8 string.
I've got a correct utf-8 on a utf-8 console window.
case 2 EUC-KR: the result on a console
I think this case is also acceptable utf-8 string with a utf-8 string.
Then changing the code as follows
cout << "2... " << str << endl;
to
cout << "2... " << str1 << endl;
case 1 UTF-8: the result on a console
I think this is okey to me for an unicode string on a utf-8 console.
case 1 EUC-KR: the result on a console
It is still correct unicode string in a euc-kr codepage.