|
This approach can work if you take some precautions. In order to ensure proper concurrency in the access to the dialog box from the different threads, use SendMessage . so, instead of having a regular method CMyDlgBox::AddString(const CString& str) , set a user-defined message (say WM_MYDLGBOX_ADDSTRING ), write a handler for it and have the threads feed the message box with m_myDlgBox.SendMessage(WM_MYDLGBOX_ADDSTRING,...) . The other safety measure you should take is making sure that the dialog box does not lock on the worker threads (say by doing WaitForSingleObject on them): this could result in deadlocks if the threads are blocked on the previous SendMessage .
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Either use the way specified by the other reply, or:
pass the HWND of the richedit control to the worker thread, and use
CRichEditCtrl * re = (CRichEditCtrl *) CWnd::FromHWnd(hwdRichEdit);
_ASSERTE(re && re->IsKindof(RUNTIME_CLASS(CRichEditCtrl));
|
|
|
|
|
Yeah, but then he'd need sync mechanisms too.
If you send messages you dont need any sync mechanisms.
Nish
It's seven o'clock
On the dot
I'm in my drop top
Cruisin' the streets - Oh yeah
I got a real pretty, pretty little thing that's waiting for me
|
|
|
|
|
All the InsertText etc. are SendMessages - which do the job nicely.
MFC's thread support is intended to work this way (although this can be quite a pain)
|
|
|
|
|
Mr. Lopez has the best answer. Passing messages from a thread to a window (which is running on the main app thread) is the best way for these objects to communicate.
But here are some more things to think about...
PostMessage is a better choice than SendMessage. SendMessage waits for a response from the receiver. This can cause deadlocks. Also PostMessage is the only way to send a message to a user thread.
Note that using PostMessage requires a little more work. You can't do this:
CString s("Thread is busy.");
::PostMessage(hWnd, WM_UPDATE_STATUS, (WPARAM)0, (LPARAM)&s); If s loses scope before the message get processed then your code will crash. You will want to do this:
CString *ps = new CString("Thread is busy.");
::PostMessage(hWnd, WM_UPDATE_STATUS, (WPARAM)0, (LPARAM)s); As noted the receiver must delete the object so you don't have a memory leak:
LRESULT CMyWnd::OnThreadMessage(WPARAM w, LPARAM l)
{
ASSERT(l);
if(l)
{
CString *ps = (CString *)l;
delete ps;
}
return 0;
} You can see this requires more work but the code is much more crash proof.
Your threads should also check their message queues. This gives your window a way to communicate to them. This gives you a chance to tell them to stop or die. You can do something like this in your thread loop:
MSG msg;
if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_STOP_THREAD)
{
}
else
if(msg.message == WM_QUIT)
{
}
} When you are done with the threads make sure they die and clean up. I do something like this before my window closes:
void CMyWnd::OnDestroy()
{
if(m_pThread)
{
m_pThread->PostThreadMessage(WM_QUIT, 0, 0);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
delete m_pThread;
m_pThread = NULL;
MSG msg;
while(::PeekMessage(&msg, (HWND)NULL, WM_UPDATE_STATUS,
WM_UPDATE_STATUS, PM_REMOVE))
{
CString *ps = (CString *)msg.lParam;
if(ps)
delete ps;
}
}
CView::OnDestroy();
} Keeping these things in mind when you write thread code will help keep your threads robust and crash proof.
Hope this helps.
Jonathan Craig
www.mcw-tech.com
|
|
|
|
|
Thanks for your replies everyone! Its given me something to work on.
I'm very new to MFC and Windows Programming, but I'm learning a lot about it. The frustrating thing is that I know I'm getting stuck on things that would probably be quite simple!
Anyway, I think I figured out how to create a custom message and map it to a function, but what is the handle hWnd (is it the dialog handle or the Richedit control handle?) and how should I get it and access it from the thread?? This is probably a silly question though and I'll probably figure it out before long
Thanks again
-Mark
|
|
|
|
|
No, that's not a silly question. After I posted my message I noticed I left that information out.
hWnd is a HWND type variable which is a standard windows handle. If you have any class derived from CWnd (dialogs, controls, views, etc.) they have a windows handle. It is the m_hWnd member variable. The CWnd::GetSafeHwnd() method is the best way to get it. See the MSDN.
If your thread is going to send or post a message back to a window, then it must have at least a HWND to the window. You could pass it a pointer to the window object itself like CDialog *, CView *, CFormView *. Since each of these are derived from CWnd they all have a m_hWnd member. I prefer to pass the thread just the m_hWnd (HWND) member since that's all it needs to send or post a message. Also the thread doesn't need to know what type of window (dialog, view, etc.) it is, just that it is a window. This make the thread code a little more reusable.
You can pass a thread any information it needs from the AfxBeginThread method. The second parameter is used for this. I like to pack the information into a structure and pass a pointer to the structure.
In the header file:
#define WM_THREAD_WORKING (WM_USER + 1)
typedef struct _thread_info_tag
{
HWND pNotifyWnd;
long lValue1;
int nValue2;
char szValue3[80];
} _thread_info;
class CMyDialog : public CDialog
{
.
.
static UINT ThreadFunc(LPVOID lpParam);
.
.
.
.
afx_msg LRESULT OnThreadMessge(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
}; In source file:
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
.
.
ON_MESSAGE(WM_THREAD_WORKING, OnThreadMessge)
END_MESSAGE_MAP()
CMyDialog::OnStartThread()
{
_thread_info *pInfo = new _thread_info;
pInfo->pNotifyWnd = this->GetSafeHwnd();
pInfo->lValue1 = 12345;
pInfo->nValue2 = -321;
strcpy(pInfo->szValue3, "String for thread.");
AfxBeginThread(ThreadFunc, (LPVOID)pInfo);
.
.
}
UINT CMyDialog::ThreadFunc(LPVOID lpParam)
{
ASSERT(lpParam);
_thread_info *pInfo = (_thread_info *)lpParam;
HWND pNotifyWnd = pInfo->pNotifyWnd;
long lValue1 = pInfo->lValue1;
int nValue2 = pInfo->nValue2;
char szValue3[80];
strcpy(szValue3, pInfo->szValue3);
delete pInfo;
.
.
::PostMessage(pNotifyWnd, WM_THREAD_WORKING, 0, 0);
.
.
}
LRESULT CMyDialog::OnThreadMessge(WPARAM wParam, LPARAM lParam)
{
.
.
return 0;
} That's how it's done...
Hope this helps.
Jonathan Craig
www.mcw-tech.com
|
|
|
|
|
Thanks Jonathan, really appreciate the help!
I had a bit of a lightbulb moment yesterday where it all clicked and started making a bit of sense!
I found out that by casting (LPVOID)this and passing it to my thread, I could access the m_hWnd member of the object that started the thread and post the message like that.
I knew that was probably not the best or safest way to do it either, and my custom event message isnt as neat as yours so that's a great help!
Thanks again!
-Mark
|
|
|
|
|
Just one Quick Question about PostMessage though . . .
What's the difference between WPARAM and the LPARAM?? I see they are both 32bit message parameters, but cant see any significant difference? Are there any restrictions on the use of either of them?
Thanks
-Mark
|
|
|
|
|
In Win32 there is no difference. In Win16 (Windows 3.1) the WPARAM was 16 bit and LPARAM was 32 bit.
There are no restrictions on using them. The general rule is that WPARAM is use more for passing numbers and LPARAM is for passing address values.
As above in Win32 both are the same size and can be use interchangeably. But in Win64? Will WPARAM be 32 bit and LPARAM be 64 bit? Something to keep in mind...
Jonathan Craig
www.mcw-tech.com
|
|
|
|
|
What is the best way to represent Currency/Money values (aside from COleCurrency) with higher precision? This is particulary usefull in countries like Indonesia where US$1 = appx. 8000-9000 Rupia where higher precision is needed.
In VB, it has the Currency data type. In Visual C++, how do our expert guys represent it?
Thanks...
|
|
|
|
|
According to MSDN:
The long double data type (80-bit, 10-byte precision) is mapped directly to double (64-bit, 8- byte precision) in Windows NT and Windows 95.
The VB currency is a 8 byte 64 bit I think...so this is even better.
"An expert is someone who has made all the mistakes in his or her field" - Niels Bohr
|
|
|
|
|
Currency is a 64 bit signed integer scaled by 10,000.
So, 1 dollar would be 10000. 50 USA cents (half a dollar) would be 5000.
Tim Smith
I know what you're thinking punk, you're thinking did he spell check this document? Well, to tell you the truth I kinda forgot myself in all this excitement. But being this here's CodeProject, the most powerful forums in the world and would blow your head clean off, you've got to ask yourself one question, Do I feel lucky? Well do ya punk?
|
|
|
|
|
Does anyone know what the fix is for building MFC ActiveX components on VC++ 6.0 on WinME? I have tried to build a basic component on Win98/NT and ME and cannot get it to work on ME - It works fine on NT & 98. It does not get past the registration point sometimes or it also hangs on coping some licence file which I assume is the activeX .LIC ?. I have the lastest SP for VC++ 6.0.
Mike
|
|
|
|
|
I have two multithreaded applications the runs perfectly in NT, 2000 and XP, but when I port it to 98 they die. The first has no user interface, and for some reason ends up in a deadlock, where half the threads are stuck in the PumpMessage loop, and the other half are sleeping. None of the threads (except the main thread) have any windows. The secondary threads that are stuck pumping come from CSockets. As I said, this has worked fine on NT based kernels.
The second program throws a page fault in Kernel32 or quits out of all the threads with a result code of -1. Unfortunately, the VC++ debugger won't trace to the line (or area) that is causing the problem.
Does anyone know what the difference is that would cause such problems?
Thanks.
|
|
|
|
|
Ten bucks you are using an API not supported on 9x.
Tim Smith
I know what you're thinking punk, you're thinking did he spell check this document? Well, to tell you the truth I kinda forgot myself in all this excitement. But being this here's CodeProject, the most powerful forums in the world and would blow your head clean off, you've got to ask yourself one question, Do I feel lucky? Well do ya punk?
|
|
|
|
|
Depends can't find anything. Do you have a suggestion on how to determine this?
|
|
|
|
|
MattW wrote:
The secondary threads that are stuck pumping come from CSockets
Bad idea, in my opinion.
CSocket[not thread safe] misbehaves in mutlti-threaded apps.
Nish
It's seven o'clock
On the dot
I'm in my drop top
Cruisin' the streets - Oh yeah
I got a real pretty, pretty little thing that's waiting for me
|
|
|
|
|
If you implement CSocket correctly you can safely use it in multithreaded apps. This is actually the easiest way to set up a server that can respond to multiple clients at the same time. I have had no problems with this in NT.
|
|
|
|
|
I don't know about the first program, but in the second, you're not passing NULL as the last parameter of CreateThread() are you? This is supported on NT etc. (after all, why do you need the thread id, when you get the thread handle) but causes a kernel error in 95/98/Me.
------------------------
Derek Waters
derek@lj-oz.com
|
|
|
|
|
I'm using AfxBeginThread which doesn't have this parameter:
m_pThread = AfxBeginThread(SequenceThread,this,THREAD_PRIORITY_HIGHEST,0,CREATE_SUSPENDED);
It is weird though that 98 generates negative thread numbers (as seen in the debugger)...
|
|
|
|
|
Which command can convert .obj to .lib?
This is simple but i forgot it.
Many Thanks
|
|
|
|
|
I didn't even know of a conversion utility that did this...?
I would specify Win32 static library in the New dialog.
This tells vc++ that the output of compilation will be a lib file rather than executable.
Cheers!
"An expert is someone who has made all the mistakes in his or her field" - Niels Bohr
|
|
|
|
|
Thanks for you reply.
My situation is that the .obj file is not produced by myself. it's a
commercial package which i must include into my project.
This package, actually, is not big at all, it's only 28KB. however,
I only user no more than 3 function in the package, Therefore, convert
this .obj to .lib do reduce the size of my application.
Cheers.
|
|
|
|
|
You cannot convert just about any obj file to lib!!!
Nish
It's seven o'clock
On the dot
I'm in my drop top
Cruisin' the streets - Oh yeah
I got a real pretty, pretty little thing that's waiting for me
|
|
|
|