Link to home
Start Free TrialLog in
Avatar of deafie_sb
deafie_sb

asked on

MFC UpdateData and Threading problems

Im having a weird problem with a MFC Dialog program Im writing and I cannot find a good solution. Im hoping someone might have an answer.

The problem is with the UpdateData function when new data is to be placed on in the dialog panel called PanelDlg. Im running two threads that are IDENTICAL in every aspect, except on which channel it should be reading

I have two worker threads that I am constantly running because it needs to listen at a PCI card for incoming data. Both the thread and the main Dialog classes are using DLLIMPORT functions: the worker threads to LISTEN and send a message when incoming data is being recieved from a channel, and within the Dialog panel itself, read the data and update the screen.

Some notes:

CH1_text and CH2_text are CString variables and they are connected to the dialog box by using:

      DDX_Text(pDX, CH1_INCOMING, CH1_text);
      DDX_Text(pDX, CH2_INCOMING, CH2_text);

And CH1_INCOMING and CH2_INCOMING are RichEdit boxes on the dialog panel.

Also you may notice that while I am passing WPARAM and LPARAM back to the main Dialog class, no data is being sent. This is because I only want to raise the event and have the functions themselves recieve the data and then update as it is written in onFlag1 and onFlag2 functions of the dialog class.

The threads are working properly and they are indeed firing the correct messages, therefore the functions in PanelDlg are being called correctly as well.

The problem: Only the function onFlag1 will update, but not on the function onFlag2. Ive tried using UpdateData(TRUE) and without it, but I get the same results.

Does any one have any ideas on what Im doing wrong?

Steve


------ global.h file
 
typedef struct _THREADINFOSTRUCT
{
	HWND hWnd;
	CString cString;
} THREADINFOSTRUCT;
 
 
------ PanelDlg.cpp file:
 
THREADINFOSTRUCT *myStruct = new THREADINFOSTRUCT;
 
 
PanelDlg::DoDataExchange(CDataExchange* pDX)
{
// Some code here...
 
BEGIN_MESSAGE_MAP(CSubAssemblyPanelDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
 
	// .... more stuff
 
	ON_MESSAGE(WM_ONFLAG1, onFlag1)
	ON_MESSAGE(WM_ONFLAG2, onFlag2)
 
 
END_MESSAGE_MAP()
}
 
BOOL PanelDlg::OnInitDialog()
{
	// ... some code
 
	MyStruct->hWnd = m_hWnd;
	tis->cString = "Main Dialog Thread";
	s1Thread = AfxBeginThread(s1.ThreadProc1, myStruct, THREAD_PRIORITY_NORMAL,0,0,0);
	s2Thread = AfxBeginThread(s1.ThreadProc2, myStruct, THREAD_PRIORITY_NORMAL,0,0,0);
 
	// ... some code
 
}
 
LPARAM PanelDlg::onFlag1(WPARAM wParam, LPARAM lParam)
 
{
      int count, i;
      char inchar;
      //UpdateData(TRUE);
      NumBytesWaiting(ch1,&count);
      for (i = 0; i < count; i++)
 
      {
            ReceiveByte(ch1, &inchar);
            CH1_text.AppendChar(inchar);
      }
 
      UpdateData(FALSE);      //THIS LINE DOES UPDATE CORRECTLY
      return 0;
}
 
 
LPARAM PanelDlg::onFlag2(WPARAM wParam, LPARAM lParam)
 
{
      int count, i;
      char inchar;
      //UpdateData(TRUE);
 
      NumBytesWaiting(ch2,&count);
      for (i = 0; i < count; i++)
 
      {
            ReceiveByte(ch2, &inchar);
            CH2_text.AppendChar(inData.inchar);
      }
 
      UpdateData(FALSE);    <---DOES NOT UPDATE THE DIALOG BOX! 
 
     return 0;
 
}
 
---- thread.h file
 
#define WM_ONFLAG1 (WM_USER+0x101)
#define WM_ONFLAG2 (WM_USER+0x102)
 
---- thread.cpp file
 
UINT serial1::ThreadProc1(LPVOID lParam)
{
	THREADINFOSTRUCT *tis = (THREADINFOSTRUCT*)lParam;
	int status;
	int cnt;
 
	while (true)
	{
		// TODO: add code here
		
		status = Wait_For_Interrupt(ch1, 100); // channel 1 for 100 msec
 
		if (status >= 0)
		{
			NumBytesWaiting_SER(serial_ch0,&cnt);
			if (cnt > 0)
				PostMessage(tis->hWnd,WM_ONFLAG1,0,0);
 
		}
	}
 
	return 0;
}
 
UINT serial1::ThreadProc2(LPVOID lParam)
{
	THREADINFOSTRUCT *tis = (THREADINFOSTRUCT*)lParam;
	int status;
	int cnt;
 
	while (true)
	{
		// TODO: add code here
		
		status = Wait_For_Interrupt(ch2, 100);  // channel 2 for 100 msec
 
		if (status >= 0)
		{
			NumBytesWaiting_SER(serial_ch0,&cnt);
			if (cnt > 0)
				PostMessage(tis->hWnd,WM_ONFLAG2,0,0);
 
			 
		}
	}
 
	return 0;
}

Open in new window

Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Two things.
1) Use a breakpoint (or TRACE statement) to check the onFlag2 function is actually being called.
2) Check the DoDataExchange (you haven't pasted the contents of it) actually maps the second string to a control on your dialog.
Avatar of deafie_sb
deafie_sb

ASKER

Well, sadly, I wasn't able to debug it through the computer I am working on. The PCI card is on another computer and I'm passing the executable through USB memory stick. I know... it's very archaic the way it is but it's the only solution. The company I work for is VERY paranoid and I'm better off getting a root canal than getting something done by my IT department.

I did, however, used AfxMessageBox in the onFlag2 function like "onFlag2 called", and then read the data, have another AfxMessageBox to display the CString CH2_text, and once again it verified that it was getting data. Therefore, the firing of the message and the DLL interops are working fine. It's just when it comes up to UpdateData(FALSE), it does NOT update the data.

It will in onFlag1 and that's identical to onFlag2.

Wierd, isn't it?

Steve
IT Department - hmmm, :-)


You do have an edit (why richedit?) control connected to the string don't you? - check your DoDataExchange function.

Also consider using
SetDlgItemText(IDC_EDIT_CONTROL, STRING_VALUE);
Actually... when I looked at it again, I just realized something. When I was testing it out on the threads, I was doing it INDIVIDUALLY and not together on the AfxMessageBox. I seeing something weird more. In both functions, I put in "Inside onFlag1" and "Inside onFlag2" respectively, when data is coming in on channel 1, it first fires up onFlag1, then onFlag2... when I'm trying to see data coming in from onFlag2, nothing happens.

I'm guessing then there's something wrong on how the messages are being sent back to the main thread. But there again, I don't see where there could be a problem since I have the messages on different values. Any ideas?

Steve
Sorry, I just saw your comment now. I guess we were posting at the same time.

I have it as a rich edit so it can scroll better. I was asked to make it a RichEdit box. The electrical engineer wanted that way so he can scroll back and look at the data. Hey, he's the boss. I'm just working with it. :-D

Steve

In onFlag2 you read the data in the local variable inchar but you append a (global?) inData.inchar.

PARAM PanelDlg::onFlag2(WPARAM wParam, LPARAM lParam)
 
{
      int count, i;
      char inchar;
      //UpdateData(TRUE);
 
      NumBytesWaiting(ch2,&count);
      for (i = 0; i < count; i++)
 
      {
            ReceiveByte(ch2, &inchar);
            CH2_text.AppendChar(inchar); <----------------------
      }
 
      UpdateData(FALSE);    <---DOES NOT UPDATE THE DIALOG BOX! 
 
     return 0;
 
}

Open in new window

Rich edit - OK, was just puzzled.

Behaviour - wierd, I don't see anything obviously wrong.

From your code the thread proc is identical apart from the port/message to return.
If that is correct then I would suggest the following.
Have just one thread proc.
Expand your struct (THREADINFOSTRUCT) to include the Port to listen to.
In the thread proc use that ID to control which port to listento AND pass that back as a parameter when you do the PostMessage.

The recipient dialog can then use a switch statement based on the parameter being returned.

For me you then have less duplicate code - possibly easier to understand and debug / expand in future if required.
@ alb66

PARAM PanelDlg::onFlag2(WPARAM wParam, LPARAM lParam)
 
{
      int count, i;
      char inchar;  <<----------------  ****************************
      //UpdateData(TRUE);
 
      NumBytesWaiting(ch2,&count);
      for (i = 0; i < count; i++)
 
      {
            ReceiveByte(ch2, &inchar);
            CH2_text.AppendChar(inchar); <----------------------
      }
 
      UpdateData(FALSE);    <---DOES NOT UPDATE THE DIALOG BOX!
 
     return 0;
 
}
oh yeah... I forgot about that one and I corrected it a long time ago, like last week. :-)

That code was a bit old and I didn't update it until now. I had to restore off the network and that was in there from a test I was doing. Again, my IT department and I'll leave it at that.

I'm still having the same problem. I'm seeing the onFlag1 being called, but not onFlag2. I'm wondering if there's a problem I have with the PostMessage() calls?
You should define application's messages using WM_APP and not WM_USER:
http://msdn.microsoft.com/en-us/library/ms644930(VS.85).aspx
I'm not sure if it is the solution, but it is better if change the define.

#define WM_ONFLAG1 (WM_APP+0x101)
#define WM_ONFLAG2 (WM_APP+0x102)

As for your other comment, and again, I do apologize. I guess I'm posting just as you are as well...

This card I am using is a customize card that deals with different protocols: RS232, ARNIC 429, MIL-1553, discrete, etc. and I'm using their libraries to run the card via MFC, hence why I am running by dialog. The function NumBytesWaiting actually will SUSPEND control until data is present in the PCI Card buffer. Another words, if it is set at ZERO, then it will suspend until it comes back with a positive value. That's why I was spawning off the process into two threads so it wouldn't hang up the system, nor will it hang up the other channel if there is data present in that buffer as well.

After I am done with this one, I have to add two other channels that deals with another, and that's going to be a bit easier since I could use the your method to send which channel to read and where. But that adds more of a problem because it will still be another message that I have to produce so it will know that a different protocol is being read than what is on Channel 1 and Channel 2.

I actually had this written in Visual Studio 2008 C#, but the machine that has this card is Windows 2000, and Microsucks won't allow .NET Framework 3.5 to work in a 2000 environment. Yes, I know IV2008 could set for .NET 2.0, but even when I did so, I'm still seeing some features of 3.5 still going through and that's causing havoc with Win2000.

And I wondering why I haven't gone postal yet!!!! :-D

Steve
OK, thanks for the more info.
Your logic looks to be sensible.  I don't see why it should not work.

The only thing I can see (as your current message definitions do work - message box) is a problem with the data exchange itself.

Could you add TWO new edit controls to the dialog and use a SetDlgItemText call, one per message handler, to duplicate the output but bypassing the DoDataExchange mechanism.
alb66:

Thanks for the suggestion, and I just tried it, but I'm still expericing the same problem. Only when data is sent to onFlag1 will the dialog panel, but not onFlag2.

Andy:

How would I go about doing SetDlgItem? If you saw what I had previously stated, I said that CH1_text and CH2_text are CString variables; CH1_INCOMMING and CH1_INCOMMING  are RichTextBox. They are all connected to the dialog box by being defined in PanelDlg::DoDataExchange(CDataExchange* pDX):

      DDX_Text(pDX, CH1_INCOMING, CH1_text);
      DDX_Text(pDX, CH2_INCOMING, CH2_text);

I just realized that I didn't put it in the code snippet that I had, however, I did state that they were connected with those commands. My apologies if there was confusion over that one.

I also think we're all missing something else... As I said before, when WM_ONFLAG1 is sent, onFlag1 is called but at the same time it also calls up onFlag2 as well. Therefore it does update the RichText boxes. However, when WM_ONFLAG2 message is sent, onFlag2 is never called up, even when I changed from WM_USER to WM_APP.

I need a stiff drink after I get this working... :-)

Steve
eg.

LPARAM PanelDlg::onFlag2(WPARAM wParam, LPARAM lParam)
 
{
static int siCounter = 0;

      int count, i;
      char inchar;
      //UpdateData(TRUE);
 
      NumBytesWaiting(ch2,&count);
      for (i = 0; i < count; i++)
 
      {
            ReceiveByte(ch2, &inchar);
            CH2_text.AppendChar(inData.inchar);
      }
 
CString s;
s.Format("onFlag2 (%d): %s", siCounter++, CH2_text);
SetDlgItemText(IDC_EDIT2, s);  //Put the text directly into the duplicate edit control

      UpdateData(FALSE);    <---DOES NOT UPDATE THE DIALOG BOX!

 
     return 0;
 
}


Do similar for the handler onFlag1.
What this will do is display the text directly, no transfer through the DoDataExchange mechanism - see if the problem is there rather than in the message handlers.  Also the static int will provide a counter so we see if the getting of the text into the CString var is somehow failing by the counter incrementing but the text not being changed.
Andy:
Just real quick -- when you mean IDC_EDIT2, are you referring it to be CH2_INCOMING? That's what I set the ID (identifer) for the RichTextBox.

And after adding your code, still nothiing. Not even the SetDlgItemText worked in both functions!

Again... when data is coming on Channel 2, NOTHING happens. However, on Channel 1, BOTH RichText boxes are updates on Channel 1 and 2!

Got a sledgehammer? :-D



I meant create two NEW edit boxs and populate these with text with a SetDlgItemText call.  If you already had a IDC_EDIT2 then it was just chance.


>>Not even the SetDlgItemText worked in both functions!
Very odd - did you do EXACTLY what I suggested in code?


(Is this dialog in a dll ?)
Yes, I did except creating the two NEW edit boxes. I'm going to do that next.

One other thing... in another forum, someone suggested that my message map could be messed up. Were you a bit curious about that as well?

Steve
ASKER CERTIFIED SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Okay Andy,

I did what you suggested and this is what happened. I've created two new plain edit boxes. I had to do some modification to:

.AppendFormat(_T("onFlag1 (%d): %s\n"), siCounter++,(const BYTE*)(LPCTSTR)RS232_CH1_text);

the counter was updating. I sent "hello" in ascii and the box showed "onFlag1 (5) hello", yet also at the same time, in the second box it said "onFlag2 (6) ".

Which brings me back to think there is something wrong with the message map. I'm including it so you can see what it is just in case.

I did notice something different. On where it says ON_MESSAGE(WM_ONFLAG1, onFlag1), it wasn't referencing by address. All the other message maps had &PanelDlg::Foo, yet mine wasn't.

So I went ahead and changed it to &PanelDlg::onFlag1 to see what happens, and I even changed it to LRESULT.

I'm sad to say nothing is working. This is really upsetting now... I don't know why the thread of the second channel will NOT call up onFlag2!

Steve
BEGIN_MESSAGE_MAP(PanelDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(A_CH1, &PanelDlg::OnBnClickedCh1)
	ON_BN_CLICKED(A_CH2, &PanelDlg::OnBnClickedCh2)
	ON_BN_CLICKED(A_CH3, &PanelDlg::OnBnClickedCh3)
	ON_BN_CLICKED(A_CH4, &PanelDlg::OnBnClickedCh4)
	ON_BN_CLICKED(A_L1, &PanelDlg::OnBnClickedL1)
	ON_BN_CLICKED(A_L2, &PanelDlg::OnBnClickedL2)
	ON_BN_CLICKED(A_L3, &PanelDlg::OnBnClickedL3)
	ON_BN_CLICKED(A_L4, &PanelDlg::OnBnClickedL4)
	ON_BN_CLICKED(A_L5, &PanelDlg::OnBnClickedL5)
	ON_BN_CLICKED(A_L6, &PanelDlg::OnBnClickedL6)
	ON_BN_CLICKED(H1, &PanelDlg::OnBnClickedH1)
	ON_BN_CLICKED(H2, &PanelDlg::OnBnClickedH2)
	ON_BN_CLICKED(H3, &PanelDlg::OnBnClickedH3)
	ON_BN_CLICKED(H4, &PanelDlg::OnBnClickedH4)
	ON_BN_CLICKED(H5, &PanelDlg::OnBnClickedH5)
	ON_BN_CLICKED(H6, &PanelDlg::OnBnClickedH6)
	ON_BN_CLICKED(P1, &PanelDlg::OnBnClickedP1)
	ON_BN_CLICKED(P2, &PanelDlg::OnBnClickedP2)
	ON_BN_CLICKED(P3, &PanelDlg::OnBnClickedP3)
	ON_BN_CLICKED(P4, &PanelDlg::OnBnClickedP4)
	ON_BN_CLICKED(P5, &PanelDlg::OnBnClickedP5)
	ON_BN_CLICKED(P6, &PanelDlg::OnBnClickedP6)
	ON_WM_TIMER()
 
	ON_MESSAGE(WM_ONFLAG1, onFlag1)
	ON_MESSAGE(WM_ONFLAG2, onFlag2)
 
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_SEND1, &PanelDlg::OnBnClickedSend1)
	ON_BN_CLICKED(IDC_SEND2, &PanelDlg::OnBnClickedSend2)
	ON_BN_CLICKED(IDOK, &PanelDlg::OnBnClickedOk)
END_MESSAGE_MAP()

Open in new window

May you post also PanelDlg::DoDataExchange() ?
Sure can Alb.


void PanelDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, RX1_DATA, RX1_data_value);
	DDX_Text(pDX, RX1_PARITY, RX1_Parity_value);
	DDX_Text(pDX, RX1_SSM, RX1_SSM_value);
	DDX_Text(pDX, RX1_SDI, RX1_SDI_value);
	DDX_Text(pDX, RX1_LABEL, RX1_label_value);
	DDX_Text(pDX, RX1_DATA_HEX, RX1_data_hex_value);
	DDV_MaxChars(pDX, RX1_data_hex_value, 4);
	DDX_Text(pDX, RX1_LABEL_OCTAL, RX1_label_octal_value);
	DDX_Text(pDX, RX2_PARITY, RX2_Parity_value);
	DDX_Text(pDX, RX2_SSM, RX2_SSM_value);
	DDX_Text(pDX, RX2_DATA, RX2_data_value);
	DDX_Text(pDX, RX2_SDI, RX2_SDI_value);
	DDX_Text(pDX, RX2_LABEL, RX2_label_value);
	DDX_Text(pDX, RX2_DATA_HEX, RX2_data_hex_value);
	DDV_MaxChars(pDX, RX2_data_hex_value, 4);
	DDX_Text(pDX, RX2_LABEL_OCTAL, RX2_label_octal_value);
	DDX_Text(pDX, CH1_INCOMING, CH1_text);
	DDX_Text(pDX, CH2_INCOMING, CH2_text);
	DDX_Text(pDX, TX1_OUTPUT, TX1_text);
	DDX_Text(pDX, TX2_OUTPUT, TX2_text);
}

Open in new window

>>>I did what you suggested and this is what happened. I've created two new plain edit boxes. I had to do some modification to:

.AppendFormat(_T("onFlag1 (%d): %s\n"), siCounter++,(const BYTE*)(LPCTSTR)RS232_CH1_text);



There should be no need to cast a CString variable for a Format fn call of another CString var.  Exactly what is the variable RS232_CH1_text declared as?  (I thought you had said it was a CString)
Hi andy,

Sorry for such a long delay. I've been out sick. Someone else suggested to me to do it this way:

ON_MESSAGE(WM_ONFLAG1, &PanelDlg::onFlag1)
ON_MESSAGE(WM_ONFLAG2, &PanelDlg::onFlag2)

That seemed to solve the problem. As for the other question you had, RS232_CH1 was from an old file that I was copying and pasting into my new project and I forgot to change the variable name.

Sorry about the delay, and I really do appreciate your help guys.

Steve