bluedragon99
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...
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...
One good example you can find here: http://www.mentalis.org/apilist/CreateProcess.shtml
ASKER
wow thats some aweful syntax but I like that it creates a new process on it's own...working on it now.
ASKER
no luck yet still workin...
ASKER
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
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
Let us know how it goes.
James
Delete the service
and then While installing the service use the service type as
SERVICE_INTERACTIVE_PROCES S | SERVICE_WIN32_OWN_PROCESS
and also when calling RegisterServiceCtrlHandler and filling the service type use the same above specified service type.
Whenever u launch the application it will be visible.
and then While installing the service use the service type as
SERVICE_INTERACTIVE_PROCES
and also when calling RegisterServiceCtrlHandler
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
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.
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.
...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
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:\\win dows\\note pad.exe");
strcpy(WinStaAndDesktop,"W inSta0\\De fault");
StartInfo.cb=sizeof(StartI nfo);
StartInfo.dwFlags=STARTF_U SESHOWWIND OW;
StartInfo.wShowWindow=SW_S HOWMAXIMIZ ED;
StartInfo.lpDesktop=WinSta AndDesktop ; <<<<<--------------------- ---
if (0==CreateProcess(NULL,Com mandStr,NU LL,NULL,FA LSE,0,NULL ,NULL,&Sta rtInfo,&Pr ocInfo))
{
oopsie...
}
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
strcpy(CommandStr,"c:\\win
strcpy(WinStaAndDesktop,"W
StartInfo.cb=sizeof(StartI
StartInfo.dwFlags=STARTF_U
StartInfo.wShowWindow=SW_S
StartInfo.lpDesktop=WinSta
if (0==CreateProcess(NULL,Com
{
oopsie...
}
ASKER
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
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
ASKER
It's the actual ACT of logging in that makes it unresponsive too. If I restart the services after logging in it runs forever.
ASKER
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)
ASKER
Service form load event:
Private Sub Form_Load()
AString = "teststring"
NTService.StartService
Dim strDisplayName As String
On Error GoTo CreateNewLog
Open OptionsFrm.LoggingPath.Tex t 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.Tex t 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.Tex t For Append As #61
Print #61, "Service Installed Successfully - " & Time & " " & Date
Close #61
On Error GoTo 0
Call CreateStartupKey
End If
Private Sub Form_Load()
AString = "teststring"
NTService.StartService
Dim strDisplayName As String
On Error GoTo CreateNewLog
Open OptionsFrm.LoggingPath.Tex
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.Tex
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.Tex
Print #61, "Service Installed Successfully - " & Time & " " & Date
Close #61
On Error GoTo 0
Call CreateStartupKey
End If
ASKER
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.
ASKER
Form1.Winsock1.RemoteHost = "" ??
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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_LE NGTH 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_L ENGTH + 3)) As Byte
dwAddressLength As Long
sIPAddress(0 To (MAX_ADAPTER_ADDRESS_LENGT H - 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(.IpAddres sList.IpAd dress.IpAd dr, 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
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_LE
Private Const MAX_ADAPTER_ADDRESS_LENGTH
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_L
dwAddressLength As Long
sIPAddress(0 To (MAX_ADAPTER_ADDRESS_LENGT
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(.IpAddres
'// 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?
Any feedback?
Also, you may use CreateProcess windows API call: http://www.cpcug.org/user/clemenzi/technical/Languages/Execute_Shell.htm