Writing to binary file crashes application

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
ValeV

Writing to binary file crashes application

Post by ValeV »

Heyall,

I am writing a simple application (CodeBlocks, minGW, wxWidgets 3.1.2), which has 2 buttons. First one loads binary file, second one saves data from binary file in human readable format. I'm using FileDialog for choosing binary and text file.

Button1 function for browsing is this:

Code: Select all

void Binary_ReaderFrame::OnButton1Click(wxCommandEvent& event)
{
    string f;
    if (FileDialog1->ShowModal() == wxID_OK)
    {
        f = string((FileDialog1->GetPath()).mb_str());

        auto found = f.find_last_of(".");
        if (found != string::npos && f.substr(found).compare(".bin"))
        {
            wxMessageBox(_("Wrong file chosen.\n"));
            return;
        }
    }
    else
    {
        wxMessageBox(_("Error loading file.\n"));
        return;
    }
    StatusBar1->SetStatusText("BIN file: " + FileDialog1->GetPath());
}
And button function for saving is this.

Code: Select all

void Binary_ReaderFrame::OnButton2Click(wxCommandEvent& event)
{
    string f = string((FileDialog1->GetPath()).mb_str());
    string f2 = "";

    if (f.empty())
    {
        wxMessageBox(_("Please first choose binary file."));
        return;
    }

    if (FileDialog2->ShowModal() == wxID_OK)
    {
        f2 = string((FileDialog2->GetPath()).mb_str());

        auto found = f2.find_last_of(".");
        if (found != string::npos && f2.substr(found).compare(".txt"))
        {
            wxMessageBox(_("Wrong file chosen.\n"));
            return;
        }
    }
    else
    {
        StatusBar1->SetStatusText("Error loading file.");
        return;
    }

    wxMessageBox("1");

    ifstream in(f, ios::binary | ios::in);

    wxMessageBox("1.5");

    ofstream out(f2);

    while(in)
    {
        int8_t x;
        in.read((char*)&x, 1);
        x -= 99;
        out << x;
    }

    in.close();
    out.close();

    wxMessageBox(_("Finished.\n"));
}

Now to the problem. The application runs and works fine when runninng through IDE (CodeBlocks), but when I run it as standalone application (\bin\Release\[name].exe), the application crashes at:

Code: Select all

ifstream in(f, ios::binary | ios::in);
(I know this because message box "1,5" appears, then application crashes.

If it helps, I built wxWidgets as:

Code: Select all

C:\MinGW\bin\mingw32-make -f makefile.gcc -j4 BUILD=debug MONOLITHIC=0 RUNTIME_LIBS=static SHARED=0
Any tips on how to fix this?
Kvaz1r
Super wx Problem Solver
Super wx Problem Solver
Posts: 357
Joined: Tue Jun 07, 2016 1:07 pm

Re: Writing to binary file crashes application

Post by Kvaz1r »

It would be much better to have full code for reproducing the error. Can you provide MCVE?
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Writing to binary file crashes application

Post by doublemax »

The application runs and works fine when runninng through IDE (CodeBlocks), but when I run it as standalone application (\bin\Release\[name].exe), the application crashes at:
The most likely reasons are probably:
1) the file is not found. Check the filepath
2) you don't have write access

Also, you say that it crashes on writing, but the code you've shown is reading.

Code: Select all

f = string((FileDialog1->GetPath()).mb_str());
This will fail if the path contains non-ascii characters. Keep using wxString to process strings that are returned from wxWidgets.

Also, you seem to have permanent instances of FileDialog1 that you're reusing. This is bad practice. Create them on the stack each time you need them.
Use the source, Luke!
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Writing to binary file crashes application

Post by PB »

In addition to what doublemax said.

You can avoid the conversion to std::string when checking for the extension, using wxString's methods. Additionally, your check may be unintentionally case-sensitive, which is usually not desirable in such scenarios.

C++ streams may not be the best suited for such operation, TBH I never used them and used handier I/O functions of the framework (MFC, wxWidgets...).

Modal dialogs are usually not stored and used later, they are created on stack and have limited lifetime.

FWIW, here is (not really tested) simple example of converting binary file to its hexadecimal interpretation. The code for reading and writing file is a bit more complicated as it process the input file by chunks instead of one byte at a time. The code is not for production use, it is just a demonstration, that's why there are e.g. wxLogMessage()s and wxYield()s...

Code: Select all

#include <wx/wx.h>
#include <wx/filedlg.h> 
#include <wx/ffile.h>

bool ConvertFileBinaryToFileHex(const wxString& inputFileName, const wxString& outputFileName)
{
    const size_t charsPerLine = 80;
    const size_t bufSize = 16 * 1024;
    char buffer[bufSize];
    wxFFile inFile, outFile;

    wxLogMessage("Converting binary file '%s' to hexadecimal text file '%s'...",
        inputFileName, outputFileName);

    if ( !inFile.Open(inputFileName, "rb") || !outFile.Open(outputFileName, "w") )
        return false;

    wxFileOffset bytesLeft = inFile.Length();
    wxFileOffset bytesHexed = 0;
    size_t chunkSize = sizeof(buffer);
    wxString hexLine;

    while ( bytesLeft > 0 && !inFile.Error() && !outFile.Error() ) 
    {
        if ( chunkSize > bytesLeft )
            chunkSize = bytesLeft;

        if ( inFile.Read(&buffer, chunkSize) != chunkSize )
            break;

        for ( size_t i = 0; i < chunkSize; ++i )
        {
            hexLine += wxDecToHex(buffer[i]) + " ";
            if ( hexLine.size() >= charsPerLine
                || ( bytesLeft == chunkSize && i == chunkSize - 1 ) )
            {
                hexLine.RemoveLast(1); // remove the last space
                hexLine += "\n";
                if ( !outFile.Write(hexLine) )
                    break;
                hexLine = "";
            }
            bytesHexed++;
        }
        wxLogMessage("Converted chunk of %zu bytes", chunkSize);
        wxYield();

        bytesLeft -= chunkSize;
    }

    if ( bytesLeft > 0 || inFile.Error() || outFile.Error() || bytesHexed != inFile.Length() )
    {
        wxLogError("Error converting binary file to text file.");
        return false; 
    }

    wxLogMessage("Conversion finished successfully.");

    return true;
}

class MyDialog : public wxDialog
{
public:
    MyDialog() : wxDialog(NULL, wxID_ANY, "Test", wxDefaultPosition, wxSize(600, 600))
    {
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);

        mainSizer->Add(new wxButton(this, wxID_ANY, "Convert binary file to hexadecimal..."), wxSizerFlags().Expand().Border());
        Bind(wxEVT_BUTTON, &MyDialog::OnConvert, this);

        wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
            wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
        mainSizer->Add(logCtrl, wxSizerFlags().Expand().DoubleBorder().Proportion(3));

        SetSizer(mainSizer);
    }

private:
    void OnConvert(wxCommandEvent&)
    {
        wxString inputFileName = wxFileSelector("Select binary file to read", "", "", "",
            "Binary files (*.bin)|*.bin", wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);

        if ( inputFileName.empty() )
            return; // user cancelled the dialog

        if ( !inputFileName.Upper().EndsWith(".BIN") )
        {
            wxLogError("Invalid input file extension.");
            return;
        }

        wxString outputFileName = wxFileSelector("Select file name for hexadecimal output", "", "", "",
            "Text files (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this);

        if ( outputFileName.empty() )
            return; // user cancelled the dialog

        wxWindowDisabler disabler;
        wxBusyCursor busyCursor;

        ConvertFileBinaryToFileHex(inputFileName, outputFileName);
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
ValeV

Re: Writing to binary file crashes application

Post by ValeV »

Thank you both for the help. I removed C++ streams and used the example from @PB. EXE file is working now, with no crashes.

I have 3 quick questions though:
1)
you seem to have permanent instances of FileDialog1 that you're reusing
I dont understand this. I have 2 wxFileDialog components which I use ShowModal() and GetPath(). I didn't explicitly define any global variables for them. Can you please explain in simpler terms what you meant?

2) New code is below, can you check it quickly if I didn't do any stupid stuff (like using wxFFile desctructor)?

Button1:

Code: Select all

void BinaryReaderFrame::OnButton1Click(wxCommandEvent& event)
{
    if (FileDialog1->ShowModal() == wxID_OK)
    {
        if (!(FileDialog1->GetPath().Upper().EndsWith(".BIN")))
        {
            wxMessageBox("Invalid file extension.");
            return;
        }
    }
    else
    {
        return;
    }
    StatusBar1->SetStatusText("BIN file: " + FileDialog1->GetPath());
}

Button2:

Code: Select all

void BinaryReaderFrame::OnButton2Click(wxCommandEvent& event)
{
    wxFFile inFile, outFile;

    if (FileDialog1->GetPath().empty())
    {
        wxMessageBox(_("Please first choose binary file."));
        return;
    }

    if (FileDialog2->ShowModal() == wxID_OK)
    {
        if (!(FileDialog2->GetPath().Upper().EndsWith(".TXT")))
        {
            wxMessageBox("Invalid file extension.");
            return;
        }
    }
    else
    {
        return;
    }


    if (!inFile.Open(FileDialog1->GetPath(), "rb") || !outFile.Open(FileDialog2->GetPath(), "w"))
    {
        wxMessageBox("Error: Can't read binary file or can't write to text file.");
    }

    char buffer;
    while (!inFile.Error() && inFile.Read(&buffer, 1))
    {
        buffer -= 99;
        outFile.Write(buffer);
    }

    inFile.~wxFFile();
    outFile.~wxFFile();

    wxMessageBox(_("Finished.\n"));
}
3) I am worrying that binary file will be created on different system than it will be translated to text (with this application). If this happens, should I use fixed width integer type (uint8_t)? If so, how?
User avatar
T-Rex
Moderator
Moderator
Posts: 1248
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Re: Writing to binary file crashes application

Post by T-Rex »

You probably should call Flush() and Close() for output file and Close() for input file instead of calling the descructor manually.
I dont understand this. I have 2 wxFileDialog components which I use ShowModal() and GetPath(). I didn't explicitly define any global variables for them.
Your file dialogs get created outside of OnButton1Click method. It would be better to create them inside the method and release the memory when the method finishes. Actually, you can create the dialogs on stack, in stead of creating on heap.
ValeV

Re: Writing to binary file crashes application

Post by ValeV »

Thank you, now I understand. I use wxSmith, which I suppose makes it frame scoped.

I will use Flush() and Close(), thank you for advice.
Post Reply