Bitmap file fails to open - c++

I run the following command and it always returns null as the Bitmap file fails to open for some reason. Please help!
const XCHAR* szFilePathW = L"C:\\Users\\Simrat\\Desktop";
std::ofstream bmpF;
char szFilePathA[MSO_MAX_PATH]; // std::ofstream.open() takes char* in Android C++ compiler, whereas it takes both char* and wchar* in VC++
WideCharToMultiByte(CP_UTF8, 0, szFilePathW, -1, szFilePathA, MSO_MAX_PATH, NULL, NULL);
bmpF.open(szFilePathA, std::ofstream::binary | std::ofstream::out);
if (!bmpF.is_open())
return null;
The functionality of the WideCharToMultiByte() function could be found here:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd374130(v=vs.85).aspx

As commented, you appear to be missing a filename. And the code should simply be:
const WCHAR* szFilePath = L"C:\\Users\\Simrat\\Desktop\\name_of_bitmap_file_I_want_to_create.bmp";
std::ofstream bmpF;
bmpF.open (szFilePath, std::ofstream::binary | std::ofstream::out);
No need to convert to multibyte, see MSDN (note that open has a const wchar_t * overload for the filename parameter - on Windows).

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.

How can I create a file when file path name is over 255 characters using MFC in Windows?

I am working in Windows,using vc++2010 and MFC.
Following is my code:
CFile File;
TCHAR lpCause[1024];
CFileException eException;
CString strErrorMessage;
// a very long file path name means a file name over 255 characters
if (!File.Open(_T("a very long file path name"), CFile::modeCreate, &eException))
{
eException.GetErrorMessage(lpCause, 1024);
strErrorMessage = lpCause;
}
else
File.Close();
When I run the code, I got error message:"a very long file path name contains an incorrect path".
My questions are:
How to modify my code to make it work?
I learn that CreateFile() function can add "\\\\?\" in the beginning of file path, then it will extend this limit to 32767 wide characters.How can I do the same thing in MFC?
Cause
In the source of CFile::Open(), there is an explicit check if the path length exceeds _MAX_PATH:
if (lpszFileName != NULL && SUCCEEDED(StringCchLength(lpszFileName, _MAX_PATH, NULL)) )
If _MAX_PATH is exceeded, the function sets pException->m_cause = CFileException::badPath and returns FALSE.
This is true even for the MFC version that comes with VS2017.
So the standard technique to circumvent the _MAX_PATH limit, that is prefixing the path with \\?\ won't work.
Possible Solutions
Call CreateFileW() directly to pass it a path with \\?\ prefix. Use the CFile constructor that accepts a HANDLE to manage the file through a CFile object. The CFile object will take ownership of the handle so you must not call CloseHandle() on the handle.
HANDLE hFile = CreateFileW( L"\\\\?\\a very long file path name", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, NULL );
if( hFile != INVALID_HANDLE_VALUE )
{
// Manage the handle using CFile
CFile file( hFile );
// Use the file...
// The CFile destructor closes the handle here.
}
else
{
DWORD err = GetLastError();
// TODO: do your error handling...
}
Another possibility is to derive a class from CFile that overrides CFile::Open() (which is virtual). For the implementation copy/paste the MFC source, but leave out the _MAX_PATH check. For a big project, this class could be a drop-in replacement for CFile to enable long paths. You could even go so far to prepend the \\?\ prefix if it isn't already there (but that is more involved as the prefix also disables the regular transformations from a Win32 path to a NT-style path, like converting / to \, resolving dots and so on).

Open utf8 encoded filename in c++ Windows

Consider the following code:
#include <iostream>
#include <boost\locale.hpp>
#include <Windows.h>
#include <fstream>
std::string ToUtf8(std::wstring str)
{
std::string ret;
int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL);
if (len > 0)
{
ret.resize(len);
WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL);
}
return ret;
}
int main()
{
std::wstring wfilename = L"D://Private//Test//एउटा फोल्दर//भित्रको फाईल.txt";
std::string utf8path = ToUtf8(wfilename );
std::ifstream iFileStream(utf8path , std::ifstream::in | std::ifstream::binary);
if(iFileStream.is_open())
{
std::cout << "Opened the File\n";
//Do the work here.
}
else
{
std::cout << "Cannot Opened the file\n";
}
return 0;
}
If I am running the file, I cannot open the file thus entering into the else block. Even using boost::locale::conv::from_utf(utf8path ,"utf_8") instead of utf8path doesn't work. The code works if I consider using wifstream and using wfilename as its parameter, but I don' want to use wifstream. Is there any way to open the file with its name utf8 encoded? I am using Visual Studio 2010.
On Windows, you MUST use 8bit ANSI (and it must match the user's locale) or UTF-16 for filenames, there is no other option available. You can keep using string and UTF-8 in your main code, but you will have to convert UTF-8 filenames to UTF-16 when you are opening files. Less efficient, but that is what you need to do.
Fortunately, VC++'s implementation of std::ifstream and std::ofstream have non-standard overloads of their constructors and open() methods to accept wchar_t* strings for UTF-16 filenames.
explicit basic_ifstream(
const wchar_t *_Filename,
ios_base::openmode _Mode = ios_base::in,
int _Prot = (int)ios_base::_Openprot
);
void open(
const wchar_t *_Filename,
ios_base::openmode _Mode = ios_base::in,
int _Prot = (int)ios_base::_Openprot
);
void open(
const wchar_t *_Filename,
ios_base::openmode _Mode
);
explicit basic_ofstream(
const wchar_t *_Filename,
ios_base::openmode _Mode = ios_base::out,
int _Prot = (int)ios_base::_Openprot
);
void open(
const wchar_t *_Filename,
ios_base::openmode _Mode = ios_base::out,
int _Prot = (int)ios_base::_Openprot
);
void open(
const wchar_t *_Filename,
ios_base::openmode _Mode
);
You will have to use an #ifdef to detect Windows compilation (unfortunately, different C++ compilers identify that differently) and temporarily convert your UTF-8 string to UTF-16 when opening a file.
#ifdef _MSC_VER
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;
}
#endif
int main()
{
std::string utf8path = ...;
std::ifstream iFileStream(
#ifdef _MSC_VER
ToUtf16(utf8path).c_str()
#else
utf8path.c_str()
#endif
, std::ifstream::in | std::ifstream::binary);
...
return 0;
}
Note that this is only guaranteed to work in VC++. Other C++ compilers for Windows are not guaranteed to provide similar extensions.
UPDATE: as of Windows 10 Insider Preview Build 17035, Microsoft now supports UTF-8 as a system-wide encoding that users can set their locale to. And as of Windows 10 Version 1903 (build 18362), applications can now opt in via their app manifest to use UTF-8 as a process-wide codepage, even if the user locale is not set to UTF-8. These features allow ANSI-based APIs (like CreateFileA(), which std::ifstream/std::ofstream use internally) to work with UTF-8 strings. So, in theory, with this feature turned on, you might be able to pass a UTF-8 encoded string to std::ifstream/std::ofstream and it would "just work". I can't confirm that, as it very much depends on the implementation. It would be safer to stick with passing in UTF-16 filenames, since that is Windows' native encoding, which the ANSI APIs will simply convert to internally.
You can use std::filesystem::u8path in C++14/17:
std::filesystem::path pa = std::filesystem::u8path((const char*)yourStdStringPath.c_str());
std::ofstream ofs(pa);
It's deprecated in C++20 since you can use the u8 prefix.

C++ Downloading a file to appdata

I'm trying to download a file to
char* appdata = getenv("APPDATA");
lpURLDownloadToFile URLDownloadToFile;
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip",appdata+"test.zip", 0, 0);
I don't have an error when I write the path manually,But ı got error when I try to use appdata+"test.zip"
How can I do it. Thanks.
You cannot combine char* pointers by adding them like so, you need to concatenate those strings. You might want to use std:string instead which support operator+=.
std::string appdata(getenv("APPDATA"));
appdata += "test.zip";
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip", appdata.c_str(), 0, 0);

C++ Get Program files dir, append extra path and execute

I'm trying to write a few simple lines of code that will get the 'Program Files' dir path on both XP and Vista/7 (on vista/7 I need the path to the x86 folder), add some extra path to an application and execute it.
This is what I have so far, but it's not executing the external program, not giving an error as well..
wchar_t localAppData[MAX_PATH];
STARTUPINFO sInfo;
PROCESS_INFORMATION pInfo;
SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, 0, NULL, localAppData);
std::wstringstream ss;
ss << localAppData << L"/MyApp/MyExe.exe";
LPCWSTR str = ss.str().c_str();
CreateProcess(str, NULL,NULL, NULL,FALSE,NULL,NULL,NULL,&sInfo,&pInfo);
return str;
I've updated my code to this according to hmjd's suggestion:
wchar_t localAppData[MAX_PATH];
STARTUPINFO sInfo = { sizeof(STARTUPINFO), NULL, L"winsta0\\default" };
PROCESS_INFORMATION pInfo;
SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, 0, NULL, localAppData);
std::wstringstream ss;
ss << localAppData << L"/PacificPoker/bin/888poker.exe";
std::wstring ss_str = ss.str();
wchar_t* path = new wchar_t[ss_str.length() + 1]();
std::copy(ss_str.begin(), ss_str.end(), path);
CreateProcess(path, NULL,NULL, NULL,FALSE,NULL,NULL,NULL,&sInfo,&pInfo);
delete[] path;
return GetLastError();
Still getting '3' for GetLastError, but I can confirm that C:\Program Files\MyApp\MyExe.exe exists..
A few problems with the code:
sInfo is not initialised, you at least need to set the cb member
STARTUPINFO sInfo = { sizeof(STARTUPINFO), // 'cb'
NULL, // 'lpReserved'
L"winsta0\\default" };// 'lpDesktop'
See STARTUPINFO for more details.
the first argument to CreateProcess() should be non-const, but is being passed c_str() which would be a const if it was not a dangling pointer. The ss.str() method returns a std::string, and the c_str() is returning a pointer into that std::string but it is a temporary object and is destroyed at the end of the expression (the ;), making str a dangling pointer. Change to:
std::wstring ss_str = ss.str();
wchar_t* path = new wchar_t[ss_str.length() + 1]();
std::copy(ss_str.begin(), ss_str.end(), path);
CreateProcess(path, ...);
delete[] path;
Check return values of all your functions and query GetLastError() to determine failure reason.
You've probably tried this, but does localAppData get a valid string if you set the CSIDL to CSIDL_PROGRAM_FILES instead of CSIDL_PROGRAM_FILESX86?
If you're testing under XP, I don't know what CSIDL_PROGRAM_FILESX86 will return.