Solved

NT Service

Posted on 1998-12-16
34
388 Views
Last Modified: 2012-05-04
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?
0
Comment
Question by:Chizl
  • 16
  • 6
  • 4
  • +3
34 Comments
 
LVL 4

Author Comment

by:Chizl
ID: 1450678
Edited text of question
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450679
Edited text of question
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450680
Chizl,
The WaitForInputIdle function only works with GUI applications.

Exactly what is your service doing?
0
 
LVL 1

Expert Comment

by:koundinyaanil
ID: 1450681
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


0
 
LVL 4

Author Comment

by:Chizl
ID: 1450682
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.
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450683
ControlService, maybe?
0
 

Expert Comment

by:cmihalache
ID: 1450684
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.
0
 

Expert Comment

by:jcstrider
ID: 1450685
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.
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450686
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?  
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450687
You can't use an invisible form.  It will freeze up anytime you try to access the form for anything while in a Service.
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450688
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.
0
 

Expert Comment

by:jcstrider
ID: 1450689
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.
0
 

Expert Comment

by:cmihalache
ID: 1450690
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.
0
 
LVL 1

Expert Comment

by:koundinyaanil
ID: 1450691
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.




0
 

Expert Comment

by:jcstrider
ID: 1450692
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.
0
 
LVL 1

Expert Comment

by:koundinyaanil
ID: 1450693
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.
0
 
LVL 1

Expert Comment

by:koundinyaanil
ID: 1450694
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.

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 4

Author Comment

by:Chizl
ID: 1450695
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?
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450696
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?
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450697
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/
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450698
Keep in mind not to put it to sleep or take up 100% CPU..
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450699
I'm keeping it open until I see a solution.
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450700
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.
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450701
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.
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450702
Simpleservice uses a pipe for communication!!!
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450703
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
        }







0
 
LVL 1

Expert Comment

by:mathies
ID: 1450704
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.
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450705
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.
0
 

Accepted Solution

by:
cmihalache earned 300 total points
ID: 1450706
Hi Chizl,
I don't know if you can control your service through an ActiveX interface. Services are designed to interact with other applications through ControlService API function.
You can create your service with SERVICE_USER_DEFINED_CONTROL access. Then, in the main function, you can create a semaphore:

.
hSemaphore = CreateSemaphore(lpSemaphoreAttributes, 0, 1, "MySemaphore")
ServiceTableEntry.lpServiceName = SERVICE_NAME
ServiceTableEntry.lpServiceProc = FncPtr(AddressOf ServiceMain)
b = StartServiceCtrlDispatcher(ServiceTableEntry)
.

Then, in the ServiceMain subroutine:
.
      ServiceStatus.dwCurrentState = SERVICE_RUNNING
      b = SetServiceStatus(hServiceStatus, ServiceStatus)


      '** Perform tasks -- if none exit
      While True
          WaitForSingleObject(hSemaphore, INFINITE)
          ' do your task here ...
      WEnd

      ''** If an error occurs the following should be used for shutting
      ''** down:
      ''   SetServerStatus SERVICE_STOP_PENDING
      ''   Clean up
      ''   SetServerStatus SERVICE_STOPPED
End Sub

Then, in the Handler subroutine:
.
         Case SERVICE_CONTROL_STOP
            ServiceStatus.dwWin32ExitCode = 0
            ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING
            ServiceStatus.dwCheckPoint = 0
            ServiceStatus.dwWaitHint = 0
            b = SetServiceStatus(hServiceStatus, ServiceStatus)

            '** Do whatever it takes to stop here.
            CloseHandle(hSemaphore)

            ServiceStatus.dwCurrentState = SERVICE_STOPPED
         Case SERVICE_CONTROL_MYTASK
            ReleaseSemaphore(hSemaphore, 1, NULL)
.

The application that wants to request that your service to do a task should open your service and call:
ControlService(hService, SERVICE_CONTROL_MYTASK, lpServiceStatus)
Your service must be started before this call.

I didn't tested this code but it should work and it can be improved. The ideea is that the thread that execute your ServiceMain function goes to sleep in the WaitForSingleObject call. Then, when an app calls ControlService, the Handler function increments the semaphore calling ReleaseSemaphore and deblock the service's thread. The WaitForSingleObject function decrements the semaphore, the service makes its task and goes to sleep in the next loop.

0
 
LVL 4

Author Comment

by:Chizl
ID: 1450707
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.
0
 
LVL 4

Author Comment

by:Chizl
ID: 1450708
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.
0
 
LVL 1

Expert Comment

by:muffinthedog
ID: 1450709
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.

0
 
LVL 4

Author Comment

by:Chizl
ID: 1450710
What is "MySemaphore", a function?
0
 

Expert Comment

by:cmihalache
ID: 1450711
It is the name of the semaphore. It can be used to open the semaphore from other programs (like the name of a file).
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

707 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

18 Experts available now in Live!

Get 1:1 Help Now