Solved

Handling (system) messages without forms

Posted on 1998-10-16
17
240 Views
Last Modified: 2010-04-06
i want to be able to respond to "system messages" (say a WM_ENDSESSION) from within my program.

i know how to do it by using messagehooks: Application.HookMainWindow(MsgHook). but if my application doesn't have any forms -and if i don't want any VCL stuff - then how do i go about doing this ?

i'd appreciate a small example DPR file :-)
0
Comment
Question by:DelphiOnly
  • 7
  • 5
  • 2
  • +3
17 Comments
 
LVL 7

Expert Comment

by:ahalya
ID: 1343166
Take a look at application.processmessage function.

i guess you can have a similar routine in the DPR file.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1343167
Hi DelphiOnly,

Then you can do it with making an invisible window, I once wrote something for a component of mine. You can look at the source :

http://www.geocities.com/SiliconValley/Garage/3862/cdevents.zip

If you need a specific example, let me know...

Regards, Zif.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1343168
here is already something :

you can use this in an application

FWindowHandle: HWND;
procedure WndProc(var Msg: TMessage);

constructor TCDEvents.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FWindowHandle := AllocateHWnd(WndProc); <---- !
end;

destructor TCDEvents.Destroy;
begin
  DeallocateHWnd(FWindowHandle); <----- !
  inherited Destroy;
end;

procedure TCDEvents.WndProc(var Msg: TMessage);
begin
     if (Msg.Msg = WM_DEVICECHANGE) then begin
      if FEnabled and (fNotifyMode = nmEvent) then
       try
         WMDeviceChange(TWMDeviceChange(Msg));
       except
         Application.HandleException(Self);
       end
     end
    else
      Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

Regards, Zif

0
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:DelphiOnly
ID: 1343169
ZifNab,

Thx for your comments. But i want to avoid using the overhead assosciated with VCL. (thus i would like NOT to use the Forms unit).

But AllocateHWnd comes from the Forms unit.  (and when i try your example it gives me an exe file of 165 kB ! - i'm thinking somewhere in the range of 2-5 kB)

Is there a way not to use the Forms unit ? Also can you give any advice on what should be in the main program body ?


0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1343170
Hmm.. A windows program, in only 2-5 Kb? Sounds optimistic to me! (Especially with Delphi)
0
 
LVL 1

Expert Comment

by:Edo082297
ID: 1343171
Hi DelphiOnly
  Even though Forms is referenced, the linker (a part of the compiler) only uses the code that is absolutely necessary -- even within units themselves. When you compile your application, and say for example that you use only one small function from a large unit, ONLY that function is added to the executable. Delphi's compiler is in fact one of the most highly optimized for this; even most C++ compilers link in a whole library file if it included in the header, regardless of how many routines are called.
   RWilson is right when he (she?) says that you are being optimistic to have an application that will deal with message hooks and other Windows paraphernalia in 2 - 4K, as Windows requires the extra baggage - not Delphi.

Regards,
Edo

0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1343172
Hi DelphiOnly,

It is possible to have a windows program about 5k in size.

Avoid the VCL by writing you app ironically the "C" way.

ie. Use RegisterClass and have the lpfnWndProc member of WNDCLASS point to a call back function which takes hWnd, Msg, wParam and lParam.
This is where you respond to system messages like WM_QUIT and WM_ENDSESSION.

Note that you don't have to create a window.

You'll need to write your own message loop using GetMessage, TranslateMessage and DespatchMessage.

You should find code in C which does this (I can paste some if you can't find any) and translate it to Delphi. You may find your DelphiOnly life will be easier. Ironic eh?

Cheers,
Phil.

I've just had an afterthought. If you have the Delphi sources, you'll be able to find where RegisterClass, TranslateMessage etc are used. Note that every windows message based program will use these functions. You may be able to use DelphOnly after all!

You may still find the C code easier because there are only about 20 lines of code (two functions), mostly windows function calls. Delphi probably has a powerful but convoluted message processing loop.
0
 

Author Comment

by:DelphiOnly
ID: 1343173
ok, i was too optimistic with my 2-5 k comment; but i have windows programs (that don't use any forms) withim 15-20 kB.

if my programs is going to be 165 kB, then i can use a form to handle the messages rather than trying to go without forms.

Phil:

this is what i have been doing. i was thinking that Dispatch Message would send the messages to the WndProc of ZifNab's invisible window.

repeat
  if PeekMessage(M, X.WindowHandle, 0, 0, PM_REMOVE) then
  begin
  if (M.Message = WM_QUIT) or (M.Message = WM_Close) then Halt
  else
    begin
    TranslateMessage(M);
    DispatchMessage(M);
    end
  end;
until X.Terminated;

and btw, DephiOnly doesn't exclude winapi, i guess :-)
0
 

Author Comment

by:DelphiOnly
ID: 1343174
Phil:

i would love to have a look at the C code, if you wouldn't mind posting it.

Thx.
0
 
LVL 3

Accepted Solution

by:
philipleighs earned 130 total points
ID: 1343175
Here is some C code for you.
It is an app which prevents the user closing windows down.
It responds to WM_QUERYENDSESSION and WM_ENDSESSION.
You have to kill it from task manager.
Incidentally, it compiles to 21k using DevStudio 5.0 (with no debug info).
I discovered that you need to create a window otherwise the program doesn't get WM_ messages. The code creates a 0 by 0 window for this purpose.

Cheers,
Phil.


//Creates an invisible app (no window appears, no taskbar icon, no alt+tab)
//that responds to WM_ENDSESSION and WM_QUERYENDSESSION messages

//After running the app, it must be closed using Task Manager (NT) or Ctrl+Alt+Del (95)

#include <windows.h>




long FAR PASCAL WindowProc( HWND hWnd, UINT message,  
                            WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
    case WM_QUERYENDSESSION:
      MessageBox(GetDesktopWindow(), "QueryEndSession", "Title", 0);
      //Return FALSE to prevent windows shutdown
      //Return TRUE to allow shutdown
      return FALSE;

    case WM_ENDSESSION:
      MessageBox(GetDesktopWindow(), "EndSession", "Title", 0);
      return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}




static BOOL doInit( HINSTANCE hInstance, int nCmdShow )
{
    HWND hwnd;
    WNDCLASS wc;
 
    /*
     * set up and register window class
     */
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "ClassName";
    if (0 == RegisterClass( &wc ))
    {
      return FALSE;
    }
     
    /*
     * create an invisible window
     */
    hwnd = CreateWindowEx(
        WS_EX_TOOLWINDOW, //Prevents taskbar icon, and does not appear in Alt+Tab
        "ClassName",      //must be the same as wc.lpszClassName
        "",
        WS_POPUP,
        0,
        0,
        0,
        0,
        NULL,
        NULL,
        hInstance,
        NULL );
 
    if( !hwnd )
    {
        return FALSE;
    }
 
    ShowWindow( hwnd, nCmdShow );
    UpdateWindow( hwnd );
 
    return TRUE;
}
 



int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
 
    lpCmdLine = lpCmdLine;
    hPrevInstance = hPrevInstance;
 
    if( !doInit( hInstance, nCmdShow ) )
    {
       return FALSE;
    }
 
    while( 1 )
    {
      if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
      {
          if( !GetMessage( &msg, NULL, 0, 0 ) )
              return msg.wParam;
          TranslateMessage(&msg);  
          DispatchMessage(&msg);
      }
      else
      {
          WaitMessage();
      }
    }
}
0
 

Author Comment

by:DelphiOnly
ID: 1343176
Phil:

i have trouble in "translating" an equivalent code. i cannot find a declaration of "WindowProc" that works with
wc.lpfnWndProc := WProc(Window, Msg, WParam, LParam)

i declared WProc as:
function WProc(Window: HWND; Msg, WParam, LParam: Longint): Longint;far;stdcall;

(have tried several other variants; including using the @ , but Delphi doesn't like any of my constructs).

Can you show me how to declare the WndProc function in Delphi and then assisgn it to lpfnWndProc. (just the headers should be ok)

Thx.
0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1343177
Hi,

Here is some code.

function WndProc(h: HWND; m: Integer; w, l: LongInt): LRESULT;
  begin
    {more code here}
  end;


var wc: TWndClass;
    n: Atom;
begin
  wc.style := 0;
  wc.lpfnWndProc := @WndProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := HInstance;
  wc.hIcon := 0;
  wc.hCursor := 0;
  wc.hbrBackground := GetStockObject(COLOR_BTNFACE);
  wc.lpszMenuName := nil;
  wc.lpszClassName := 'ClassName';
  n := RegisterClass(wc);


You should assign wc.lpfnWndProc the address of WndProc like this:
  wc.lpfnWndProc := @WndProc;
ie, you don't need the parameters.

Cheers,
Phil.


0
 

Author Comment

by:DelphiOnly
ID: 1343178
Thx and sorry for bugging you with another question.  CreateWindowEx is not creating a window (and returning a zero). Can you show what is wrong in the following translation of your initialization code ?


function InitOk:boolean;

Const
     WinHPos   = 100;
      WinVPos   = 100;
      WinWidth  = 100;
      WinHeight = 100;
      ExStyle   = WS_EX_APPWINDOW;
      Style     = WS_POPUP;

var   wc : TWNDCLASS;
      a : Atom;

begin;
//setup and register window class
wc.style := cs_HRedraw or CS_VRedraw;
wc.lpfnWndProc := @WProc;
wc.cbClsExtra  := 0;
wc.cbWndExtra  := 0;
wc.hInstance   := hInstance;
wc.hIcon       := 0;
wc.hCursor     := LoadCursor(0, IDC_Arrow);
wc.hbrBackground := GetStockObject(Black_Brush);
wc.lpszMenuName  := nil;
wc.lpszClassName := 'TClass';
a := RegisterClass(WC);
if a <> 0 then
   begin;
   //Now Create a window to receive messages.
   WHandle := CreateWindowEx(ExStyle, 'TClass', 'Window Caption', Style,
                             WinHPos, WinVPos, WinWidth, WinHeight,
                             0, 0, hInstance, nil);
   if WHandle <> 0 then                            //<================WHandle is zero here !!!
      begin;
      ShowWindow(WHandle, sw_Show);
      UpdateWindow(WHandle);
      Result := true;
      end
   else Result := false;
   end
else Result := false;
Terminated := false;
end;


0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1343179
Hi,

The problem is not with the code you pasted, instead it is WProc.

It should be declared as:
function WProc(wnd: HWND; m: Word; w: WParam; l: LParam): LRESULT; stdcall;
  begin
    Result := DefWindowProc(wnd, m, w, l);
  end;

Important things to note:
WProc must have stdcall; at the end of the declaration, sorry I omitted this before.
It should not return 0 (well not normally at least). It should return DefWindowProc.
Also, the types of the parameters have changed in the declaration of WProc from my last entry.

So the reason that WHandle was getting 0 is because WProc was returning 0? (I'm guessing here)
As soon as CreateWindowEx is called, a call is made to WProc where m = WM_CREATE. If you return 0, then no window is created.

Cheers,
Phil.

0
 

Author Comment

by:DelphiOnly
ID: 1343180
Phil:

i called DefWndProc but didn't have stdcall. will try it out and let you know. Thanks a lot for your help.


0
 

Author Comment

by:DelphiOnly
ID: 1343181
Ok. i got it working. Thx again.

btw, is there a reason you're calling PeekMessage with a PM_NORemove and then a GetMessage ?

Why not use PeekMessage with a PM_Remove ?
0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1343182
Hi,

re PM_NOREMOVE: It is just the way I've seen Microsoft do it in sample apps. Feel free to change it if you want.

Phil

0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Microsoft Active Directory, the widely used IT infrastructure, is known for its high risk of credential theft. The best way to test your Active Directory’s vulnerabilities to pass-the-ticket, pass-the-hash, privilege escalation, and malware attacks …

856 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question