Solved

How to send a message to an NT service?

Posted on 1997-05-13
16
404 Views
Last Modified: 2013-12-03
How can I send a message to my program, which is running
as a service under NT?

Background:

My program runs as an NT service (managed by SRVANY.EXE), and it doesn't have a window -- but it does have a window procedure.

I'd like another of my programs to send a user message to the window procedure of my service program.  But since it doesn't have a window, it doesn't have a window handle -- so I don't know how to send to it.  

NT knows about the window procedure, because it calls it sometimes.

I'm able to obtain the process ID of my service program, but I don't know how to get from the process ID to the window procedure.


Any ideas?

Thanks in advance, Mark

0
Comment
Question by:elfield
  • 8
  • 5
  • 3
16 Comments
 
LVL 5

Expert Comment

by:y96andha
ID: 1397302
Have you looked at the PostThreadMessage function?
0
 
LVL 1

Accepted Solution

by:
ete earned 100 total points
ID: 1397303
Hello Mark
If your program has a window procedure, it does have a window also. The API level syntax of a window procedure is:

LONG WINAPI SomeWndProc(
HWND hwnd,
UINT uiMsg,
WPARAM wParam,
LPARAM lParam)

The first argument identifies the window uniquely in the system. Your window might be minimized or hidden, but it does exist, if you have a window procedure.

Which programming language you have used to create the program?

If you have used C language, you most propably know the classname of the window. In that case your other program can dynamically find the window using FindWindow() API call.

If you want to send messages from one process to another, you have to accept few restrictions.  As you know, all 32-bit processes run in their own virtual memory space. That is why you cannot send any dynamic data (pointers) in the LPARAM of SendMessage() API. Pointers are only valid in the calling process context. This limits the data only to numeric values and constants.

If you need to send dynamic data, you may use WM_COPYDATA message, which transports the data from one memory space to another. Unfortunally it has also some limitations. The lpData member of the COPYDATA structure cannot contain any pointers, it must be single block of data.

On the other hand, there are libraries available, which can pass any data between memory spaces. DirectIPC is one of them. It contains function DxIpcSendProcessMessage(). Look for dxipc32.exe in www.winsite.com at Windows NT programming utilities.

Once again, unfortunately there is no direct way of getting a window handle from process id. You are not out of hope though. It depends on the programing language what you are using.

If you are using C/C++, it can be done. Other languages, most likely not.

When you try to obtain the window handle from process id, there are also some limitations. You can make generic assumptions only to the main or active window of the process. If the process has multiple top level windows or if it is OLE enabled application, there are some restrictions.

Please tell more details of your programs, so I will try to give you a suitable solution, maybe even with source code samples.

Best regards
ETE
0
 

Author Comment

by:elfield
ID: 1397304
Dear ETE,

Thanks.  I'll try your FindWindow() suggestion (in C) and I'll
get back to you -- with luck, by tommorrow.  (I didn't realize
FindWindow would be able to see a service.)  
0
 

Author Comment

by:elfield
ID: 1397305
Dear ETE,

I tried your suggestion, but it didn't work when the process was
running as a service.  It does work when the process is started from
the command line.

Here's the startup code of program A (which may run as a service):

int     PASCAL
WinMain(ih, ph, cl, sh)
      HINSTANCE ih;                   /* instance handle */
        HINSTANCE ph;                   /* previous instance */
        LPSTR   cl;                     /* command line */
      int     sh;                     /* initial window display */
{
static  char    b[81];
      char    a[81];                  /* initial command line */
        char    *p;
      char    *q;
        WNDCLASS w;                     /* window class */

        strcpy(a, _argv[0]);            /* save program name */
      strlwr(a);                      /* to lower case */
        for (p = a; (q = strpbrk(p, ":/\\")) != NULL; p = q + 1)
            ;                       /* find start of command name */
        strcpy(b, p);
      p = strchr(b, '.');             /* exclude .exe from class name */
        if (p)
            *p = 0;
        w.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
      w.lpfnWndProc   = wpr;
        w.cbClsExtra    = 0;
      w.cbWndExtra    = 0;
        w.hInstance     = ih;
      w.hIcon         = LoadIcon(ih, "TICO_MI");
        if (w.hIcon == NULL) /* if not found, use Windows icon */
            w.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        w.hCursor       = LoadCursor(NULL, IDC_ARROW);
      w.hbrBackground = GetStockObject(WHITE_BRUSH);
        w.lpszMenuName  = NULL;
      w.lpszClassName = b;
      RegisterClass(&w);
      ...
}

Here's the logic of program B, which wants to send a WM_CLOSE
message to program A:

{
      char    *b;
        HWND    w;

      b = the lower-case program name (no path, no .exe)
      w = FindWindow(b, NULL);
      if (w)                  /* if found */
            PostMessage(w, WM_CLOSE, 0, 0); /* close the process*/
}

The FindWindow() call finds Program A when it's started from the
command line, but not when it's started as a service.

Any ideas?

0
 

Author Comment

by:elfield
ID: 1397306
(Here it is again, perhaps less garbled -- but some comments
are truncated or lack */)

Dear ETE,

I tried your suggestion, but it didn't work when the process was
running as a service.  It does work when the process is started from the command line.

Here's the startup code of program A (which may run as a service):

int     PASCAL
WinMain(ih, ph, cl, sh)
      HINSTANCE ih;                   /* instance handle */
        HINSTANCE ph;                   /* previous instance */
        LPSTR   cl;                     /* command line */
      int     sh;                     /* initial window display */
{
static  char    b[81];
      char    a[81];                  /* initial command line */
        char    *p;
      char    *q;
        WNDCLASS w;                     /* window class */

        strcpy(a, _argv[0]);            /* save program name */
      strlwr(a);                      /* to lower case */
        for (p = a; (q = strpbrk(p, ":/\\")) != NULL; p = q + 1)
            ;                       /* find start of name */
        strcpy(b, p);
      p = strchr(b, '.');             /* exclude .exe from name
        if (p)
            *p = 0;
        w.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
      w.lpfnWndProc   = wpr;
        w.cbClsExtra    = 0;
      w.cbWndExtra    = 0;
        w.hInstance     = ih;
      w.hIcon         = LoadIcon(ih, "TICO_MI");
        if (w.hIcon == NULL) /* if not found, use Windows icon */
            w.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        w.hCursor       = LoadCursor(NULL, IDC_ARROW);
      w.hbrBackground = GetStockObject(WHITE_BRUSH);
        w.lpszMenuName  = NULL;
      w.lpszClassName = b;
      RegisterClass(&w);
      ...
}

Here's the logic of program B, which wants to send a WM_CLOSE
message to program A:

{
      char    *b;
        HWND    w;

      b = the lower-case program name (no path, no .exe)
      w = FindWindow(b, NULL);
      if (w)                  /* if found */
            PostMessage(w, WM_CLOSE, 0, 0); /* close process
}

The FindWindow() call finds Program A when it's started from the
command line, but not when it's started as a service.

Any ideas?

0
 

Author Comment

by:elfield
ID: 1397307

(One more time ..., if this is hard to read, send me your e-mail
address and I'll e-mail it)

Dear ETE,

I tried your suggestion, but it didn't work when the process was
running as a service.  It does work when the process is started from the command line.

Here's the startup code of program A (which may run as a service):

int     PASCAL
WinMain(ih, ph, cl, sh)
        HINSTANCE ih;                   /* instance handle */
        HINSTANCE ph;                   /* previous instance */
        LPSTR   cl;                     /* command line */
        int     sh;                     /* initial window display */
{
static  char    b[81];
        char    a[81];                  /* initial command line */
        char    *p;
        char    *q;
        WNDCLASS w;                     /* window class */

        strcpy(a, _argv[0]);            /* save program name */
        strlwr(a);                      /* to lower case */
        for (p = a; (q = strpbrk(p, ":/\\")) != NULL; p = q + 1)
                ;                /*find start of command name */
        strcpy(b, p);
        p = strchr(b, '.');    /* exclude .exe from class name */
        if (p)
                *p = 0;
        w.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
        w.lpfnWndProc   = wpr;
        w.cbClsExtra    = 0;
        w.cbWndExtra    = 0;
        w.hInstance     = ih;
        w.hIcon         = LoadIcon(ih, "TICO_MI");
        if (w.hIcon == NULL) /* if not found, use Windows icon */
                w.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        w.hCursor       = LoadCursor(NULL, IDC_ARROW);
        w.hbrBackground = GetStockObject(WHITE_BRUSH);
        w.lpszMenuName  = NULL;
        w.lpszClassName = b;
        RegisterClass(&w);
        ...
}

Here's the logic of program B, which wants to send a WM_CLOSE
message to program A:

{
        char    *b;
        HWND    w;

        b = the lower-case program name (no path, no .exe)
        w = FindWindow(b, NULL);
        if (w)                  /* if found */
                PostMessage(w, WM_CLOSE, 0, 0);
}

The FindWindow() call finds Program A when it's started from the
command line, but not when it's started as a service.

Any ideas?

0
 
LVL 1

Expert Comment

by:ete
ID: 1397308
Okay, your code cleared the situation.

When your application is run from the command line, it has the window you specified in your code and it runs as an ordinary application. When you run it through SRVANY.EXE, it runs in an instance context of of SRVANY.EXE.

This means, that SRVANY.EXE is a generic service entry point, which interacts with Windows NT Service Control Manager. It loads your app with LoadLibrary() API, fills SERVICE_TABLE_ENTRY, calls StartServiceCtrlDispatcher() and RegisterServiceCtrlHandler() API's, etc, to create a generic service. Then it creates it's own hidden window and attaches your window procedure on it. That's why FindWindow() never finds the window you are looking for.

If you know the process id of your service process and the process has only one window, the window handle can be found in the following manner:

DWORD pidTemp = your_original_pid;
HWND   hwndSrv = NULL;

//call EnumWindows() with a pointer to the temp var
EnumWindows(
(WNDENUMPROC)YourEnumProc,
(LPARAM)&pidTemp);

if(pidTemp != your_original_pid)
   hwndSrv = (HWND)pidTemp;
.
.
.

BOOL CALLBACK YourEnumProc(
HWND hwnd,
LPARAM lParam)
{
   LPDWORD lpdwPid = (LPDWORD)lParam;
   DWORD dwThisPId = 0;
   
   GetWindowThreadProcessId(hwnd,
                                       (LPDWORD)&dwThisPId);

   //no match for process id , continue enumarating
   if(*lpdwPid != dwThisPId)
       return TRUE;                  

   //match found for the process id,
   //set window handle to the var!!!

   *lpdwPid = (DWORD)hwnd;          

   //because id found, terminate
   return FALSE;
}

Place your process id into a temporary variable. Call EnumWindows() API with ADDRESS of this variable. Your enumeration procedure loops through the top level windows in the system and gets the process id of them. It compares the given process id to the retrieved one. If match found, the window handle is returned in the id variable.

NOTE! This mechanism is not adquite for general puroses. It omits OLE enabled apps, multiple and child windows and window hierarchies in the app, but it should be suitable to your purposes.


Best regards
ETE
0
 

Author Comment

by:elfield
ID: 1397309
I already tried the "enumerate windows" method (using GetWindowThreadProcessID).   It gives window
handles for programs launched from the command line, but not for my service program (or for programs launched by my service program).  

If you're willing to give me your e-mail address, I can send you a zipped file that includes a test program (in C) that demonstrates this behavior.




launcwindows, but not for ser
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 1

Expert Comment

by:ete
ID: 1397310
That is a good idea!
I will test your code immediately after receiving it and return the modified source code, if a reasonable solution can be found. I will work on it during the weekend. So the results, good or bad, you will have at the latest on Monday.

My email address:
Esa.Tervo@teleste.fi
0
 
LVL 1

Expert Comment

by:ete
ID: 1397311
Thanks for the source code. Testing revieled, that the original purpose to find the window and send a msg will not work.

But anyway, not a big deal. We can always interact directly with the Windows NT Service Control Manager.

I wrote a library, DxSrv32.dll, which lets you control not only those services run with SRVANY.EXE, but all installed services in the computer. You can also install new ones and remove existing ones.

This library can be called from any application and it gives your programs total control on all the services on the machine.

Library contains following functions:

DxSrvInstallService()
DxSrvRemoveService()
DxSrvStartService()
DxSrvStopService()
DxSrvPauseService()
DxSrvContinueService()
DxSrvIsServiceRunning()

I will email you the comlete source code.

My best regards
ETE
0
 

Author Comment

by:elfield
ID: 1397312
Thanks for your response.

However, I don't want to interact with the service program
via DxSrv functions.  The service program launches many other
programs (the sample I sent you launched just one).  I need to
locate these launched programs and send messages to them.
0
 
LVL 5

Expert Comment

by:y96andha
ID: 1397313
Have you written all these programs yourself? Do you really have to use window messages, or could you use some other means of communication? I would suggest using a named pipe.
0
 

Author Comment

by:elfield
ID: 1397314
Thanks for your suggestion.  A named pipe might work if the window procedure (of the program launched by a service) gets a message that there's something in the pipe to read.  Do you know if it will?  (As is, the window procedure receives very few messages -- mainly timer events.)  

(Yes, they're all my programs.)
0
 
LVL 1

Expert Comment

by:ete
ID: 1397315
Hi Mark,
In your instructions you emailed me, there was a simple reason why you cannot interact with your apps with window messages.

You must let the service interact with the desktop (run on the SYSTEM account, check Control Panel, Services, Startup, Logon As..). If the service is not allowed to interact with desktop, there is no window handle available and no messaging, period.

If you allow interaction however, your current tx_xts.exe, ts_wlp.exe, etc are visible. They cannot be closed through Task Manager, but they can be closed through their system menu.

Most propably you would like to avoid this side effect. When your tx_xts.exe is run through SRVANY.EXE, just make your application windows hidden.

The other drawback are the logoff and shutdown events. All interactive windows will be closed by system on those events. However, you can omit closing by disabling WM_QUERYENDSESSION and WM_ENDSESSION messages.

This is the only easy solution to your problem. Only by allowing interactivity you get the handles and the messaging and the functionality you are looking for.

Best regards,
ETE


0
 
LVL 5

Expert Comment

by:y96andha
ID: 1397316
I do not think that you can have a message sent to you window procedure automatically.

Do you really need to get it through the window procedure? What kind of messages are you expecting? Since you don't have a window, unless you set it to run interactively as the system account, couldn't you use some other kind of loop to wait for whatever you have to wait for?
0
 

Author Comment

by:elfield
ID: 1397317
I tried the PostThreadMessage (suggested by y96andha) --
after having the service-launched process identify its
thread is by writing it to a file -- and that didn't work
either.  Interestingly PostThreadMessage returned "successful",
but the receiving process didn't get the message.

So, thanks to everybody for trying.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

In this article, I will show how to use the Ribbon IDs Tool Window to assign the built-in Office icons to a ribbon button.  This tool will help us to find the OfficeImageId that corresponds to our desired built-in Office icon. The tool is part of…
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

757 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

21 Experts available now in Live!

Get 1:1 Help Now