I seem to be having a very strange problem with GetOpenFileName.
It errors for no apparent reason, however, if I call CommDlgExtendedError() in the error check, the error never happens in the first place.
Here is my code:
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
OPENFILENAME fm;
char flnm[MAX_PATH];
ZeroMemory(&fm, sizeof(fm));
fm.lStructSize = sizeof(OPENFILENAME);
fm.hwndOwner = NULL;
fm.lpstrFilter = "Text Files (*.txt)\0*.txt\0";
fm.lpstrFile = flnm;
fm.nMaxFile = MAX_PATH;
fm.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
fm.lpstrDefExt = "";
if(!GetOpenFileNameA(&fm))
{
MessageBoxA(NULL, "failed! :(", NULL, NULL);
}
return 0;
}
What's shown? "failed! :("
If I remove this check, I do see a file dialog. However, it doesn't work, and the filename box is pre-filled with random junk.
If I change to:
if(!GetOpenFileNameA(&fm))
{
DWORD dwErr = CommDlgExtendedError();
MessageBoxA(NULL, "failed! :(", NULL, NULL);
}
"failed! :(" is NOT shown. The file dialog shows and performs without issue.
What is going on!?!?
OPENFILENAME fm;
char flnm[MAX_PATH]; // nobody initialized me ...
ZeroMemory(&fm, sizeof(fm));
fm.lStructSize = sizeof(OPENFILENAME);
fm.hwndOwner = NULL;
fm.lpstrFilter = "Text Files (*.txt)\0*.txt\0";
fm.lpstrFile = flnm; // ... who knows what I am?
fm.nMaxFile = MAX_PATH;
fm.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
fm.lpstrDefExt = "";
if(!GetOpenFileNameA(&fm))
{
MessageBoxA(NULL, "failed! :(", NULL, NULL);
}
The documentation for lpstrFile states:
The file name used to initialize the File Name edit control. The first character of this buffer must be NULL if initialization is not necessary. When the GetOpenFileName or GetSaveFileName function returns successfully, this buffer contains the drive designator, path, file name, and extension of the selected file.
You are not initializing flnm and therein lies the problem. You can solve the problem by writing flnm[0] = '\0' before you call GetOpenFileName.
Related
I am writing a simple text editor using the Win32 API and I am trying to write a function to compare the content of the file to the content of an edit control. I currently have this:
BOOL checkForModification (PCWSTR pszFileName, HWND hEdit) {
BOOL bSuccess = FALSE;
DWORD dwTextLength = GetWindowTextLengthA(hEdit);
hFile = CreateFile(pszFileName, GENERIC_READ,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
DWORD dwFileSize;
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize != 0xFFFFFFFF)
{
PSTR pszFileText;
pszFileText = (PSTR)GlobalAlloc(GPTR, dwFileSize + 1);
if (pszFileText != NULL) {
DWORD dwRead;
if (ReadFile(hFile, pszFileText, dwFileSize + 1, &dwRead, NULL))
{
bSuccess = TRUE;
pszFileText[dwFileSize] = 0;
LPSTR pszEditText = (LPSTR)GlobalAlloc(GPTR, dwTextLength + 1);
GetWindowTextA(hEdit, pszEditText, dwTextLength);
int res = CompareStringA(LOCALE_SYSTEM_DEFAULT, NULL, pszFileText, -1, pszEditText, -1);
if (res != CSTR_EQUAL) {
MessageBox(NULL, L"You changed the text!", L"Alert", MB_OK | MB_ICONINFORMATION);
}
GlobalFree(pszEditText);
}
else {
MessageBox(NULL, L"Oh no! Something went wrong!\nError code: 2", L"Error", MB_OK | MB_ICONERROR);
}
GlobalFree(pszFileText);
}
}
CloseHandle(hFile);
}
else {
MessageBox(NULL, L"Oh no! Something went wrong!\nError code: 1", L"Error", MB_OK | MB_ICONERROR);
}
return bSuccess;
}
The problem that I am having is that the result of CompareStringA is always returning CSTR_LESS_THAN, even when I don't change the text in the edit control. The encoding of the file is UTF-8. Why is this happening?
Seriously, use a debugger and test it with a file that contains simple text like ABCDE. You should be able to figure out the problem in less than 30 seconds just by inspecting a few variables!
You can trivially determine that the problem is not reading the documentation of function GetWindowTextA (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtexta).
The size you pass include the null terminating charaters. Assuming that the edit also contains ABCDE, then the length is 5.
Calling GetWindowTextA(hEdit, pszEditText, dwTextLength); where dwTextLength will returns a buffer that contains ABCD plus the null character.
Obviously ABCD is before ABCDE using usual sorting rules.
Actually, for my purposes, I have found it to be easiest to just use the Edit_GetModify macro to see if any edits have been made to the text in the edit control. While this doesn't do exactly what I want (it will return TRUE even if you undo your changes), it is more efficient than having to read the entire file then compare its entire contents to that of the edit control.
I've tried to load a bmp image using LoadImage() in order to display it, but I just can't find a way to load the image. Here is some code:
EDIT
as suggested, here is a runnable example
#include <windows.h>
#include <iostream>
#include <fstream>
std::string GetLastErrorAsString()
{
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int iShow) {
std::ofstream file("output.txt");
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, L"C:\\yes.bmp", IMAGE_BITMAP, 300, 300, LR_LOADFROMFILE);
if (hBitmap == NULL) {
file << "error: " << GetLastErrorAsString().c_str() << std::endl;
return 1;
}
else {
file << "success." << std::endl;
}
return 0;
}
And this result is always NULL. If I put a wrong file name, the error message will be The system cannot find the file specified. but if the name is right, there is simply no error message.
I've tried to save the bmp file in 24 and 32 bits, I played with the LR_ flags, it doesn't change anything.
I also tried setting the HINSTANCE to the main's one, without any success.
I run this code once at the beginning of the main.
Thanks for your help, I hope I just misunderstood how this works
I created a function that reads from a file, and fills a data structure based on that file. The function works well alone, so I decided to build an GUI around its function. I have a function that calls the Windows GUI and returns a LPOPENFILENAME structure, to which I copy the LPOPENFILENAME.lpstrFile over to a LPCWSTR.
Next, I initialize a wstring to hold the LPCWSTR value. Here's where things get a bit strange.
LPCWSTR File = FileOpenOBJ(hWnd, hInst).lpstrFile;
MessageBox(hWnd, File, L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
wstring Fileformatting(File);
Which does a runtime display of something like this:
Which is fine, since LPCWSTR File now holds the value from the LPOPENFILENAME structure, but when a wstring is initialized with the value of File, both variables seemingly go out of range or pull from a pointer location that is no longer accurate and their values appear to be something like:
The goal is to hold the file name in a string so that I can (PHP-like) escape the "\" characters to insert into my function. As such:
C:\Users\InfinityMachine\Documents\Test.obj -> C:\\Users\\InfinityMachine\\Documents\\Test.obj
In which I:
while (Fileformatting.find(L"\\") != wstring::npos){
Fileformatting.replace(Fileformatting.find(L"\\"), 2, L"\\\\");
}
How can I get the correct value of LPCWSTR File into wstring Fileformatting?
You have probably initialized lpstrFile to a variable that is local to the FileOpenOBJ function, and when that function returns you get undefined behavior when you try to use it. It appears to work the first time because the stack hasn't been exercised yet, but that changes when you make the call to MessageBox.
Moving the initialization of the wstring would mask the problem but wouldn't fix it, you're still relying on undefined behavior. The fix would be to have the structure contain a pointer that doesn't go out of scope.
In addition to what others have said about dangling pointers and such, for this kind of situation, you are better off simply having FileOpenOBJ() return a wstring instead of a copy of the original OPENFILENAME struct:
wstring FileOpenOBJ(HWND hWnd, HINSTANCE hInst)
{
WCHAR szFileName[MAX_PATH+1] = {0};
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
...
GetOpenFileName(&ofn);
return szFileName;
}
wstring File = FileOpenOBJ(hWnd, hInst);
MessageBoxW(hWnd, File.c_str(), L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
If you need to return other values from the OPENFILENAME, you should define your own struct to hold what you need, for example:
struct FileInfo
{
wstring FullPath;
wstring FileName;
wstring Ext;
int FilterIndex;
...
};
FileInfo FileOpenOBJ(HWND hWnd, HINSTANCE hInst)
{
WCHAR szFileName[MAX_PATH+1] = {0};
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
...
GetOpenFileName(&ofn);
FileInfo fi;
fi.FullPath = szFileName;
fi.FileName = &szFileName[ofn.nFileOffset];
if (ofn.nFileExtension != 0)
fi.Ext = &szFileName[ofn.nFileExtension];
fi.FilterIndex = ofn.nFilterIndex;
...
return fi;
}
FileInfo fi = FileOpenOBJ(hWnd, hInst);
MessageBoxW(hWnd, fi.FullPath.c_str(), L"Warning 1: File/lpstrFile assignment ", MB_ICONEXCLAMATION | MB_OK);
Don't expose direct access to the OPENFILENAME struct. It should be left as a private implementation detail of FileOpenOBJ()'s internal logic.
I'm trying to copy a file from resources to %localappdata%. I've something like this:
HINSTANCE hInstance = GetModuleHandle(NULL);
HANDLE hFile = INVALID_HANDLE_VALUE;
HRSRC hrsrc = FindResource(hInstance, MAKEINTRESOURCE(MSIE), RT_RCDATA);
HGLOBAL exeRes = LoadResource(hInstance, hrsrc);
DWORD size = SizeofResource(hInstance, hrsrc);
TCHAR szPath[MAX_PATH];
HANDLE hfile;
if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, szPath))) {
PathAppend(szPath, TEXT("test.exe"));
hFile = CreateFile(szPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
LPVOID exePtr = LockResource(hrsrc);
DWORD exeWritten = 0;
BOOL writeResult = WriteFile(hFile, exePtr, size, &exeWritten, NULL);
cout << GetLastError() << endl;
BOOL closed = CloseHandle(hFile);
system("PAUSE");
return 0;
I'm able to locate the HRSRC and confirm the size using SizeofResource() just fine. CreateFile is in fact creating the file and returning the handle. GetLastError() reports that there are no errors. The amount of bytes written to disk is exactly right.
The output exe however is corrupted (the version of this file is incompatible with the version of Windows... blah blah) - it's lost its icon and everything. Looking at the original and the output file side-by-side in a hex editor it appears there's random data at the start of file.
What am I missing here?
Your LockResource is not correct. It should be passed the HGLOBAL exeRes you loaded previously.
LockResource(hrsrc);
should be
LockResource(exeRes);
From the MS documentation on LockResource() :
Do not try to lock a resource by using the handle returned by the FindResource or FindResourceEx function. Such a handle points to random data.
And as a side note, you may want to try cleaning up that loaded-and-locked resource when you're through with it.
I need to convert:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646839(v=vs.85).aspx
lpstrFileTitle
Type: LPTSTR
The file name and extension (without path information) of the selected file. This member can be NULL.
Even though in MSVC++ 2012 Express it says its LPSTR.
To
http://msdn.microsoft.com/en-us/library/windows/desktop/bb172802(v=vs.85).aspx
pSrcFile [in]
Type: LPCTSTR
Pointer to a string that specifies the filename. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.
I would very much appreciate this. :)
char szFileName[MAX_PATH] = {0};
char szFileTitleName[MAX_PATH] = {0};
HRESULT hr = S_OK;
RtlZeroMemory(&gc.ofn, sizeof(gc.ofn));
gc.ofn.lStructSize = sizeof(gc.ofn);
gc.ofn.hwndOwner = hWnd;
gc.ofn.lpstrFilter = "All Image Files\0" "*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico\0"
"Windows Bitmap\0" "*.bmp;*.dib\0"
"High Definition Photo\0" "*.wdp;*.mdp;*.hdp\0"
"Graphics Interchange Format\0" "*.gif\0"
"Portable Network Graphics\0" "*.png\0"
"JPEG File Interchange Format\0" "*.jpg;*.jpeg\0"
"Tiff File\0" "*.tif\0"
"Icon\0" "*.ico\0"
"All Files\0" "*.*\0"
"\0";
gc.ofn.nMaxFileTitle = MAX_PATH;
gc.ofn.lpstrFileTitle = gc.szFileTitleName; // its a char
gc.ofn.lpstrFile = szFileName;
gc.ofn.nMaxFile = MAX_PATH;
gc.ofn.lpstrTitle = "Open Image";
gc.ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
if(GetOpenFileName(&gc.ofn)) {
gc.render_on = true;
}
.
D3DXCreateTextureFromFileEx (d3dDevice, gc.szFileTitleName , D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0,
D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,
colorkey, &init_map->image_info, NULL, &init_map->texture_buffer)
This will have a blank image. I have tested this with a messagebox right after GetOpenFile, and it returns great.
MessageBoxA ( NULL , gc.ofn.lpstrFileTitle , "" , 0 );
But before the D3DXCreateTextureFromFileEx, its messed up.
Just storing the the char into gc.szFileTitleName. No idea why the other way was going out of scope ...
LPCTSTR is just a const version of LPTSTR. That type of conversion is a standard conversion, so you shouldn't need to do anything explicitly to get what you want. Your error must be somewhere else.
Unless you are somehow compiling a piece of your code with UNICODE set and some without it...
GetOpenFileName(), D3DXCreateTextureFromFileEx(), and MessageBox() all deal with TCHAR-based strings, but you are not actually utilizing TCHAR in your code. Try this instead:
TCHAR szFileName[MAX_PATH] = {0};
TCHAR szFileTitleName[MAX_PATH] = {0};
HRESULT hr = S_OK;
RtlZeroMemory(&gc.ofn, sizeof(gc.ofn));
gc.ofn.lStructSize = sizeof(gc.ofn);
gc.ofn.hwndOwner = hWnd;
gc.ofn.lpstrFilter = TEXT("All Image Files\0") TEXT("*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico\0")
TEXT("Windows Bitmap\0") TEXT("*.bmp;*.dib\0")
TEXT("High Definition Photo\0") TEXT("*.wdp;*.mdp;*.hdp\0")
TEXT("Graphics Interchange Format\0") TEXT("*.gif\0")
TEXT("Portable Network Graphics\0") TEXT("*.png\0")
TEXT("JPEG File Interchange Format\0") TEXT("*.jpg;*.jpeg\0")
TEXT("Tiff File\0") TEXT("*.tif\0")
TEXT("Icon\0") TEXT("*.ico\0")
TEXT("All Files\0") TEXT("*.*\0")
TEXT("\0");
gc.ofn.nMaxFileTitle = MAX_PATH;
gc.ofn.lpstrFileTitle = gc.szFileTitleName;
gc.ofn.lpstrFile = szFileName;
gc.ofn.nMaxFile = MAX_PATH;
gc.ofn.lpstrTitle = TEXT("Open Image");
gc.ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
if(GetOpenFileName(&gc.ofn)) {
gc.render_on = true;
}
.
D3DXCreateTextureFromFileEx (d3dDevice, gc.szFileTitleName , D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, colorkey, &init_map->image_info, NULL, &init_map->texture_buffer)
.
MessageBox(NULL, gc.ofn.lpstrFileTitle, TEXT(""), 0);