views:

179

answers:

4

im trying to send a message between 2 separate projects, but my problem is that im trying to make the receiver run inside a TThread Object, but WndProc wont work from inside an Object, must be a function, is there anyway to create a window inside a TThread that can process messages inside the thread?

here is what i mean

function TDataThread.WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
 Result := 0;
 case uMsg of
   WM_DATA_AVA: MessageBox(0, 'Data Avaibale', 'Test', 0);
  else Result := DefWindowProc(hwnd, uMsg, wParam, lParam);
 end;
end;

Procedure TDataThread.Create(const Title:String);
begin
 HAppInstance := HInstance;
 with WndClass do
 begin
  Style := 0;
  lpfnWndProc := @WindowProc;          //The Error Lies here (Variable Required)
  cbClsExtra := 0;
  cbWndExtra := 0;
  hInstance := HAppInstance;
  hIcon := 0;
  hCursor := LoadCursor(0, IDC_ARROW);
  hbrBackground := COLOR_WINDOW;
  lpszMenuName := nil;
  lpszClassName := 'TDataForm';
 end;
 Windows.RegisterClass(WndClass);
 MainForm := CreateWindow('TDataForm', PAnsiChar(Title), WS_DLGFRAME , XPos, YPos, 698, 517, 0, 0, hInstance, nil);
end;

i need to have a form so i can get its handle from another application Using FindWindow and FindWindowEx if needed

A: 

I dont think you can do what you are trying to do. The user interface runs from the main thread. Why are you doing this?

GrandmasterB
+6  A: 

Running a wndproc in a background thread can be done in Win32, but it's widely regarded as a bad idea.

To do it, you must ensure that your background thread contains a message dispatch loop: GetMessage/TranslateMessage/DispatchMessage. You must ensure that the window handle you want to process messages in the background thread is created on the background thread (CreateWindow is called in the context of the background thread) and all its child windows as well. And you must ensure that your background thread calls its message loop frequently in addition to whatever else it's doing (which kinda defeats the purpose of using a background thread!)

If your background thread doesn't have a message loop, the window handles that are created on the background thread will never receive any messages, so nothing will happen.

Now then, why you shouldn't do this: Windows are message-driven, which means they are inherently a cooperatively multitasked dispatch system. Every GUI windows app has to have a message loop in the main thread to get anything done. That message loop will support virtually any number of windows, all on the main thread. A properly implemented UI will not do anything in the main thread to block execution, so the message loop will always be ready and responsive.

So if the existing message loop on the main thread will handle all your window messaging needs without blocking or freezing, why would you want to make your life more complicated by trying to run a second message loop in a background thread? There is no advantage to using a background thread.

dthorpe
As suggestion, let the main thread get the message and signal your working thread when there's new data available to process.
jachguate
All threads in Windows are equal, there's nothing "main" or "background" about them. They differ in whether they have a message loop or not, and there is one that was created first in a process, but that's about it as far as differences go. Interaction with COM may require a thread to have a message loop, working with windows in a thread requires it to have a message loop. A message loop is also a fine way to communicate with a thread. Apart from the VCL being a bad match for it there's nothing wrong with multiple message loops in a process.
mghie
so there is no way i can make my thread communicate with other processes?since each process must send back a reply to its thread to show that its ready to map a file for data !
killercode
Sure, there are plenty of ways to do it. The question is how much work do you want to put into it. The window handle doesn't have to live in the context of your background thread. You could use a normal window handle in your UI thread and when it receives a particular message from the other process, the message handler can signal an event that your background thread is waiting on.
dthorpe
Since you mention the other process sending back a reply to indicate data is ready, you could also consider using a named mutex per process/thread pair. The thread starts the process, passing the name of the mutex as a param, then the thread blocks waiting for the mutex to signal. The process gets the named mutex and signals it when the work is done. No message loops required. (This assumes you have control of the source code for the process as well as the thread)
dthorpe
therer will be more than one thread for more than one instance of the senderthere is no way that if i used the main app's thread that it would know which thread this message is sent to
killercode
@mghie: Yes, all threads in Windows are equal. All programmers, however, are not. If there is a solution that will get the job done without extensive use of threads, use it. If there is a solution that can get the job done without using thread-bound window handles, background threads, and COM in the same sentence, even better. People are drawn to threads like moths to flame, with similar results.
dthorpe
user: What message number is the process sending to the thread to indicate the data is ready?
dthorpe
i dont think the message number is that important :Slike WM_USER + 60 or something like that
killercode
and i already have the solution without using threads, but i wanna know how to do it, i may need that in another project somehow
killercode
The message number is relevant. If each process sent a different message to signal its respective thread, you could differentiate the messages on the receiving end. The same is true if the process included some distinctive data value in the dword message params.
dthorpe
@user: "already have the solution without threads / want to know how to do it". See the last part of my reply to @mghie. :>
dthorpe
@user: If you want to use threads, use threads. You have all the info you need in the 2nd paragraph of my answer.
dthorpe
@dthorpe: "People are drawn to threads like moths to flame, with similar results" - There seem to be two kinds of thinking, either to consider threads the moment there is any code that may block or take longer than a few 100 ms, the other to eschew multiple threads as long as possible, and then some more. The latter brought us programs (certain IDEs come to mind) that hang for half a minute when one simply wants to enter text or open the help window, that's why I'm for the former approach.
mghie
@mghie: There are plenty of appropriate uses of threads, no argument there. I just find that folks jump into threads prematurely and create a bigger mess of things. A properly designed multithreaded solution to an appropriate problem is a beautiful thing, and equally rare.
dthorpe
+3  A: 

You don't need a Window to receive messages, try the following. In the thread (once) make a call to PeekMessage to force the creation of a Message Queue, example:

  // Force Message Queue Creation
  PeekMessage(Msg, 0, WM_USER, WM_USER, PM_NOREMOVE);

Then setup a Message Loop/Pump, example:

  // Run until terminated
  while not Terminated do
  begin

    if GetMessage(@Msg, 0, 0, 0) then
    begin
      case Msg.message of
        WM_DATA_AV: MessageBox(0, 'Data Avaibale', 'Test', 0); 
      else begin
        TranslateMessage(@Msg);
        DispatchMessage(@Msg);
      end;
    end;
  end;
Remko
yea, but how am i gonna know the handle of this thread to send messages to?since the sender is from another process
killercode
Use PostThreadMessage (http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx), it takes the ThreadId instead of a Window Handle.
Remko
But then you have the problem of the sending app needing to locate the receiving thread's ID. Using a window makes that search easier.
Remy Lebeau - TeamB
+2  A: 

Creating a window inside a TThread works fine, provided the TThread implements a message loop, AND CreateWindow() is called inside the same thread context as the message loop. In other words, you must call CreateWindow() from inside the TThread's Execute() method, NOT from inside its constructor, eg:

type
  TDataThread = class(TThread)
  private
    FTitle: String;
    FWnd: HWND;
    FWndClass: WNDCLASS;
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
  public
    constructor Create(const Title:String); reintroduce;
  end;

constructor TDataThread.Create(const Title:String); 
begin 
  inherited Create(False);
  FTitle := Title;
  with FWndClass do 
  begin 
    Style := 0; 
    lpfnWndProc := @DefWindowProc;
    cbClsExtra := 0; 
    cbWndExtra := 0; 
    hInstance := HInstance; 
    hIcon := 0; 
    hCursor := LoadCursor(0, IDC_ARROW); 
    hbrBackground := COLOR_WINDOW; 
    lpszMenuName := nil; 
    lpszClassName := 'TDataForm'; 
  end; 
end; 

procedure TDataThread.Execute; 
var
  Msg: TMsg;
begin
  if Windows.RegisterClass(FWndClass) = 0 then Exit;
  FWnd := CreateWindow(FWndClass.lpszClassName, PChar(FTitle), WS_DLGFRAME, XPos, YPos, 698, 517, 0, 0, HInstance, nil); 
  if FWnd = 0 then Exit;
  while GetMessage(Msg, FWnd, 0, 0) > 0 do
  begin
    if Msg.message = WM_DATA_AVA then begin
      MessageBox(0, 'Data Available', 'Test', 0);
    end else
    begin
      TranslateMessage(msg);
      DispatchMessage(msg)
    end;
  end;
end;

procedure TDataThread.DoTerminate;
begin
  if FWnd <> 0 then DestroyWindow(FWnd);
  Windows.UnregisterClass(FWndClass);
  inherited;
end;
Remy Lebeau - TeamB
+1, this is the important technical information from dthorpe's answer, which was a little buried in caveats. There's no need to have a member `FWndClass` though, put everything into `Execute()`, get rid of `DoTerminate()`, and things will be clearer. If both class name and window title were parameters to the constructor this would make for a nice helper base class.
mghie
I prefer to use DoTerminate() because it allows the thread to cleanup after itself regardless of whether Execute() exits cleanly or due to an uncaught exception. Putting a try/except around the entire Execute() code is a bit ugly for me.
Remy Lebeau - TeamB
Artificially putting setup, use of and destruction of a data structure into different methods is much worse. Your code for example will happily call `UnregisterClass()` even if `RegisterClass()` has failed.
mghie
Which is why I would normally add a check to make sure it was registered before unregistering it. This was merely an example.
Remy Lebeau - TeamB