Click to See Complete Forum and Search --> : attempting IPC
dave2k
November 24th, 2005, 09:57 AM
i am attempting to read messages from another app sent to my app using WM_COPYDATA. The tricky part is that the other app needs to return immediately, and my app will take a good few seconds to process nearly each bit of text.
my calling app basically goes through each line of text in a file, makes a copy data structure, and sends it to my procedure below:
At the moment, if i put cnsl() in WM_COPYDATA case, it prints out the contents of the text fiel fine, but more importantly, when i put the cnsl() in the WM_CUSTOM case (which i need to do), i get 级@级@级@级@级@级@级@级@级@级@级@级@级@级@ Why does this happen?
Cheers :)
Marc G
November 24th, 2005, 10:17 AM
You assign a pointer to text which will probably point to garbage when you are processing WM_CUSTOM.
You need to copy the data in WM_COPYDATA to your text variable. Depending on the type of your text variable, you can use new [] to allocate the memory and then use memcpy to copy the data from lpMsg->lpData to your newly allocated buffer, then post your WM_CUSTOM message and use delete [] in your WM_CUSTOM handler to free the memory.
dave2k
November 24th, 2005, 10:34 AM
i used case WM_COPYDATA:
{
COPYDATASTRUCT *lpMsg =(COPYDATASTRUCT*)lParam;
memcpy(text, (char*)lpMsg->lpData, 100);
PostMessage(hwnd, WM_CUSTOM, wParam, lParam);
return 0;
}
case WM_CUSTOM:
{
Sleep(2000);
cnsl(text);
return 0;
}
and it works i.e. prints the text, but get this, the Sleep pauses output in my procedure to represent long processing, and even though i am using PostMessage which is supposed tor eturn immediately, the program sending the messages still waits for my procedure to process the message before sending another one.
why could this be????
Marc G
November 24th, 2005, 10:40 AM
You should not do such long processing in your main procedure. While you are sleeping in your WM_CUSTOM, no other messages can be processed.
A better way would be to use threads and forward the WM_COPYDATA data to your thread. This thread could hold a queue of WM_COPYDATA messages and process them one after the other.
dave2k
November 24th, 2005, 11:40 AM
Ok, cheers for the reply. I redid my program with a thread (scuse the code, newbie):
bool running = true;
HANDLE Event = 0;
char NewLine[MAX_PATH];
int x = 0;
It seems to be working, but the progam with the thread only displays the last line. I am guessing this is because the variable which holds the string gets over written until the last line of the file.
So how can i overcome this? I am guessing one answer is to use some sort of list, like a deque or something, but is there an easier answer?
dave2k
November 24th, 2005, 11:56 AM
so you said something about the htread holding a queue - do i have to declare the queue globally? Else how would i populate it from the WinProc?
thanks.
dave2k
November 24th, 2005, 12:57 PM
hi guys, i attempted to use a queue to read all the lines in a my thread. First here is the relevant code in my WinProc: case WM_CREATE:
startConsoleWin();
Event = CreateEvent(NULL, 0, 0, NULL);
running = true;
CreateThread(NULL, 0, ThreadProc, (PVOID)&x, 0, NULL);
return 0;
as you can see, every message get's put into the queue. So why does the thread only seem to print out every other line?
cheers
Marc G
November 24th, 2005, 01:02 PM
You can use std::queue or std::deque to implement a queue. However, I wouldn't use the char datatype but use std::string instead. So, you can declare your queue as follows:
std::queue< std::string > myqueue;
You can use push to push new items on the queue and front/pop to get and pop the first item from the queue.
BUT you need synchronize access to your queue to prevent multiple threads from accessing the same queue at the same time. For this you can use a critical section: lock the critical section before each push, pop, front and unlock it afterwards. Something like:
DWORD WINAPI ThreadProc(LPVOID lParam)
{
// ResetEvent(Event); <- shouldn't be required if you create your event as auto-reset.
}
return(0);
}
The same EnterCriticalSection(myCriticalSection); and LeaveCriticalSection(myCriticalSection); are to be used when you push a new string on the queue in your WM_COPYDATA handler.
dave2k
November 24th, 2005, 02:10 PM
but i only create one thread, so are you sure this is the cause?
cheers
dave2k
November 24th, 2005, 02:19 PM
ok i tried the code case WM_CREATE:
startConsoleWin();
Event = CreateEvent(NULL, 0, 0, NULL);
running = true;
CreateThread(NULL, 0, ThreadProc, (PVOID)&x, 0, NULL);
InitializeCriticalSection(&myCS);
return 0;
case WM_COPYDATA:
{
COPYDATASTRUCT *lpMsg =(COPYDATASTRUCT*)lParam;
EnterCriticalSection(&myCS);
Lines.push((char*)lpMsg->lpData);
LeaveCriticalSection(&myCS);
SetEvent(Event);
return 0;
} withbool running = true;
HANDLE Event = 0;
int x = 0;
queue<char*> Lines;
CRITICAL_SECTION myCS;
ok i sorted it - i had to initialize the crit section. However, still i am not receiving all my lines - some of them are being lost.
Can anyone help - this is gettign really frustrating :(
Marc G
November 24th, 2005, 02:58 PM
but i only create one thread, so are you sure this is the cause?
You have 2 threads: the one you created and the main thread running your window procedure.
I already said: do not put char* in a std::queue or any other STL container.
Use std::string to put in the container like in my example.
dave2k
November 24th, 2005, 03:16 PM
wow i think it works - forgive my ignorance - so why does string work but not char*???
cheers ;)
dave2k
November 24th, 2005, 03:53 PM
Ok, i think i have found a flaw (maybe not)..
Say i Sleep(5) every 10th time in my receving code. The point of this is that if it needs to pause, it would, but then would catch up with the sending code as much as possible. However, after Sleep(), the receiving code still prints out at the same rate as the sending script, meaning it will never catch up, and everytime there is a Sleep, the gulf between the two processes gets larger and larger.
Am i making sense? How could i get round this? Maybe some sort of loop which goes through the whole contents of my queue container everytime my thread executes?
I would greatly appreciate your input on this - thankyou :)
Marc G
November 25th, 2005, 02:57 AM
wow i think it works - forgive my ignorance - so why does string work but not char*???
cheers ;)
Because, char* represents a pointer to the actual string and it's never a good idea to store pointers in stl containers, because if you somehow deleted the memory pointed to by such a pointer, the pointer would point to garbage and might crash your app. Also, when you copy all items from one vector of pointers to another vector of pointers, only the pointers are copied and you now have 2 vectors with pointers to the same data and to copies of the data.
By using std::string inside stl containers you solve all these issues. Memory management is all handled for you.
Marc G
November 25th, 2005, 03:04 AM
Ok, i think i have found a flaw (maybe not)..
Say i Sleep(5) every 10th time in my receving code. The point of this is that if it needs to pause, it would, but then would catch up with the sending code as much as possible. However, after Sleep(), the receiving code still prints out at the same rate as the sending script, meaning it will never catch up, and everytime there is a Sleep, the gulf between the two processes gets larger and larger.
Am i making sense? How could i get round this? Maybe some sort of loop which goes through the whole contents of my queue container everytime my thread executes?
I would greatly appreciate your input on this - thankyou :)
Yes, that's because in your thread loop you call "WaitForSingleObject(Event, INFINITE);" which will block the thread until the event is raised again, so you can never catch up on your messages. You can do something like:
DWORD WINAPI ThreadProc(LPVOID lParam)
{
while(running == true)
{
EnterCriticalSection(&myCS);
if (Lines.size() == 0)
{
// No items, wait until an item arrives
LeaveCriticalSection(&myCS);
WaitForSingleObject(Event, INFINITE);
}
else
LeaveCriticalSection(&myCS);
return(0);
}
NOTE: It might be a good idea to add a Sleep(0) or Sleep(1) after processing each line to give other threads a chance of running.
NOTE: There is a typo in your while loop:
while(running = true)should bewhile(running == true).
dave2k
November 25th, 2005, 04:16 AM
Cheers for all the help, finally works now
Greatly Appreciated :)
Marc G
November 25th, 2005, 04:26 AM
Cheers for all the help, finally works now
Greatly Appreciated :)
Glad I could help :)
dave2k
November 26th, 2005, 06:41 AM
If i created a thread using a seperate class altogether, do i have to created two critical sections? i.e. one for my class and one for my window procedure?
or should i do something like: case WM_CREATE:
startConsoleWin();
mC* = MyClass();
mC->Event = CreateEvent(NULL, 0, 0, NULL);
mC->running = true;
CreateThread(NULL, 0, ThreadProc, (PVOID)&x, 0, NULL);
mC->InitializeCriticalSection(&myCS);
return 0;
thanks
Marc G
November 26th, 2005, 12:37 PM
You should use only 1 critical section. You use 1 critical section to protect 1 piece of code, for example your queue and then use this critical section from wherever your access the queue.
NOTE: if you do create a separate class for your thread and your queue, then personally I would make the queue private and add a member function AddItemToQueue to your class. This function will add the item to your queue and will protect the queue with the critical section. This is more safe as users of that class are not bother with the critical section issues.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.