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

Editors IDEsMicrosoft DevelopmentSystem Programming

Avatar of undefined
Last Comment
deafie_sb

8/22/2022 - Mon
AndyAinscow

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.
ASKER
deafie_sb

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
AndyAinscow

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);
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
ASKER
deafie_sb

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
ASKER
deafie_sb

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

alb66

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

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
AndyAinscow

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.
AndyAinscow

@ 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;
 
}
ASKER
deafie_sb

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?
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
alb66

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)

ASKER
deafie_sb

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
AndyAinscow

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.
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
ASKER
deafie_sb

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
AndyAinscow

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.
ASKER
deafie_sb

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



This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
AndyAinscow

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 ?)
ASKER
deafie_sb

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
AndyAinscow

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
See how we're fighting big data
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
ASKER
deafie_sb

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

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
alb66

May you post also PanelDlg::DoDataExchange() ?
ASKER
deafie_sb

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

AndyAinscow

>>>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)
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
ASKER
deafie_sb

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