Solved

Handling (system) messages without forms

Posted on 1998-10-16
17
230 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
 

Author Comment

by:DelphiOnly
Comment Utility
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
Comment Utility
Hmm.. A windows program, in only 2-5 Kb? Sounds optimistic to me! (Especially with Delphi)
0
 
LVL 1

Expert Comment

by:Edo082297
Comment Utility
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
Comment Utility
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
Comment Utility
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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 

Author Comment

by:DelphiOnly
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

762 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now