Link to home
Start Free TrialLog in
Avatar of bluedragon99
bluedragon99Flag for United States of America

asked on

Shelling an Exe from non-interactive service? VB6

Hello,

Trying to call another app from my non interactive system service (program =a service frontend =a normal exe).  I would like the service to call the front end upon startup but shell is not going to cut it, it appears the shell runs it under the system user account (same as the service is running under).  I don't want to have to feed it any usernames or passwords.  is there any way to make it run visibly, being called from the service??

Don't really want to use reg run or start menu startup if I can help it...
Avatar of RanjeetRain
RanjeetRain

Use a timer event. Launch your interface app a few seconds/minutes later the service started. By that time you will have the shell.

Also, you may use CreateProcess windows API call: http://www.cpcug.org/user/clemenzi/technical/Languages/Execute_Shell.htm
Avatar of bluedragon99

ASKER

wow thats some aweful syntax but I like that it creates a new process on it's own...working on it now.
no luck yet still workin...
it is only opening dos style exe's.
A little more information here would be nice, as in what is the goal of this EXE that you want to spawn.  The issues is on an NT/2k/XP machine if you create a process from the system account it will run on system's desktop.  MOre often than not you will have to "impersonate the logged on user."  Another option would be to have a helper application running (via the startup folder or the reg run key) so that it runs in the current users context.  Then you could use either a named event to signal this app or a global message (broadcast message) to signal your helper to spawn your service requested application.

Jake
If you want to be able to launch windows forms from a service you need to check the "allow interaction with desktop" in it's properties, this can be found by right clicking on the service in the services control panel. This allows launched windows to be visible, otherwise, as you stated, you will be limited to dos command prompt style applications.
Just a note, the "allow service to interact with desktop" is in the log on tab in the service properties.

Let us know how it goes.

James
Delete the service
and then While installing the service use the service type as
SERVICE_INTERACTIVE_PROCESS | SERVICE_WIN32_OWN_PROCESS
and also when calling RegisterServiceCtrlHandlerand filling the service type use the same above specified service type.
Whenever u launch the application it will be visible.
I'm still concerned that everyone is happily suggesting that it's "Ok" to run his interactive application from the Local System account.  Local System has more privliges than Administrator, so if this application is intended to be sold it will be a MAJOR security hole.  It isn't at all hard to inject your own code into another interactive process (Easily done with settimer and any edit control...).  Just look at all the processes running on your machine that do have a tray icon, 100% of them should be running in your user context.  As an alternative, .Net offers some nice classes to start a process using the currently logged on users credentials.

Jake
Another note, if it's run from the System account it will NOT have access to any network drives.
CreateProcess() uses the calling processes security context.  Use CreateProcessAs() to use a different one.

CreateProcess() can run windowed programs as well console programs, but you do have to fiddle with STARTUPINFO.

If the calling process is a non-interactive service, you can specify the target station and desktop in the new process' STARTUPINFO.

Correct, but in order to change the security context you would have to have one (LPSECURITY_ATTRIBUTES) of the currently logged in user.  Even then, I feel it would be lot more work to do it this way (if it's even possible to acquire the user's token w/ out knowing it's name/pass)instead of using a helper application as the author stated he didn't want to have to know the user/pass.
>>currently logged in user
...if there is one.  But, given that bludragon99 mentioned the spawned app was to be visible, I suppose it's safe to presume the service doesn't start the process until a user has logged on.

Actually, I got the impression that his using LocalSystem was acceptable as long as the app would be visible, but then, being and old fart with coke bottle bottom bifocals, I could very well have missed something.

As am I taking the assumption that it will be running on NT/2k/XP/2k3...  Now trying to run an application in interactive mode without a currently logged on user is more difficult than one may think, as there are numerous hoops one has to jump through in order to be able to do it.  Also, as he's stating it's VB6 I don't think he would be able to create a window without an existing desktop.  I do remember trying to create a similar service for an in house application that would just have to inform the user if an error occured, and if no user was logged on to pop it up on the "Default" desktop.  I can't remember the specifics but I don't think VB is capable of creating a window when there is no user logged on....

  I guess we should just wait for some author feedback.  Again, a little more information on what this "spawned" application is accomplishing would be nice.

Jake
>>> in interactive mode without a currently logged on user is more difficult than one may think
Actually, more difficult than that.  Or not, depending on your outlook.  

An interactive app would seem to imply a user is present.  
An interactive service just means it's associated with WinSta0\Default.

Here's a snippet to show how to start a windowed program from a non-interactive service - presuming someone has logged on to see it.  The only real difference difference is that in this case, StartInfo.lpDesktop gets the name of the windows station and desktop to be associated with the started process.  If not specified, the started process is associated with the windows station and desktop of the starting process which, in the case of a non-interactive service are not visible.

(Yes, Blue, it does look vaguely like C, doesn't it)

STARTUPINFO          StartInfo;
PROCESS_INFORMATION  ProcInfo;
char                 WinSta0Default[512];
char                 CommandStr[512];

memset(&StartInfo,0,sizeof(StartInfo));
strcpy(CommandStr,"c:\\windows\\notepad.exe");
strcpy(WinStaAndDesktop,"WinSta0\\Default");

StartInfo.cb=sizeof(StartInfo);
StartInfo.dwFlags=STARTF_USESHOWWINDOW;
StartInfo.wShowWindow=SW_SHOWMAXIMIZED;
StartInfo.lpDesktop=WinStaAndDesktop;                     <<<<<------------------------
if (0==CreateProcess(NULL,CommandStr,NULL,NULL,FALSE,0,NULL,NULL,&StartInfo,&ProcInfo))
   {
   oopsie...
   }
sorry it took me so long to get back..

Here's what I've done

Service = non-interactive
Front end = not a service, interactive, loads from "run" in registry
console = talks to service over network ports

front end and service talk over local ports


This seems to work but now when the machine is logged in the service stops responding to console commands for some reason.  before login the service communicates with the console fine.  upon login service IS still running but won't respond to any console commands at all.  very odd
It's the actual ACT of logging in that makes it unresponsive too.  If I restart the services after logging in it runs forever.
Actually just found out the service is still functioning and doing it's job just no comm. with console after login...very weird why would a service be affected by user login?? (even removed the front end from the reg, problem persists)
Service form load event:

Private Sub Form_Load()
AString = "teststring"
NTService.StartService

Dim strDisplayName As String

On Error GoTo CreateNewLog
Open OptionsFrm.LoggingPath.Text For Append As #31
    Print #31, "Starting - " & Time & " " & Date
Close #31
On Error GoTo 0






strDisplayName = "Service"

NTService.ServiceName = strDisplayName
NTService.DisplayName = strDisplayName
NTService.Interactive = False
NTService.StartMode = svcStartAutomatic

On Error GoTo CreateNewLog
Open OptionsFrm.LoggingPath.Text For Append As #83
    If NTService.Interactive = True Then
        Print #83, "Running In Interactive Mode - " & Time & " " & Date
    Else
        Print #83, "Non-Interactive Mode - " & Time & " " & Date
    End If

    If NTService.StartMode = svcStartAutomatic Then
        Print #83, "Starting Automatically - " & Time & " " & Date
    Else
        Print #83, "Starting Manually Only - " & Time & " " & Date
    End If
Close #83
On Error GoTo 0


IsServiceInstalled
If ServiceInstalled = False Then
    NTService.Install
    On Error GoTo CreateNewLog
    Open OptionsFrm.LoggingPath.Text For Append As #61
        Print #61, "Service Installed Successfully - " & Time & " " & Date
    Close #61
    On Error GoTo 0
    Call CreateStartupKey
End If
Ah! Found it.  When the service talks to the "localhost" I had hard coded remotehost = "LocalHost".  Well this was killing communication from console to service and vice versa because it was talking to localhost not the console.  How do I get around this??? is there a way to negate the remotehost = "localhost" and make it talk to who spoke to it?  I don't want to have to hardcode the console machine's ip into the program.
Form1.Winsock1.RemoteHost = "" ??
ASKER CERTIFIED SOLUTION
Avatar of jacobhoover
jacobhoover

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
That "should" resolve to the true IP, if not one can use a more complex method to get the machine's IP address.  

Ie, in a module...: (Works on Windows 98, Windows 2000, Windows XP)

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2004 VBnet, Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce
'               or publish this code on any web site,
'               online service, or distribute as source
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'// SLIGHT MODIFICATION DONE IN ORDER TO ACCOMIDATE FIRST ADAPTER
'// BEING DISCONECTED
Private Const MAX_ADAPTER_NAME_LENGTH As Long = 256
Private Const MAX_ADAPTER_DESCRIPTION_LENGTH As Long = 128
Private Const MAX_ADAPTER_ADDRESS_LENGTH As Long = 8
Private Const ERROR_SUCCESS As Long = 0

Private Type IP_ADDRESS_STRING
   IpAddr(0 To 15) As Byte
End Type

Private Type IP_MASK_STRING
   IpMask(0 To 15) As Byte
End Type

Private Type IP_ADDR_STRING
   dwNext As Long
   IpAddress As IP_ADDRESS_STRING
   IpMask As IP_MASK_STRING
   dwContext As Long
End Type

Private Type IP_ADAPTER_INFO
   dwNext As Long
   ComboIndex As Long  'reserved
   sAdapterName(0 To (MAX_ADAPTER_NAME_LENGTH + 3)) As Byte
   sDescription(0 To (MAX_ADAPTER_DESCRIPTION_LENGTH + 3)) As Byte
   dwAddressLength As Long
   sIPAddress(0 To (MAX_ADAPTER_ADDRESS_LENGTH - 1)) As Byte
   dwIndex As Long
   uType As Long
   uDhcpEnabled As Long
   CurrentIpAddress As Long
   IpAddressList As IP_ADDR_STRING
   GatewayList As IP_ADDR_STRING
   DhcpServer As IP_ADDR_STRING
   bHaveWins As Long
   PrimaryWinsServer As IP_ADDR_STRING
   SecondaryWinsServer As IP_ADDR_STRING
   LeaseObtained As Long
   LeaseExpires As Long
End Type

Private Declare Function GetAdaptersInfo Lib "iphlpapi.dll" _
  (pTcpTable As Any, _
   pdwSize As Long) As Long
   
Private Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (dst As Any, _
   src As Any, _
   ByVal bcount As Long)

Public Function LocalIPAddress() As String
   
  'api vars
   Dim cbRequired  As Long
   Dim buff()      As Byte
   Dim Adapter     As IP_ADAPTER_INFO
   Dim AdapterStr  As IP_ADDR_STRING
   
  'working vars
   Dim ptr1        As Long
   Dim sIPAddr     As String
   Dim found       As Boolean
   
   Call GetAdaptersInfo(ByVal 0&, cbRequired)

   If cbRequired > 0 Then
   
      ReDim buff(0 To cbRequired - 1) As Byte
     
      If GetAdaptersInfo(buff(0), cbRequired) = ERROR_SUCCESS Then
     
        'get a pointer to the data stored in buff()
         ptr1 = VarPtr(buff(0))

         Do While (ptr1 <> 0) 'And (found = False)
         
           'copy the data from the pointer to the
           'first adapter into the IP_ADAPTER_INFO type
            CopyMemory Adapter, ByVal ptr1, LenB(Adapter)
         
            With Adapter
                       
              'the DHCP IP address is in the
              'IpAddress.IpAddr member
                 
               sIPAddr = TrimNull(StrConv(.IpAddressList.IpAddress.IpAddr, vbUnicode))
                 
               '// MODIFIED HERE IN CASE THERE IS A DISCONNECTED ADAPTER
               '// ON MY MACHINE, XP Pro, THE FIRST ADAPTER IS DHCP CONFIGURED
               '// AND IT'S DISCONECTED, RESULTING IN AN IP OF 0.0.0.0
               If Len(sIPAddr) > 0 And (sIPAddr <> "0.0.0.0") Then
                  found = True
                  Exit Do
               End If

               ptr1 = .dwNext
                             
            End With  'With Adapter
           
        'ptr1 is 0 when (no more adapters)
         Loop  'Do While (ptr1 <> 0)

      End If  'If GetAdaptersInfo
   End If  'If cbRequired > 0

  'return any string found
   LocalIPAddress = sIPAddr
   
   
End Function


Private Function TrimNull(item As String)

    Dim pos As Integer
   
   'double check that there is a chr$(0) in the string
    pos = InStr(item, Chr$(0))
    If pos Then
       TrimNull = Left$(item, pos - 1)
    Else
       TrimNull = item
    End If
 
End Function

@bluedragon99
  Any feedback?