Link to home
Start Free TrialLog in
Avatar of Chizl
ChizlFlag for United States of America

asked on

NT Service

I have an NT Service I wrote in VB5.  As you know a NT Service can not have a GUI so without putting my application to sleep, which nothing can access it, or running a loop that takes up 100% of the CPU all the time, how can I get it to idle?

I have to, because it's a service, create a new thread to run my own stuff.  Creating this thread, but then it's destroyed as soon as the function starts up.  I could of used that hProcess from CreateThread function, but I have to destroy it so that the NT Service portion of it will run.

Would this be what I use:
Declare Function WaitForInputIdle Lib "user32" _
    Alias "WaitForInputIdle" (ByVal hProcess As Long, _
    ByVal dwMilliseconds As Long) As Long

If so how do I get the hProcess of my application?
Avatar of Chizl
Chizl
Flag of United States of America image

ASKER

Edited text of question
Avatar of Chizl

ASKER

Edited text of question
Avatar of muffinthedog
muffinthedog

Chizl,
The WaitForInputIdle function only works with GUI applications.

Exactly what is your service doing?
raise the stakes, i'll give u complete solution.
i know exactly what u are talking about, i'm currently
implementing a lot of Vb Services.

-Anil


Avatar of Chizl

ASKER

I have a lot of VB services too, but they only run by themself.  This one needs to be ready for someone to send info to it at all times.  It is a DCOM Service.
ControlService, maybe?
you can use a pump message loop:
while(GetMessage(...))
{
  ...
}
Even if the services are not GUI applications, they can receive messages and can have a message pump. So, your app sleeps while it didn't have any message.
Having written several services that run in a timer loop, i have monitored the cpu usage and mine never went above the 1% mark and that was only every once in a while. Try the timer control on an invisible form. If you would explain what exactly you are trying to do maybe I can help.
Avatar of Chizl

ASKER

A timer loop??

You can't use a Form that has a timer on it, there is no GUI.
You can't do a while loop, because that does run the CPU up to 100%.
You can't use Sleep API, because it will not respond when you try to access it.

what are you using for a timer?  
Avatar of Chizl

ASKER

You can't use an invisible form.  It will freeze up anytime you try to access the form for anything while in a Service.
Avatar of Chizl

ASKER

I have gotten the timer to run after you shut the service down.  It will run about 20 times before the application shuts down, but not until after shut down of the service.
I have written a service that sits in memory and scans the registry for appropriate times to shell out and run an app residing on the server. The app then starts services on the workstations and those services do the work and send info back to the server by writing the updated info to the servers registry. The server service then knows that it is done and continues it timer loop. I use a form with nothing on it except the timer control and set the visible property to false. It never locked up. You can implement callbacks to the service but it is a little more difficult and prone to problems, which is why I used the registry to store the info. I can point you to a web site that has an article on NT services and example code. Seems the callback stuff was in the pure code method of implementing services in vb.
I didn't understand very well how the things goes on in your application. Anyway, WaitForInputIdle function works only for threads that have a message pump. If you can be more explicit, I'll give you a solution.
chizl as per your comment: I have a lot of VB services too, but they only run by themself.  This one needs to be ready for someone to send info to it at all times.  It is a DCOM Service.

a) if this is all u need, u don't have to go any further than
using MTS, create a server package and drop u'r component in that and keep that running while Idle, so any application can access it.

 


b) Option two, i downloaded NTSVC from microsoft site, but i could not use as it is, so i have done a lot of changes, like this control does not update the SCM of the status when the
service takes more than 30 seconds to startup, things like that,
it has worked pretty well for me, NTSVC.ocx, invisible form & a timer. these services are in production & definately not CPU crunchers.

chizl regarding this comment:
I have gotten the timer to run after you shut the service down.  It will run about 20 times before the application shuts down, but not until after shut down of the service.

if this is happening, i would say u are not receiving the notifications correctly from SCM, u would have to give me details
as to how u have implemented the service.




You an check out http://www.flash.net/~ljjohnsn then go to his articles. He helped tremendously and is available for questions pretaining to his article.
JCstrider, well the code examples are a good starting point,
but when it comes to writing production quality services, it falls flat on lot of areas, of all i have seen NTSVC.ocx comes close, if u are ready to go in and tweak certain thing in the
C++ code.
refer to this site http://www.activeplus.com/

they have built controls and programs using the basic infrastructure provided by microsoft, i'm not using it
but seems like it can do the job, there is a free download
available, check it out.

Avatar of Chizl

ASKER

I would like to stay as far as I can from using controls and stick with API.  I want this application to be as generic as possible.  One question to those that say use an invisible form and a timer with in it.  If I could get a invisible form to be activated, why would I need to use a timer to keep it up.  If the form is loaded would it not stay up by itself?
Avatar of Chizl

ASKER

Ok that was a stupid question.   Form is invisible and so it would not stay up..  What are you going to be calling in the timer loop to make sure the forms stays alive?
Avatar of Chizl

ASKER

Wait nothing like that will work either...  Are you sure you have written a service before?

1.  When you write a service you have to create a new thread to run while the service is running.  Reason for NT Service will take over the one thread at the function, "StartServiceCtrlDispatcher".  When you create this new thread, useing "CreateThread", you then pass in a function name.  This function name can not be inside a form or it will not get to the function of, "StartServiceCtrlDispatcher".  If you pass it to another Module then inside the Module you have to reinitialize COM using, "CoInitialize" because COM does not exist in the new thread which VB take care of on the first thread.  Now you can call the form to be invisible, but after that you have to destroy COM using "CoUninitialize" and then the form is destroyed automaticly.  How are you keeping it in a loop while the form is loaded?  If you keep it in a loop to load the form then I don't need to form, because the Loop is what I'm looking form..  I put an quick example of this on the net, unfinished I would like to see how you or anyone can make the form invisible at all times while still running as a service.

http://www.karland.com/servicetest/
Avatar of Chizl

ASKER

Keep in mind not to put it to sleep or take up 100% CPU..
Avatar of Chizl

ASKER

I'm keeping it open until I see a solution.
Chiz...
    why not just look at the simpleservice sample included in the SDK documentation, change the servicestart loop to whatever, remove all the debug stuff, and compile it.
Avatar of Chizl

ASKER

I have everything that is in MS demos of service and more.   The problem is the demos they provide are not setup to receive information from other programs, but to run by themself.  I have services and know how to create one.  I want mine to be an ActiveX EXE or DLL service that can be used by other applications.
Simpleservice uses a pipe for communication!!!
Chizl... found the following in MS documentation...
wonder what it does!

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993-1996  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   client.c
//
//  PURPOSE:  This program is a command line oriented
//            demonstration of the Simple service sample.
//
//  FUNCTIONS:
//    main(int argc, char **argv);
//
//  COMMENTS:
//
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

VOID _CRTAPI1 main(int argc, char *argv[])
{
    char    inbuf[80];
    char    outbuf[80];
    DWORD   bytesRead;
    BOOL    ret;
    LPSTR   lpszPipeName = "\\\\.\\pipe\\simple";
    LPSTR   lpszString = "World";
    int     ndx;

    // allow user to define pipe name
    for ( ndx = 1; ndx < argc-1; ndx++ )
    {
        if ( (*argv[ndx] == '-') || (*argv[ndx] == '/') )
        {
            if ( stricmp( "pipe", argv[ndx]+1 ) == 0 )
            {
                lpszPipeName = argv[++ndx];
            }
            else if ( stricmp( "string", argv[ndx]+1 ) == 0 )
            {
                lpszString = argv[++ndx];
            }
            else
            {
                printf("usage: client [-pipe <pipename>] [-string <string>]\n");
                exit(1);
            }
        }
        else
        {
            printf("usage: client [-pipe <pipename>] [-string <string>]\n");
            exit(1);
        }

    }

    strcpy( outbuf, lpszString );

    ret = CallNamedPipeA(lpszPipeName,
                         outbuf, sizeof(outbuf),
                         inbuf, sizeof(inbuf),
                         &bytesRead, NMPWAIT_WAIT_FOREVER);

    if (!ret) {
        printf("client: CallNamedPipe failed, GetLastError = %d\n", GetLastError());
        exit(1);
    }

    printf("client: received: %s\n", inbuf);
}







   // open our named pipe...
    //
    hPipe = CreateNamedPipe(
                    lpszPipeName         ,  // name of pipe
                    FILE_FLAG_OVERLAPPED |
                    PIPE_ACCESS_DUPLEX,     // pipe open mode
                    PIPE_TYPE_MESSAGE |
                    PIPE_READMODE_MESSAGE |
                    PIPE_WAIT,              // pipe IO type
                    1,                      // number of instances
                    0,                      // size of outbuf (0 == allocate as necessary)
                    0,                      // size of inbuf
                    1000,                   // default time-out value
                    &sa);                   // security attributes

    if (hPipe == INVALID_HANDLE_VALUE) {
        AddToMessageLog(TEXT("Unable to create named pipe"));
        goto cleanup;
    }


    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING,       // service state
        NO_ERROR,              // exit code
        0))                    // wait hint
        goto cleanup;

    //
    // End of initialization
    //
    ////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////
    //
    // Service is now running, perform work until shutdown
    //

    while ( 1 )
    {
        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // wait for a connection...
        //
        ConnectNamedPipe(hPipe, &os);

        if ( GetLastError() == ERROR_IO_PENDING )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }

        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // grab whatever's coming through the pipe...
        //
        bRet = ReadFile(
                    hPipe,          // file to read from
                    szIn,           // address of input buffer
                    sizeof(szIn),   // number of bytes to read
                    &cbRead,        // number of bytes read
                    &os);           // overlapped stuff, not needed

        if ( !bRet && ( GetLastError() == ERROR_IO_PENDING ) )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }

        // munge the string
        //
        _stprintf(szOut, TEXT("Hello! [%s]"), szIn);

        // init the overlapped structure
        //
        memset( &os, 0, sizeof(OVERLAPPED) );
        os.hEvent = hEvents[1];
        ResetEvent( hEvents[1] );

        // send it back out...
        //
        bRet = WriteFile(
                    hPipe,          // file to write to
                    szOut,          // address of output buffer
                    sizeof(szOut),  // number of bytes to write
                    &cbWritten,     // number of bytes written
                    &os);           // overlapped stuff, not needed

        if ( !bRet && ( GetLastError() == ERROR_IO_PENDING ) )
        {
            dwWait = WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE );
            if ( dwWait != WAIT_OBJECT_0+1 )     // not overlapped i/o event - error occurred,
                break;                           // or server stop signaled
        }







You can use the Spleep function for put your application idle.

Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Then your application will just take some processor time, not all.
Avatar of Chizl

ASKER

Mathies,
  I guess you didn't read all of it, which I understand there is alot here.  I can't put it to sleep, because that is exacly what happens, the application is put to sleep then other applications can not send anything to it, until it wakes up.
ASKER CERTIFIED SOLUTION
Avatar of cmihalache
cmihalache

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Chizl

ASKER

StartServiceCtrlDispatcher is put in the ServiceMain subroutine.  That takes the thread up front.  The only way I have found to get around that is to do a CreateThread before StartServiceCtrlDispatcher is called.  Keeping the Function called from CreateThread alive is my problem, because with no interface I can't hold it open without a Sleep or a Loop that takes up all my processor.  I don't want to put this application to sleep at anytime, just Idle.  Is there no way I can do this?

If the...
Do While True
    DoEvents
Loop

. didn't take up all the processor it would be perfect.

Also if the Sleep API didn't basicly FREEZE the program for the time you specify it would also be perfect.
Avatar of Chizl

ASKER

I found something on NT Services and RPC..
Under "Chapter 13- Remote Procedure Calls (RPC) and NT Services" at http://www.ece.sc.edu/class/eece890N/shared/projects/mleonard.htm

So it can be done...  It just doesn't go into detail on how.
Chizl,
   no you're getting tired of me by now, but can't just WaitForMultipleObjects within the loop of the thread to suspend the thread until a message to process or terminate is posted?

Chizl, please note that I haven't read all the comments, so if my question seems like just totally ignorant, or something, please forgive me, and ignore this.

Avatar of Chizl

ASKER

What is "MySemaphore", a function?
It is the name of the semaphore. It can be used to open the semaphore from other programs (like the name of a file).