Handle the content of a txt file using windows API - c++

I use the fragment of code as follows to get the contents of a text file. However the buffer buff at the end has only the number 8 at one place and nothing else. The file being opened has the word "Project" as the only content. How can I handle (i.e. print) the content or the result I should receive? What is wrong with the following code:
TCHAR buff[20];
DWORD dwNumRead;
CString finalPath = path + L"\\" + fileName.c_str();
HANDLE hfile=CreateFile(finalPath ,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(ReadFile(hfile,buff,20,&dwNumRead,NULL))
{
CString temp;
temp.Format(L"%s",&buff[0]);
ATLTRACE(L"Success %s", temp);
}
CloseHandle(hfile);

The trouble is that you are trying to print the MFC CString which is composed of wide character with %s macro. You need the %S macro to print the wide character.
This works :
char buff[20] = "";
DWORD dwNumRead;
CString finalPath = path + L"\\" + fileName.c_str();;
HANDLE hfile=CreateFile(finalPath ,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(ReadFile(hfile,buff,20,&dwNumRead,NULL))
{
CString temp = buff;
ATLTRACE("Success %S", temp);
}
CloseHandle(hfile);
Otherwise, compile your program in unicode with the following extra C++ defs.
UNICODE,_UNICODE

Related

Read the file version of a dll in C: The system cannot find the file specified

I am new in the forum but I have already found a lot of help for my other projects.
I am using Visual Studio 2019 and I have created a .rc file which contains the file version and a few other things. These information are displayed in the Properties window of the my dll correctly.
I have created a function
void PrintVersion(TCHAR* pszFilePath, void (*printFunc)(const char*, ...));
which receives the file path and a pointer to my logger function. Inside that function I want to read the file version and print it to the logger. But my logger returns Error in GetFileVersionInfoSize: The system cannot find the file specified.
My function call does look like this:
TCHAR* filename = L"mydll.dll";
PrintVersion(filename, gPrintFunc);
And the function is implemented as follows:
// Read the version of the dll and write it to the logger
void PrintVersion(TCHAR* pszFilePath, void (*printFunc)(const char*, ...))
{
DWORD dwSize = 0;
DWORD verHandle = 0;
BYTE* pbVersionInfo = NULL;
VS_FIXEDFILEINFO* pFileInfo = NULL;
UINT puLenFileInfo = 0;
// Get the size of the version information. This is done to check if the file is avaialbe
// If the size is zero then a error occured
dwSize = GetFileVersionInfoSize(pszFilePath, &verHandle);
if (dwSize == 0)
{
gPrintFunc("Error in GetFileVersionInfoSize: ");
PrintLastErrorString(gPrintFunc);
return;
}
// Create some memory for the file version info
pbVersionInfo = malloc(dwSize);
// Store the information into pbVersionInfo
#pragma warning(suppress : 6387)
if (!GetFileVersionInfo(pszFilePath, verHandle, dwSize, pbVersionInfo))
{
gPrintFunc("Error in GetFileVersionInfo: ");
PrintLastErrorString(gPrintFunc);
free(pbVersionInfo);
return;
}
// Make the information easier accessable in pFileInfo
#pragma warning(suppress : 6387)
if (!VerQueryValue(pbVersionInfo, TEXT("\\"), (LPVOID*)&pFileInfo, &puLenFileInfo))
{
gPrintFunc("Error in VerQueryValue: ");
PrintLastErrorString(gPrintFunc);
free(pbVersionInfo);
return;
}
// pFileInfo->dwFileVersionMS and pFileInfo->dwFileVersionLS contain the software version
// Major2B.Minor2B.Revision2B.Build2B
gPrintFunc("File Version of %s: %d.%d.%d.%d\n",
pszFilePath,
(pFileInfo->dwFileVersionMS >> 16) & 0xffff,
(pFileInfo->dwFileVersionMS >> 0) & 0xffff,
(pFileInfo->dwFileVersionLS >> 16) & 0xffff,
(pFileInfo->dwFileVersionLS >> 0) & 0xffff
);
// Free up the reserved memory
free(pbVersionInfo);
}
// Used for receiving the last WIN32 error and write it to the logger
void PrintLastErrorString(void (*printFunc)(const char*, ...))
{
// Get the error id of the last error
DWORD iLastError;
iLastError = GetLastError();
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
LPSTR messageBuffer = NULL;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, iLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
gPrintFunc("%s\n", messageBuffer);
return;
}
I created that function by combining a few different C++ and C# examples from this forum. I am not familiar with the TCHAR* datatype. I assume that the problem has maybe something to do with the filename string. Further I am not able to print the filename to the logger with the %s format placeholder. In this case only the first letter of the filename is displayed.
One further info. Before I copied that code to the dll. I created a small console application. And in this case it was possible to read the file version of the exe. I also tried to specify the complete path of the dll. The dll and the exe, which uses the dll are in the same directory.
Maybe someone can help me :)
BR
Thank you for your answers.
I changed now the character set to: Use Unicode Character Set and now it works as expected.

C++ URLDownloadToFile to executable directory in CLR forum

I'm trying to figure out how I can fix two issues that I've been having with the URLDownloadToFile function in C++. The first is that when attempting to download the file in question, the download doesn't actually appear until the CLR C++ window is closed. The second is that as shown on the image below, the resulting file's file name and extension (Which is above the success window) is messed up (Although opening it normally shows that the file downloaded fine, with the name as the exception). If anyone has any suggestions on what I could do to fix these two issues, I'd greatly appreciate it.
For this logic, I'm using:
{
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
std::string::size_type pos = std::string(buffer).find_last_of("\\/");
return std::string(buffer).substr(0, pos);
}
void StartDownload()
{
HRESULT downloadUpdate;
LPCTSTR downloadUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", File = "download.png";
string currentDirectory = GetCurrentDirectory();
LPTSTR currentDirectoryLPTSTR = new TCHAR[currentDirectory.size() + 1];
std::string(currentDirectoryLPTSTR).append(File).c_str();
downloadUpdate = URLDownloadToFile(0, downloadUrl, currentDirectoryLPTSTR, 0, 0);
switch (downloadUpdate)
{
case S_OK:
updateSuccess();
break;
case E_OUTOFMEMORY:
updateOOMError();
break;
case INET_E_DOWNLOAD_FAILURE:
updateError();
break;
default:
updateErrorUnknown();
break;
}
}
[STAThread]
int main() {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew UpdaterGUIProject::UpdaterGUI());
StartDownload();
return 0;
}
LPTSTR currentDirectoryLPTSTR = new TCHAR[currentDirectory.size() + 1]; allocates memory, but does not fill it with any data.
std::string(currentDirectoryLPTSTR) creates a new string object and tries to copy data from currentDirectoryLPTSTR into the string. Which is undefined behavior since currentDirectoryLPTSTR is not a proper null-terminated string. This code does not cause the string object to point at the memory allocated for currentDirectoryLPTSTR, like you clearly think it does.
You are then append()ing File to that string object, not to the contents of currentDirectoryLPTSTR.
And then you throw away the string object you just created, and pass the unfilled currentDirectoryLPTSTR as-is to URLDownloadToFile(). Which is why your output file has a messed-up filename (you are lucky it even shows up in the correct folder at all).
Try this instead:
std::string GetCurrentDirectory()
{
char buffer[MAX_PATH] = {};
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH);
if (size == 0 || size == MAX_PATH) return "";
std::string fileName(buffer, size);
std::string::size_type pos = fileName.find_last_of("\\/");
return fileName.substr(0, pos + 1);
}
void StartDownload()
{
HRESULT downloadUpdate;
LPCSTR downloadUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", File = "download.png";
string localFile = GetCurrentDirectory() + File;
downloadUpdate = URLDownloadToFileA(0, downloadUrl, localFile.c_str(), 0, 0);
switch (downloadUpdate)
{
case S_OK:
updateSuccess();
break;
case E_OUTOFMEMORY:
updateOOMError();
break;
case INET_E_DOWNLOAD_FAILURE:
updateError();
break;
default:
updateErrorUnknown();
break;
}
}
On a side note, you really shouldn't be downloading files to the same folder that your program is running from. If your program is installed in a folder like C:\Program Files or C:\Program Files (x86), somewhere only admins can write to, then the download will be likely to fail. You should be downloading to a folder that the user has write access to, such as to a subfolder you create under %APPDATA% for your program to use.

Pasting after SetClipboardData() in C++ does not include newlines for Notepad

I'm developing some software which copies a large string to the windows clipboard to paste into some other software. Pasting in the other software does not work, and when I paste into Notepad, the newlines in the initial strings are gone, which is why it is failing to paste in the other software. I know this because when I re-add the newlines to Notepad, and do a Copy, Pasting then works in the other program. When I paste into Wordpad, the newlines are there mysteriously.
I'm using SetClipboardData() in C++ with the CF_TEXT clipboard format type. I've tried using CF_OEMTEXT, CF_DSPTEXT but neither of those work. I saw some documentation on CF_SYLK (Symbolic Link) for spreadsheets, as the software I'm pasting in is similar to a spreadsheet, but I couldn't get that to work either. Below is my code for copying to the clipboard.
void ClipBoardManager::CopyExcelStringToClipBoard(std::string excel_str)
{
OpenClipboard(nullptr);
EmptyClipboard();
HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, excel_str.size() + 1);
if (!hg) {
CloseClipboard();
return;
}
memcpy(GlobalLock(hg), excel_str.c_str(), excel_str.size() + 1);
GlobalUnlock(hg);
SetClipboardData(CF_TEXT, hg);
CloseClipboard();
GlobalFree(hg);
}
Any help is appreciated.
The excel_str must have the CRLF line endings. Here is example code to convert string to the good format:
string replaceAll(string in, string replaceIn, string replaceOut)
{
size_t pos = 0;
while(pos < in.size())
{
size_t pos2 = in.find(replaceIn, pos);
if(pos2 != string::npos)
{
in.replace(in.begin() + pos2, in.begin() + pos2 + replaceIn.size(), replaceOut);
pos = pos2 + replaceOut.size();
}
else
break;
}
return in;
}
If your project setup for unicode characters (default setup) - use unicode everywhere and use CF_UNICODETEXT instead CF_TEXT. Or use non unicode - but consistently - and then change project settings.
Code below will copy text with line endings correctly - after end of this program it's possible to paste copied by this program text (with line endings) from clipboard from say notepad:
#include <Windows.h>
BOOL WINAPI ToClipboard(VOID);
int main()
{
ToClipboard();
}
BOOL WINAPI ToClipboard(VOID)
{
LPTSTR lptstrCopy;
HGLOBAL hglbCopy;
if (!OpenClipboard(NULL))
return FALSE;
EmptyClipboard();
// Allocate a global memory object for the text.
wchar_t s[] = L"12345\n6789";
hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
(wcslen(s) + 1) * sizeof(wchar_t));
if (hglbCopy == NULL)
{
CloseClipboard();
return FALSE;
}
lptstrCopy = (LPTSTR)GlobalLock(hglbCopy);
memcpy(lptstrCopy, &s,
(wcslen(s) + 1) * sizeof(wchar_t));
lptstrCopy[sizeof(s)] = (TCHAR)0; // null character
GlobalUnlock(hglbCopy);
SetClipboardData(CF_UNICODETEXT, hglbCopy);
CloseClipboard();
return TRUE;
}

c++ read config file parameters using GetPrivateProfileString

I have a win32 console application in C++, Visual Studio 2012. I cannot get the config parameter from the ini file. I tried different possibilities with file path,like placing the .ini file in source folder, write the full path to is, placing in the folder with generated .exe file. The output on the console after executing of the .exe file is 0 or (null) for string in every case. What I am doing wrong? How can I read the parameters?
v1:
LPCTSTR path = L".\\config.ini";
TCHAR protocolChar[32];
int port = GetPrivateProfileString(_T("PORT"), _T("SETTINGS"), _T(""), protocolChar, 32, path);
printf("***%d***\n", port);
v2:
int port = GetPrivateProfileInt(_T("PORT"), _T("SETTINGS"), 0, _T("config.ini"));
config.ini contains:
[SETTINGS]
USER_NUM_MAX = 256 ; Maximum number of users
PORT = 8080;
Oups, under Windows hitting a ini file in not that easy. In both tries (v1 and v2), you look for the file in current directory and then in Windows directory but not in the directory where the executable file is.
The easy way is to put all ini files under Windows directory. If you find cleaner to have the ini file along with the exe one, you have some more work to do :
find the executable file full path
replace the exe end with ini
use that full path to get access to your private ini file
To get the name of the executable file, simply use GetModuleFileName with a NULL HMODULE :
LPCTSTR getExecPath() {
DWORD len = 64;
for (;;) {
LPTSTR fileName = new TCHAR[len];
if (len == ::GetModuleFileName(NULL, fileName, len)) {
delete fileName;
len *= 2;
}
else {
return fileName;
}
}
}
or if you prefere to directly get the ini file name :
LPCTSTR getIniName() {
DWORD len = 4;
for (;;) {
LPTSTR fileName = new TCHAR[len];
if (len == ::GetModuleFileName(NULL, fileName, len)) {
delete fileName;
len *= 2;
}
else {
::lstrcpy(fileName + lstrlen(fileName) - 3, "ini");
return fileName;
}
}
}
and to not forget to delete the file name when done since it is allocated with new ...
Edit per comment :
For reference, the windows directory may depend on windows version. But it can always be retrieved by the API function GetWindowsDirectory. Extract from the reference page :
UINT WINAPI GetWindowsDirectory(
_Out_ LPTSTR lpBuffer,
_In_ UINT uSize
);
Parameters
lpBuffer [out] A pointer to a buffer that receives the path. This path does not end with a backslash unless the Windows directory is the root directory. For example, if the Windows directory is named Windows on drive C, the path of the Windows directory retrieved by this function is C:\Windows. If the system was installed in the root directory of drive C, the path retrieved is C:.
uSize [in] The maximum size of the buffer specified by the lpBuffer parameter, in TCHARs. This value should be set to MAX_PATH.
Return value
If the function succeeds, the return value is the length of the string copied to the buffer, in TCHARs, not including the terminating null character.
If the length is greater than the size of the buffer, the return value is the size of the buffer required to hold the path.
If the function fails, the return value is zero. To get extended error information, call GetLastError. *
I faced this problem when I updated from VS2010 to VS2012.
On VS 2010 I simply called the function with the file name of the .ini-file as argument for lpFileName (see MSDN Documentation).
This was not working for VS 2012 any more, so I changed to go for the complete path like this:
char directoryPath[MAX_PATH];
char readParameter[MAX_STR_LEN];
GetCurrentDirectory( directoryPath, MAX_PATH );
string directoryPathAsString(directoryPath);
directoryPathAsString = directoryPathAsString + "\\" + filename;
GetPrivateProfileString("section","parameter","0",readParameter,MAX_STR_LEN, directoryPathAsString.c_str());

List files in directory C++

I'm trying to use this example
to create a program which will list all filenames in a directory in a Windows::Forms::ListBox.
Since the user won't be doing any inputting I won't be needing the
void DisplayErrorBox(LPTSTR lpszFunction) function along with other error checking.
When I click the button that triggers the event this is what is shown in the list box.
o //&#o/
/ //
/ //
/ //
/ //
/ //
/ //
/ //
Also, only one row appear each time i click the button.
It's supposed to find all the files in the directory and list them not just find the next file each time I click the button.
I also want to use a relative strPath, not absolute...
So far this is what I've done with the code:
private:
void List_Files()
{
std::string strPath = "C:\\Users\\Andre\\Dropbox\\Programmering privat\\Diablo III DPS Calculator\\Debug\\SavedProfiles";
TCHAR* Path = (TCHAR*)strPath.c_str();
WIN32_FIND_DATA ffd;
LARGE_INTEGER filesize;
TCHAR szDir[MAX_PATH];
size_t length_of_arg;
HANDLE hFind = INVALID_HANDLE_VALUE;
// Prepare string for use with FindFile functions. First, copy the
// string to a buffer, then append '\*' to the directory name.
StringCchCopy(szDir, MAX_PATH, Path);
StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
// List all the files in the directory with some info about them.
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
//If it's a directory nothing should happen. Just continue with the next file.
}
else
{
//convert from wide char to narrow char array
char ch[260];
char DefChar = ' ';
WideCharToMultiByte(CP_ACP,0,(ffd.cFileName),-1, ch,260,&DefChar, NULL);
//A std:string using the char* constructor.
std::string str(ch);
String ^ sysStr = gcnew String(str.c_str());
MessageBox::Show("File Found", "NOTE");
ListBoxSavedFiles->Items->Add (sysStr);
}
}
while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
FindFirstFile() is never called, you need to call it before calling FindNextFile():
HANDLE hFind = FindFirstFile(TEXT("C:\\Users\\Andre\\Dropbox\\Programmering privat\\Diablo III DPS Calculator\\Debug\\SavedProfiles\\*"), &ffd);
if (INVALID_HANDLE_VALUE != hFind)
{
do
{
//...
} while(FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
}
else
{
// Report failure.
}
If you do not mind using Boost, you can use a directory_iterator:
using boost::filesystem;
path p("some_dir");
for (directory_iterator it(p); it != directory_iterator(); ++it) {
cout << it->path() << endl;
}
It works on Windows too and it definitely looks much simpler. Of course, you would need to adapt your current code a little bit, but I think in the long term it is well worth the effort.
The cast (TCHAR*)strPath.c_str(); is wrong. From your use of WideCharToMultiByte I know that (TCHAR*)strPath.c_str(); is casting a char const* to a wchar_t*. Not only does that lose a const, but the width is also wrong.
If u are using Visual Studio then change the configuration settings to Use Multibyte Character set. This will your TCHAR thing to compile without any cast.