• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 285
  • Last Modified:

Handling (system) messages without forms

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
DelphiOnly
Asked:
DelphiOnly
  • 7
  • 5
  • 2
  • +3
1 Solution
 
ahalyaCommented:
Take a look at application.processmessage function.

i guess you can have a similar routine in the DPR file.
0
 
ZifNabCommented:
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
 
ZifNabCommented:
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
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

 
DelphiOnlyAuthor Commented:
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
 
rwilson032697Commented:
Hmm.. A windows program, in only 2-5 Kb? Sounds optimistic to me! (Especially with Delphi)
0
 
Edo082297Commented:
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
 
philipleighsCommented:
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
 
DelphiOnlyAuthor Commented:
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
 
DelphiOnlyAuthor Commented:
Phil:

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

Thx.
0
 
philipleighsCommented:
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
 
DelphiOnlyAuthor Commented:
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
 
philipleighsCommented:
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
 
DelphiOnlyAuthor Commented:
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
 
philipleighsCommented:
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
 
DelphiOnlyAuthor Commented:
Phil:

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


0
 
DelphiOnlyAuthor Commented:
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
 
philipleighsCommented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 7
  • 5
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now