How can I read and write text files using the windows api - c++

There are many similar questions, but mine is a bit different. Mine involves a Windows GUI. I am using an open file dialog or an "OPENFILENAME". I want to get the dialog result as OK when the user clicks the OK button and then open a text encoded file. I have done it in Java, but the UI looks weird. So I am not sure whether or not people would like it. I want to learn C++ as well, so I need some help. I have a text box called "hWndEdit" in the WM_COMMAND message inside my program. After opening, the text in the file is supposed to be displayed in the textBox I have specified. I have defined the function in a header file with the following code:
#include <iostream>
#include <fstream>
#include <windows.h>
using namespace std;
void OpenFile()
{
OPENFILENAME ofn; // common dialog box structure
HWND hwnd = nullptr; // owner window
HANDLE hf; // file handle
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of szFile to initialize itself.
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = L"NoteRecorder Notes\0(*.recnote)\0Text\0(*.txt)\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetOpenFileName(&ofn) == TRUE)
{
hf = CreateFile(ofn.lpstrFile,
GENERIC_READ,
0,
(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE)NULL);
}
}
Then I call it from my WM_COMMAND message:
case WM_COMMAND:
switch (wParam)
{
case 12:
OpenFile();
break;
}
But when the user presses the OK button in the OPENFILENAME, it doesn't read any text from the file. How can I accomplish this?
And I want to write files with a save file dialog. Tell me how to do that as well.

The main purpose of the functions GetOpenFileName and GetSaveFileName is to provide you with the filename that the user selected. However, doing the actual File Input and Output is a different issue. You can use the standard C/C++ library for that or you can use the Windows API functions, such as CreateFile, ReadFile and WriteFile.
Since you seem to already be using the function CreateFile, it would make sense to call ReadFile after verifying that the function call to CreateFile succeeded (i.e. that it did not return INVALID_HANDLE_VALUE). After reading the data, you can add a terminating null character to the data (you should make sure that the memory buffer is large enough) and then pass it to the SetDlgItemText function (if the text box is in a dialog box) or SetWindowText function.

Related

GetOpenFileName with SDL2

I wrote a c++ app using SDL2 to simulate an editBox. It worked ok untill i added a function to open and select a file on Windows explorer.
Precisely after i click "Open" on the file browser, i can not use TTF_OpenFont() anymore...
I am still able to use TextSprites i have declared at the initialisation but i can't change the string associate to them no more. And that's really annoying because my editBox have to display a string var in my main loop.
I've already checked my font path with debug break points and it didn't change (still the same absolute path) nor the font size.
I've tryed many things to solve this : use another .ttf, use another TTF_Font *var, ect
Also tryed to put my openfiledialog function in a separate thread, that didn't change anything, so i tryed to control the new thread with windows Events and then with Sdl_Event but had no luck.
I obviously spent hours and hours of testing searching the web for similar issues and found nothing but unsolved posts.
Here is the funtion that allows me to get the name of the file opened :
void CMain::changeDirectoryPath()
{
OPENFILENAME ofn;
TCHAR szFile[MAX_PATH];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = szFile;
ofn.lpstrFile[0] = '\0';
ofn.hwndOwner = NULL;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = TEXT("Text Files\0*.txt\0Any File\0*.*\0");
ofn.nFilterIndex = 1;
ofn.lpstrTitle = TEXT("Select dictionary");
ofn.lpstrInitialDir = L"data\\dictionary";
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;
if(GetOpenFileName(&ofn))
{
OutputDebugString(ofn.lpstrFile);
int cSize = WideCharToMultiByte (CP_ACP, 0, ofn.lpstrFile, wcslen(ofn.lpstrFile), NULL, 0, NULL, NULL);
string output(static_cast<size_t>(cSize), '\0');
WideCharToMultiByte (CP_ACP, 0, ofn.lpstrFile, wcslen(ofn.lpstrFile), reinterpret_cast<char*>(&output[0]), cSize, NULL, NULL);
cout<<output<<endl;
}
cdpOn = false;
}
And the one that used to change the text displayed on my TextSprite :
bool CDictionary::loadFromRenderedText(std::string textureText)
{
if(Message!=NULL)
{
SDL_DestroyTexture(Message);
Message = NULL;
TTF_CloseFont(font);
}
font = TTF_OpenFont(filePath.c_str(), policeSize);
if(!font)
{
cout<<"TTF_OpenFont: "<<TTF_GetError()<<endl;
return 0;
}
textSurface = TTF_RenderText_Solid(font, textureText.c_str(), textColor);
if(textSurface != NULL)
{
Message = SDL_CreateTextureFromSurface(renderer, textSurface);
if(Message==NULL)
{
printf("Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError());
}
else
{
position.x=50;
position.y=50;
position.w=textSurface->w;
position.h=textSurface->h;
}
SDL_FreeSurface(textSurface);
}
else
{
printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
}
return Message != NULL;
}
At last i thought to add WxWidget in my project and use wxFileDialog to see if it solve the problem but i'm affraid mixing SDL2 and wxWidget will resort in a savage mess :-(
Does anybody knows why i can not reopen a tt_font after i select and open a file with GetOpenFileName()?
Or have suggestion to possibly solve this?
Thanks in advance
Comment under this functions MSDN page says "Current Working Directory is altered when a file is opened", which is exactly what you're describing. Revert it back with SetCurrentDirectory (query at launch with GetCurrentDirectory, once).
Another way would be not closing the font since you're using it quite often.

Showing a Windows Explorer Dialog from a C++ application

I have a windows application written in C++.
The application generates certain configuration files in a hidden directory.
I want to give user an option to open that directory from my application.
Clicking that option should open a windows explorer like dialog with an input directory location.
I spend time searching for a similar api, but end up with certain dialogs like "DlgDirListComboBoxW" or "GetOpenFileName" or "GetSaveFileName".
I am looking for an api to open normal Windows explorer like Dialog with an input directory location.
It would be really helpful if the api belongs to CommonDialogs section.
You can use the SHBrowseForFolder
It shows a dialog similar to this:
This is a example for how to use it:
BOOL GetFolder(LPCSTR folderpath,
LPCSTR szCaption,
HWND hOwner /*= NULL*/)
{
BOOL retVal = FALSE;
// The BROWSEINFO struct tells the shell
// how it should display the dialog.
BROWSEINFO bi;
memset(&bi, 0, sizeof(bi));
bi.ulFlags = BIF_USENEWUI;
bi.hwndOwner = hOwner;
bi.lpszTitle = szCaption;
// must call this if using BIF_USENEWUI
::OleInitialize(NULL);
// Show the dialog and get the itemIDList for the
// selected folder.
LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
if(pIDL != NULL)
{
// Create a buffer to store the path, then
// get the path.
char buffer[_MAX_PATH] = {'\0'};
if(::SHGetPathFromIDList(pIDL, buffer) != 0)
{
// Set the string value.
folderpath = buffer;
retVal = TRUE;
}
// free the item id list
CoTaskMemFree(pIDL);
}
::OleUninitialize();
return retVal;
}
How about:
HWND hWndOwner = NULL;
ShellExecute(
hWndOwner,
_T("explore"),
_T("c:\\some\\path"),
NULL,
NULL,
SW_SHOWNORMAL);
You can set hWndOwner to your main window handle if you're so inclined and can choose from a variety of other options.
For more information and usage details, check out the MSDN page on ShellExecute.

TabCtrl_GetItem macro not working as expected

I'm creating a basic notepad program, and when the user clicks close, I want it to ask the user if they want to save the current document opened. I'm using a tabbed interface, and trying to retrieve the filename ( text on tab ) so I have a MessageBox that says "Would you like to save: untitled.txt" or similar. I'm having trouble getting the file name. This is what I currently have:
case ID_FILE_CLOSE: // When the close button is clicked
{
HWND hEdit, hTabs;
hTabs = GetDlgItem( hwnd, IDC_MAIN_TAB );
int curTab = TabCtrl_GetCurSel( hTabs );
TCITEM curtitem;
TabCtrl_GetItem( hTabs, curTab, &curtitem );
// Check for file name
MessageBox( hwnd, curtitem.pszText, "Test", MB_OK );
}
break;
This is the error I keep getting in a popup box with Break, Continue, Ignore buttons:
Unhandled exception at 0x7597d298 in notepadpremium.exe: 0xC0000005: Access violation reading location 0xcccccccc.
I'm using MS Visual C++ Express 2010.
I also have a listbox with the filenames that also show the extension ( almost like notepad++ document switcher ) and tried LB_GETITEMDATA through a message, but that always returned blank. I think that was because I use LB_ADDSTRING to add it to the listbox. ( the listbox and tabs are interconnected, when you click on a file in the listbox, it changes to the corresponding tab ). Why isnt my code working the way it should?
Read the documentation:
pitem
Type: LPTCITEM
Pointer to a TCITEM structure that specifies the information to retrieve and receives information about the tab. When the message is sent, the mask member specifies which attributes to return. If the mask member specifies the TCIF_TEXT value, the pszText member must contain the address of the buffer that receives the item text, and the cchTextMax member must specify the size of the buffer.
You are not initializing the TCITEM at all. You need to tell TabCtrl_GetItem() what data to retrieve, and more importantly what buffer you provide to receive that data into. You are not doing any of that, you are passing random data to TabCtrl_GetItem(), which is why it crashes.
Try this instead:
case ID_FILE_CLOSE: // When the close button is clicked
{
HWND hTabs = GetDlgItem( hwnd, IDC_MAIN_TAB );
int curTab = TabCtrl_GetCurSel( hTabs );
TCHAR szFileName[MAX_PATH+1] = {0};
TCITEM curtitem = {0};
curitem.mask = TCIF_TEXT;
curitem.pszText = szFileName;
curitem.cchTextMax = MAX_PATH;
if (TabCtrl_GetItem( hTabs, curTab, &curtitem ))
{
// also from the documentation:
//
// "the control may change the pszText member of the structure
// to point to the new text instead of filling the buffer with
// the requested text. The control may set the pszText member
// to NULL to indicate that no text is associated with the item."
//
// which means you cannot rely on the szFileName[] buffer actually
// containing the filename, you have to use whatever buffer the
// TCITEM is actually pointing at, which may or may not be the
// szFileName buffer...
MessageBox( hwnd, curitem.pszText, TEXT("Test"), MB_OK );
}
}
break;
As for your ListBox issue, you said you are using LB_ADDSTRING to add strings to the ListBox, but are using LB_GETITEMDATA to retrieve them. That is wrong. You need to use LB_GETTEXTLEN and LB_GETTEXT instead. LB_GETITEMDATA is used to retrieve user-defined data that was added to the ListBox using LB_SETITEMDATA.

C++ program crashes after exit

I'm using the following function to open a dialog box, using the OPENFILENAME. Problem is, after opening the dialog box, and closing it, everything works fine, but when I exit my application then Windows says it crashed.
bool openDialog()
{
OPENFILENAME ofn; // common dialog box structure
char szFile[260]; // buffer for file name
HWND hwnd = NULL; // owner window
HANDLE hf; // file handle
// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFile = szFile;
//
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
// use the contents of szFile to initialize itself.
//
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrTitle = "Open File";
ofn.lpstrFilter = "Custom File\0*.Cus\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
// Display the Open dialog box.
if (GetOpenFileName(&ofn) == true)
{
path = ofn.lpstrFile;
return true;
}
else
{
return false;
}
}
I noticed it was the dialog box because the crash only happens if I use it at runtime, meaning that when I close the application without opening the dialog box at some point, it exits successfully.
Anyways, as far as I researched, it is probably caused my a 'heap corruption' or something similar that I don't have much knowledge of, so when my application tries to close, it doesn't release memory as it should (?). If anyone could figure out a solution it'd be greatly appreciated.
Most likely the problem is to do with path if this is simply a const char * or char * declared outside the function.
When the openDialog function returns, both the ofn structure of type OPENFILENAME and character array szFile go out of scope (along with all the other local, stack-allocated variables) and are no longer valid. As a result, the character array that ofn.lpstrFile and consequently path point to is no longer valid.
You should allocate space for the file name outside the function and perform a copy with strcpy, strncpy or equivalent before returning from it. Another approach would be to move the szFile character array out of the function and into the containing class as a member variable. Either way, the scope of the character array containing the file name returned needs to extend beyond the lifetime of the execution of the openDialog function.

MFC Save file dialog

I am writing an MFC C++ application that has a Save As button for saving a .txt file to the disc. With it I am trying to add an extra verification for file overwriting (if a file with the same filename exists, then it should query the user if he wants to overwrite the old file or not). I have tried this with the below code, but it doesn't really work. When I click No on the MessageBox, it should reopen the Save As file dialog, but instead it gives me two errors: the first one is Debug assertion failed, and the second one is Encountered an improper argument. How should I do this better? This is the code:
char strFilter[] = { "Text Files (*.txt)|*.txt|" };
CFileDialog FileDlg(FALSE, CString(".txt"), NULL, 0, CString(strFilter));
while(true)
{
if( FileDlg.DoModal() == IDOK ) // this is the line which gives the errors
{
agendaName = FileDlg.GetFileName(); //filename
agendaPath = FileDlg.GetFolderPath(); //filepath (folders)
if(model->agendaExists(CSToString(agendaPath+TEXT("\\")+agendaName))) // there is another file called the same way
{
if(MessageBox(TEXT("A file with the specified name already exists. Overwrite?"), TEXT("File exists"), MB_YESNO) != 6) // user clicked NO (do not overwrite file)
{
continue;
}
}
model->sendToFile(CSToString(agendaPath+TEXT("\\")+agendaName)); // the file is unique so the agenda named agendaName found at path agendaPath is saved
return;
}
}
It should be mentioned that the errors occur on line 7 and only on the second loop through the while.
CFileDialog can detect itself if a file exists and prompt the user for overwriting.
explicit CFileDialog(
BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0
);
Just pass OFN_OVERWRITEPROMPT for the flags.
As for your problem, run in Debugger and when you get that assertion press the Retry button to see where the problem comes from (you'll probably have to look through the call stack also). Maybe you should try putting this in the while loop:
CFileDialog FileDlg(FALSE, CString(".txt"), NULL, 0, CString(strFilter));
You should use the OFN_OVERWRITEPROMPT flag in the constructor. That flag is usually one of the default flags, but you have set your flags to 0. So, if you do:
CFileDialog FileDlg(FALSE, CString(".txt"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, CString(strFilter));
if (FileDlg.DoModal() == IDOK)
{
model->sendToFile(CSToString(FileDlg.GetPathName()));
}
It should work. By the way, GetPathName() gets the full path to the selected file, so you don't need to get the folder and the file name in 2 steps.
Try including below line inside the while loop (as first line in while loop)
CFileDialog FileDlg(FALSE, CString(".txt"), NULL, 0, CString(strFilter));
This line is outside the while loop in your code