Exception thrown when closing window Topic is solved

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
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Exception thrown when closing window

Post by Ksawery »

I am getting a read access violation error when attempting to close my main window, right after pressing a button (see screenshot attached). Is this expected behaviour, and can it be overcome? Or should I simply place a control statement in my OnClose callback, to stop the window from being closed if a button has just been pressed?
Attachments
Annotation 2019-08-07 111642.jpg
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Exception thrown when closing window

Post by doublemax »

No, that shouldn't happen, but without more information it's hard to tell what the problem is. Does it only happen for one or a few buttons, or for all. What exactly does your app do after pressing that button?

Post a full callstack.
Use the source, Luke!
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: Exception thrown when closing window

Post by Ksawery »

Here is the call stack:

Code: Select all

>	PomiarWiazki_wxWidgets.exe!wxEvtHandler::SafelyProcessEvent(wxEvent & event) Line 1617	C++
 	PomiarWiazki_wxWidgets.exe!wxTimerImpl::SendEvent() Line 52	C++
 	PomiarWiazki_wxWidgets.exe!wxTimer::Notify() Line 107	C++
 	PomiarWiazki_wxWidgets.exe!wxTimerImpl::Notify() Line 47	C++
 	PomiarWiazki_wxWidgets.exe!wxProcessTimer(wxMSWTimerImpl & timer) Line 160	C++
 	PomiarWiazki_wxWidgets.exe!wxTimerWndProc(HWND__ * hWnd, unsigned int message, unsigned int wParam, long lParam) Line 173	C++
 	[External Code]	
 	user32.dll![Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]	Unknown
 	PomiarWiazki_wxWidgets.exe!wxGUIEventLoop::PreProcessMessage(tagMSG * msg) Line 96	C++
 	PomiarWiazki_wxWidgets.exe!wxGUIEventLoop::ProcessMessage(tagMSG * msg) Line 163	C++
 	PomiarWiazki_wxWidgets.exe!wxGUIEventLoop::Dispatch() Line 227	C++
 	PomiarWiazki_wxWidgets.exe!wxEventLoopManual::ProcessEvents() Line 237	C++
 	PomiarWiazki_wxWidgets.exe!wxEventLoopManual::DoRun() Line 283	C++
 	PomiarWiazki_wxWidgets.exe!wxEventLoopBase::Run() Line 90	C++
 	PomiarWiazki_wxWidgets.exe!wxAppConsoleBase::MainLoop() Line 380	C++
 	PomiarWiazki_wxWidgets.exe!wxAppConsoleBase::OnRun() Line 301	C++
 	PomiarWiazki_wxWidgets.exe!wxAppBase::OnRun() Line 336	C++
 	PomiarWiazki_wxWidgets.exe!wxEntryReal(int & argc, wchar_t * * argv) Line 507	C++
 	PomiarWiazki_wxWidgets.exe!wxEntry(int & argc, wchar_t * * argv) Line 184	C++
 	PomiarWiazki_wxWidgets.exe!wxEntry(HINSTANCE__ * hInstance, HINSTANCE__ * __formal, char * __formal, int nCmdShow) Line 305	C++
 	PomiarWiazki_wxWidgets.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 3	C++
 	[External Code]	
After the button is pressed, it immediately becomes disabled. A flag is set that signals a helper thread that the button has been pressed. On its next iteration (the thread loops every 100ms or so), the helper thread writes values to a slave device via a Modbus protocol, and resets the flag. A one-shot timer is then started (in the thread event handler, never the thread itself) and after about 1.5 seconds the button is re-enabled in the timer callback.

There are two buttons in my main window which do pretty much the same thing (only send a different Modbus message) - see screenshot attached. Here is also my entire .cpp file for reference:

Code: Select all

#include "cMain.h"

wxBEGIN_EVENT_TABLE(cMain, wxFrame)
	EVT_BUTTON(10001, OnButtonClickedWsun)
	EVT_BUTTON(10002, OnButtonClickedWysun)
	EVT_MENU(wxID_NETWORK, OnMenuClickedConnect)
	EVT_MENU(wxID_VIEW_DETAILS, OnMenuClickedDiagnostics)
	EVT_MENU(wxID_INFO, OnMenuClickedInformation)
	EVT_MENU(wxID_EXIT, OnMenuClickedExit)
	EVT_TIMER(10004, OnWriteTimer)
	EVT_TIMER(10005, OnWriteErrorTimer)
	EVT_CLOSE(OnClose)
wxEND_EVENT_TABLE()

/* Frame constructor */
cMain::cMain(const char* mbPort, int mbSlaveAddress, int mbSlaveNum) : wxFrame(nullptr, wxID_ANY, " ", wxPoint(200, 160), wxSize(870, 630), (wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER & ~wxMAXIMIZE_BOX))
{	
	//Set title
	this->SetTitle(wxT("Pomiar Wiązki w Linii Iniekcyjnej: MB" + std::to_string(mbSlaveNum) + " (COM" + mbPort[mbPortCharLength - 1] + ")"));

	//Shift window
	this->SetPosition(this->GetPosition() + ((mbSlaveNum - 1) * wxPoint(120, 120)));

	//Bind helper thread dynamically
	Bind(wxEVT_THREAD, &cMain::OnThreadUpdate, this);

	//Store modbus connection settings
	this->mbPort = mbPort;
	this->mbSlaveNum = mbSlaveNum;
	this->mbSlaveAddress = mbSlaveAddress;

	//Pointer initialization
	m_pMenuBar = nullptr;
	m_pToolsMenu = nullptr;
	m_pHelpMenu = nullptr;
	m_btn_wsun = nullptr;
	m_btn_wysun = nullptr;
	pngHandler = nullptr;
	backgroundImage = nullptr;
	m_static_txt_adc1 = nullptr;
	m_static_txt_adc2 = nullptr;
	m_static_txt_adc3 = nullptr;
	m_static_txt_adc4 = nullptr;
	m_static_txt_mberror_read = nullptr;
	m_static_txt_mberror_write = nullptr;
	m_static_txt_adcerror = nullptr;
	ctx = nullptr;
	m_frame_diag = nullptr;

	//Variables initialization
	mbADCErrorsRegister = 0;

	//Modbus status flags initialization
	mbStatusConnected = 0;
	mbStatusReadADC = 0;
	mbStatusWriteCoil = 0;
	mbStatusReadDiagnostics = 0;
	mbStatusResetDiagnostics = 0;

	//Other flags initialization
	mbModeWriteCoil = -1;
	mbDiagnosticsEnabled = FALSE;
	mbDiagnosticsResetEnabled = FALSE;
	mbDiagnosticsResetting = FALSE;
	mbDiagnosticsOpenWindowEnabled = FALSE;
	mbDiagnosticsWindowIsOpen = FALSE;

	//Construct custom Modbus diagnostics message
	mbDiagRawRequest[0] = (uint8_t)mbSlaveAddress;
	mbDiagRawRequest[1] = 0x08;
	mbDiagRawRequest[2] = 0x00;
	mbDiagRawRequest[3] = 0x02;
	mbDiagRawRequest[4] = 0x00;
	mbDiagRawRequest[5] = 0x00;

	//Construct custom Modbus diagnostics reset message
	mbDiagResetRawRequest[0] = (uint8_t)mbSlaveAddress;
	mbDiagResetRawRequest[1] = 0x08;
	mbDiagResetRawRequest[2] = 0x00;
	mbDiagResetRawRequest[3] = 0x01;
	mbDiagResetRawRequest[4] = 0xFF;
	mbDiagResetRawRequest[5] = 0x00;

	//Set window background colour
	this->SetBackgroundColour(wxColour(225, 225, 225));

	//Create buttons
	m_btn_wsun = new wxButton(this, 10001, "Wsuń", wxPoint(670, 230), wxSize(150, 50));
	m_btn_wysun = new wxButton(this, 10002, "Wysuń", wxPoint(670, 290), wxSize(150, 50));

	if (m_btn_wsun != nullptr) m_btn_wsun->SetBackgroundColour(wxColour(210, 210, 210));
	if (m_btn_wysun != nullptr) m_btn_wysun->SetBackgroundColour(wxColour(210, 210, 210));

	//Add background image
	pngHandler = new wxPNGHandler;
	wxImage::AddHandler(pngHandler);
	backgroundImage = new wxStaticBitmap(this, wxID_ANY, wxBitmap("concentric-circles.png", wxBITMAP_TYPE_PNG), wxPoint(130, 30), wxSize(510, 510));

	//Create menu bar
	m_pMenuBar = new wxMenuBar();

	//Tools menu
	m_pToolsMenu = new wxMenu();

	if (m_pToolsMenu != nullptr)
	{
		m_pToolsMenu->Append(wxID_NETWORK, _T("&Polącz"));
		m_pToolsMenu->Append(wxID_VIEW_DETAILS, _T("&Diagnostyka"));
		m_pToolsMenu->AppendSeparator();
		m_pToolsMenu->Append(wxID_EXIT, _T("&Wyjdź"));
		m_pMenuBar->Append(m_pToolsMenu, _T("&Narzędzia"));

		//About menu
		m_pHelpMenu = new wxMenu();
		m_pHelpMenu->Append(wxID_INFO, _T("&Informacje"));
		m_pMenuBar->Append(m_pHelpMenu, _T("&Pomoc"));

		//Set menu bar
		SetMenuBar(m_pMenuBar);
	}

	//Initialize static texts
	m_static_txt_device = new wxStaticText(this, wxID_ANY, wxT("MB" + std::to_string(mbSlaveNum) + "(COM" + mbPort[mbPortCharLength - 1] + ")"), wxPoint(20, 20), wxSize(110, 30), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_adc1 = new wxStaticText(this, wxID_ANY, "N/A", wxPoint(20, 270), wxSize(110, 30), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_adc2 = new wxStaticText(this, wxID_ANY, "N/A", wxPoint(330, 70), wxSize(110, 30), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_adc3 = new wxStaticText(this, wxID_ANY, "N/A", wxPoint(330, 155), wxSize(110, 30), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_adc4 = new wxStaticText(this, wxID_ANY, "N/A", wxPoint(330, 270), wxSize(110, 30), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_mberror_read = new wxStaticText(this, wxID_ANY, "Błąd w komunikacji Modbus:\nRead Error", wxPoint(590, 20), wxSize(100, 10), wxALIGN_CENTRE_HORIZONTAL);
	m_static_txt_mberror_write = new wxStaticText(this, wxID_ANY, "Błąd w komunikacji Modbus:\nWrite Error", wxPoint(590, 20), wxSize(100, 10), wxALIGN_CENTRE_HORIZONTAL);
	
	if (m_static_txt_adc2 != nullptr) m_static_txt_adc2->SetBackgroundColour(wxColour(0, 170, 250));
	if (m_static_txt_adc3 != nullptr) m_static_txt_adc3->SetBackgroundColour(wxColour(98, 204, 254));
	if (m_static_txt_adc4 != nullptr) m_static_txt_adc4->SetBackgroundColour(wxColour(214, 242, 254));

	//Set sub-title font
	myFont = wxFont(14, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_MEDIUM);
	if (m_static_txt_device != nullptr) m_static_txt_device->SetFont(myFont);

	//Set ADC readings font
	myFont = wxFont(17, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
	if (m_static_txt_adc1 != nullptr) m_static_txt_adc1->SetFont(myFont);
	if (m_static_txt_adc2 != nullptr) m_static_txt_adc2->SetFont(myFont);
	if (m_static_txt_adc3 != nullptr) m_static_txt_adc3->SetFont(myFont);
	if (m_static_txt_adc4 != nullptr) m_static_txt_adc4->SetFont(myFont);

	//Set Modbus read error message font
	myFont = wxFont(11, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_MEDIUM);
	if (m_static_txt_mberror_read != nullptr)
	{
		m_static_txt_mberror_read->SetFont(myFont);
		m_static_txt_mberror_read->SetForegroundColour(wxColour(255, 0, 0));
		m_static_txt_mberror_read->Hide();
	}

	//Set Modbus write error message font
	if (m_static_txt_mberror_write != nullptr)
	{
		m_static_txt_mberror_write->SetFont(myFont);
		m_static_txt_mberror_write->SetForegroundColour(wxColour(255, 0, 0));
		m_static_txt_mberror_write->Hide();
	}

	//Create Modbus write timer
	mbWriteTimer = new wxTimer(this, 10004);
	mbWriteErrorTimer = new wxTimer(this, 10005);

	//Initialize Modbus communication
	ctx = modbus_new_rtu(this->mbPort, mbBaudRate, mbParity, mbNumBits, mbNumStopBits);
	modbus_set_slave(ctx, this->mbSlaveAddress);
	modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
	modbus_set_response_timeout(ctx, 1, 0);
	modbus_set_byte_timeout(ctx, 0, 200000);
	mbStatusConnected = modbus_connect(ctx);

	if (mbStatusConnected != -1)
	{
		//Disable Connect menu item
		if (m_pToolsMenu->IsEnabled(wxID_NETWORK)) m_pToolsMenu->Enable(wxID_NETWORK, FALSE);

		//Enable buttons
		if (!(m_btn_wsun->IsEnabled())) m_btn_wsun->Enable();
		if (!(m_btn_wysun->IsEnabled())) m_btn_wsun->Enable();
	}
	else
	{
		//Show read error
		if (!(m_static_txt_mberror_write->IsShown()))
		{
			if (!(m_static_txt_mberror_read->IsShown())) m_static_txt_mberror_read->Show();
		}

		//Disable buttons and menu items
		if (m_btn_wsun->IsEnabled()) m_btn_wsun->Disable();
		if (m_btn_wysun->IsEnabled()) m_btn_wysun->Disable();
	}

	//Start Modbus communication in background thread
	StartModbusThread();
}

//"Connect" menu item callback
void cMain::OnMenuClickedConnect(wxCommandEvent& evt)
{
	//Initialize Modbus communication
	ctx = modbus_new_rtu(mbPort, mbBaudRate, mbParity, mbNumBits, mbNumStopBits);
	modbus_set_slave(ctx, mbSlaveAddress);
	modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
	mbStatusConnected = modbus_connect(ctx);

	if (mbStatusConnected != -1) //connection successfull
	{
		//Check if thread already exists and is running
		if (GetThread() && GetThread()->IsRunning())
		{
			GetThread()->Delete();
		}

		//Disable Connect menu item
		if (m_pToolsMenu->IsEnabled(wxID_NETWORK)) m_pToolsMenu->Enable(wxID_NETWORK, FALSE);

		//Re-enable buttons
		if (!(m_btn_wsun->IsEnabled())) m_btn_wsun->Enable();
		if (!(m_btn_wysun->IsEnabled())) m_btn_wsun->Enable();

		//Start new thread
		StartModbusThread();
	}
	else
	{
		//Show read error
		if (!(m_static_txt_mberror_write->IsShown()))
		{
			if (!(m_static_txt_mberror_read->IsShown())) m_static_txt_mberror_read->Show();
		}

		//Disable buttons and menu items
		if (m_btn_wsun->IsEnabled()) m_btn_wsun->Disable();
		if (m_btn_wysun->IsEnabled()) m_btn_wysun->Disable();

		//Reset button colours
		if ((m_btn_wysun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wysun->SetBackgroundColour(wxColour(210, 210, 210));
		if ((m_btn_wsun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wsun->SetBackgroundColour(wxColour(210, 210, 210));
	}

	evt.Skip();
}

void cMain::OnMenuClickedDiagnostics(wxCommandEvent& evt)
{
	//Disable Diagnostics menu item
	if (m_pToolsMenu->IsEnabled(wxID_VIEW_DETAILS)) m_pToolsMenu->Enable(wxID_VIEW_DETAILS, FALSE);
	mbDiagnosticsEnabled = TRUE; //send command to Modbus helper thread
	mbDiagnosticsOpenWindowEnabled = TRUE; //send command to Modbus helper thread (open window once data is available)
	evt.Skip();
}

void cMain::OnMenuClickedInformation(wxCommandEvent& evt)
{
	informationWindowIsOpen = TRUE;
	if (m_pHelpMenu->IsEnabled(wxID_INFO)) m_pHelpMenu->Enable(wxID_INFO, FALSE);
	m_frame_info = new cInformation(m_pHelpMenu, &informationWindowIsOpen);
	m_frame_info->SetPosition(this->GetPosition());
	m_frame_info->Show();
}

/* "Wsun" button callback */
void cMain::OnButtonClickedWsun(wxCommandEvent& evt)
{
	if (m_btn_wysun->IsEnabled()) //only respond when other button has not been pressed
	{
		m_btn_wsun->Disable();
		mbModeWriteCoil = 1; //set flag, event handled in Modbus thread
	}
	evt.Skip();
}

/* "Wysun" button callback */
void cMain::OnButtonClickedWysun(wxCommandEvent& evt)
{
	if (m_btn_wsun->IsEnabled()) //only respond when other button has not been pressed
	{
		m_btn_wysun->Disable();
		mbModeWriteCoil = 0; //set flag, event handled in Modbus thread
	}
	evt.Skip();
}

/* Start of Modbus helper thread */
void cMain::StartModbusThread()
{
	if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR)
	{
		wxLogError("Could not create the worker thread!");
		return;
	}
	if (GetThread()->Run() != wxTHREAD_NO_ERROR)
	{
		wxLogError("Could not run the worker thread!");
		return;
	}
}

/* Modbus helper thread */
wxThread::ExitCode cMain::Entry()
{
	while (!GetThread()->TestDestroy())
	{		
		{
			//Enter critical section
			wxCriticalSectionLocker lock(ModbusCS);

			//Read input registers
			mbStatusReadADC = modbus_read_input_registers(ctx, 3001, int(mbNumADCRegisters), mbADCRegisters);
			mbADCErrorsRegister = mbADCRegisters[mbNumADCRegisters - 1];
			wxThread::Sleep(20); //20ms delay

			//Read diagnostics
			if (mbDiagnosticsEnabled)
			{
				modbus_send_raw_request(ctx, mbDiagRawRequest, 6 * sizeof(uint8_t));
				mbStatusReadDiagnostics = modbus_receive_raw_confirmation(ctx, mbDiagRawResponse);
				wxThread::Sleep(20); //20ms delay

				//Update diagnostics
				if (mbStatusReadDiagnostics != -1)
				{
					for (uint8_t i = 0; i < mbNumDiagnostics; i++)
					{
						mbDiagnosticsRegister[i] = (uint16_t)mbDiagRawResponse[i * 2 + 4] << 8;
						mbDiagnosticsRegister[i] |= (uint16_t)mbDiagRawResponse[i * 2 + 5];
					}
				}
			}

			//Reset diagnostics
			if (mbDiagnosticsResetEnabled)
			{
				//Send diagnostics reset request
				modbus_send_raw_request(ctx, mbDiagResetRawRequest, 6 * sizeof(uint8_t));
				mbStatusResetDiagnostics = modbus_receive_raw_confirmation(ctx, mbDiagRawResponse);
				wxThread::Sleep(20); //20ms delay

				//Clear diagnostic register in preparation for new data
				if (mbStatusResetDiagnostics != -1)
				{
					for (uint8_t i = 0; i < mbNumDiagnostics; i++)
					{
						mbDiagnosticsRegister[i] = 0;
					}
				}

				//Clear reset flags
				mbDiagnosticsResetting = FALSE;
				mbDiagnosticsResetEnabled = FALSE;
			}

			//Write to coil
			if (mbModeWriteCoil != -1)
			{
				mbStatusWriteCoil = modbus_write_bit(ctx, 1, mbModeWriteCoil);
				wxThread::Sleep(20); //20ms delay
			}

			//Read configuration coil
			mbStatusReadCoil = modbus_read_bits(ctx, 1, 1, &mbConfigurationCoil);

			//Signal to main thread that new data has been received
			wxQueueEvent(GetEventHandler(), new wxThreadEvent());
		}

		//Delay next Modbus message cycle by 200ms
		wxThread::Sleep(mbScanRate);
	}

	//Destroy thread
	return (wxThread::ExitCode)0;
}

/* Event handler for the Modbus helper thread */
void cMain::OnThreadUpdate(wxThreadEvent& evt)
{
	//Enter critical section
	wxCriticalSectionLocker lock(ModbusCS);

	//Set button colors
	if (mbStatusReadCoil != -1)
	{
		if (mbConfigurationCoil == 1)
		{
			if ((m_btn_wsun->GetBackgroundColour()) != wxColour(0, 210, 0)) m_btn_wsun->SetBackgroundColour(wxColour(0, 210, 0));
			if ((m_btn_wysun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wysun->SetBackgroundColour(wxColour(210, 210, 210));
		}
		else
		{
			if ((m_btn_wysun->GetBackgroundColour()) != wxColour(0, 210, 0)) m_btn_wysun->SetBackgroundColour(wxColour(0, 210, 0));
			if ((m_btn_wsun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wsun->SetBackgroundColour(wxColour(210, 210, 210));
		}
	}

	//Handle holding register write errors
	if (mbModeWriteCoil != -1)
	{
		if (mbStatusWriteCoil == -1)
		{
			//Show Modbus write error
			m_static_txt_mberror_write->Show();
			if (!(mbWriteErrorTimer->IsRunning())) mbWriteErrorTimer->Start(3000, wxTIMER_ONE_SHOT); //start error message timer
		}

		//If coil write is successful, reset flag, otherwise try again
		if (mbModeWriteCoil == mbConfigurationCoil)
		{
			if (!(mbWriteTimer->IsRunning())) mbWriteTimer->Start(1250, wxTIMER_ONE_SHOT); //start button pressed timer
			mbModeWriteCoil = -1;
		}
	}

	//Open diagnostics window
	if (mbDiagnosticsOpenWindowEnabled && !mbDiagnosticsWindowIsOpen)
	{
		m_frame_diag = new cDiagnostics(
			&mbPortCharLength,
			mbPort,
			mbSlaveNum,
			&mbDiagnosticsWindowIsOpen, 
			&mbDiagnosticsEnabled, 
			&mbDiagnosticsResetEnabled,
			&mbDiagnosticsResetting,
			&mbStatusReadDiagnostics, 
			&mbStatusResetDiagnostics,
			mbDiagnosticsRegister, 
			&mbADCErrorsRegister,
			m_pToolsMenu);

		m_frame_diag->SetPosition(this->GetPosition());
		m_frame_diag->Show();
		mbDiagnosticsOpenWindowEnabled = FALSE;
	}

	if (mbStatusReadADC == -1)
	{
		//Reset button colours
		if ((m_btn_wysun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wysun->SetBackgroundColour(wxColour(210, 210, 210));
		if ((m_btn_wsun->GetBackgroundColour()) != wxColour(210, 210, 210)) m_btn_wsun->SetBackgroundColour(wxColour(210, 210, 210));

		if (mbDiagnosticsWindowIsOpen)
		{
			//Disable Diagnostics menu item
			if (m_pToolsMenu->IsEnabled(wxID_VIEW_DETAILS)) m_pToolsMenu->Enable(wxID_VIEW_DETAILS, FALSE);
		}

		//Show read error
		if (!(m_static_txt_mberror_write->IsShown()))
		{
			if (!(m_static_txt_mberror_read->IsShown())) m_static_txt_mberror_read->Show();
		}

		//Disable buttons
		if (m_btn_wsun->IsEnabled()) m_btn_wsun->Disable();
		if (m_btn_wysun->IsEnabled()) m_btn_wysun->Disable();

		//Update static text
		m_static_txt_adc1->SetLabel(wxT("N/A"));
		m_static_txt_adc2->SetLabel(wxT("N/A"));
		m_static_txt_adc3->SetLabel(wxT("N/A"));
		m_static_txt_adc4->SetLabel(wxT("N/A"));

		//Enable "connect" menu item
		if (!(m_pToolsMenu->IsEnabled(wxID_NETWORK))) m_pToolsMenu->Enable(wxID_NETWORK, TRUE);
	}
	else if (mbADCErrorsRegister != 0)
	{
		//Hide error
		if (m_static_txt_mberror_read->IsShown()) m_static_txt_mberror_read->Hide();

		//Disable "connect" menu item
		if (m_pToolsMenu->IsEnabled(wxID_NETWORK)) m_pToolsMenu->Enable(wxID_NETWORK, FALSE);

		//Enable buttons
		if (!(mbWriteTimer->IsRunning()) && mbModeWriteCoil == -1)
		{
			if (!(m_btn_wsun->IsEnabled())) m_btn_wsun->Enable();
			if (!(m_btn_wysun->IsEnabled())) m_btn_wysun->Enable();
		}

		//Update static text
		wxString adc1_label = ((mbADCErrorsRegister >> 0) & 0x0001) ? wxT("N/A") : wxT("" + std::to_string(mbADCRegisters[0]));
		wxString adc2_label = ((mbADCErrorsRegister >> 1) & 0x0001) ? wxT("N/A") : wxT("" + std::to_string(mbADCRegisters[1]));
		wxString adc3_label = ((mbADCErrorsRegister >> 2) & 0x0001) ? wxT("N/A") : wxT("" + std::to_string(mbADCRegisters[2]));
		wxString adc4_label = ((mbADCErrorsRegister >> 3) & 0x0001) ? wxT("N/A") : wxT("" + std::to_string(mbADCRegisters[3]));

		m_static_txt_adc1->SetLabel(adc1_label);
		m_static_txt_adc2->SetLabel(adc2_label);
		m_static_txt_adc3->SetLabel(adc3_label);
		m_static_txt_adc4->SetLabel(adc4_label);
	}
	else
	{
		if (!mbDiagnosticsWindowIsOpen)
		{
			//Disable Diagnostics menu item
			if (!(m_pToolsMenu->IsEnabled(wxID_VIEW_DETAILS))) m_pToolsMenu->Enable(wxID_VIEW_DETAILS, FALSE);
		}

		//Hide error
		if (m_static_txt_mberror_read->IsShown()) m_static_txt_mberror_read->Hide();
		
		//Disable "connect" menu item
		if (m_pToolsMenu->IsEnabled(wxID_NETWORK)) m_pToolsMenu->Enable(wxID_NETWORK, FALSE);

		//Enable buttons
		if (!(mbWriteTimer->IsRunning()) && mbModeWriteCoil == -1)
		{
			if (!(m_btn_wsun->IsEnabled())) m_btn_wsun->Enable();
			if (!(m_btn_wysun->IsEnabled())) m_btn_wysun->Enable();
		}

		//Update static text
		m_static_txt_adc1->SetLabel(wxT("" + std::to_string(mbADCRegisters[0])));
		m_static_txt_adc2->SetLabel(wxT("" + std::to_string(mbADCRegisters[1])));
		m_static_txt_adc3->SetLabel(wxT("" + std::to_string(mbADCRegisters[2])));
		m_static_txt_adc4->SetLabel(wxT("" + std::to_string(mbADCRegisters[3])));
	}
}

/* Timer event - write to configuration coil */
void cMain::OnWriteTimer(wxTimerEvent& evt)
{
	//Re-enable buttons
	if (!(m_btn_wsun->IsEnabled())) m_btn_wsun->Enable();
	if (!(m_btn_wysun->IsEnabled())) m_btn_wysun->Enable();
}

/* Timer event - write to configuration coil error message*/
void cMain::OnWriteErrorTimer(wxTimerEvent& evt)
{
	//Hide Modbus write error
	if (m_static_txt_mberror_write->IsShown()) m_static_txt_mberror_write->Hide();
}

/* Frame closed - Modbus thread destruction */
void cMain::OnClose(wxCloseEvent&)
{
	//Close diagnostics window and delete diagnostics thread
	if (mbDiagnosticsWindowIsOpen)
	{
		m_frame_diag->GetThread()->Delete();
		m_frame_diag->Close();
	}

	//Close information window
	if (informationWindowIsOpen)
	{
		m_frame_info->Close();
	}

	//Check if thread exists and is running
	if (GetThread() && GetThread()->IsRunning())
	{
		GetThread()->Delete();
	}

	//Close Modbus connection and free resources
	modbus_close(ctx);
	modbus_free(ctx);

	Destroy();
}

/* Frame closed from menu - Modbus thread destruction */
void cMain::OnMenuClickedExit(wxCommandEvent& evt)
{
	//Close diagnostics window and delete diagnostics thread
	if (mbDiagnosticsWindowIsOpen)
	{
		m_frame_diag->GetThread()->Delete();
		m_frame_diag->Close();
	}

	//Close information window
	if (informationWindowIsOpen)
	{
		m_frame_info->Close();
	}

	//Check if thread exists and is running
	if (GetThread() && GetThread()->IsRunning())
	{
		GetThread()->Delete();
	}

	//Close Modbus connection and free resources
	modbus_close(ctx);
	modbus_free(ctx);

	Destroy();
}
It seems that the timer might be causing the problem, perhaps I should ensure it is stopped when closing a window?

Regards,
Ksawery
Attachments
Annotation 2019-08-07 111643.jpg
Annotation 2019-08-07 111643.jpg (39.82 KiB) Viewed 692 times
Ksawery
Experienced Solver
Experienced Solver
Posts: 83
Joined: Thu Jul 25, 2019 12:31 pm

Re: Exception thrown when closing window

Post by Ksawery »

I added the following lines of code to disable all timers when closing the window, and the issue seems to have disappeared:

Code: Select all

//Stop any running timers
if (mbWriteTimer->IsRunning()) mbWriteTimer->Stop();
if (mbWriteErrorTimer->IsRunning()) mbWriteErrorTimer->Stop();
I don't know whether that is the correct solution to the problem, so I'm not marking this as solved yet.

Regards,
Ksawery
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Exception thrown when closing window

Post by doublemax »

I added the following lines of code to disable all timers when closing the window, and the issue seems to have disappeared:
Yes. Otherwise it's possible that the timer event handler gets called, but a control that is accessed is already destroyed.
Use the source, Luke!
Post Reply