Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Calling StartService in VB6 with arguments?

Status
Not open for further replies.

Nagrom

Programmer
Jan 28, 2002
234
US
Does anyone out there know of a way to call the StartService API and actually pass it arguments in VB? I understand that it takes a "pointer to an array of pointers" to null terminated ANSI strings, but I can't get it to work. Amazingly enough I can't find a single answer out there of someone actually passing arguments to the call - they're always passing NULL for the arguments. I've tried the following with no success:

Creating an array of LONG variable types and populating it with the address of the string contents via StrPtr (this points to a null terminated Unicode string though and I'm using StartServiceA, but I thought I'd give it a try too), then passing it to the API with VarPtr so I'm passing it a pointer to that array.

Creating an array of LONG variable types and populating it with the address of Byte arrays for each argument via VarPtr. I'm creating the Byte arrays from the strings with the following function:

Private Sub unicodeToByteArray(ByRef arrBytes() As Byte, strStringToConvert As String)

' Declare local variable(s)
Dim lngStringLength As Long
Dim lngStringPosition As Long

' Get the length of the string in characters
lngStringLength = Len(strStringToConvert)

' ReDim the byte array with the string length so
' we have room for each character and a null char
' at the end of the array
ReDim arrBytes(lngStringLength) As Byte

' Populate each position in the array with the
' ASCII value of the appropriate letter
For lngStringPosition = 1 To lngStringLength
arrBytes(lngStringPosition - 1) = Asc(Mid(strStringToConvert, lngStringPosition, 1))
Next

' Add a null char to the end of the array
arrBytes(lngStringLength) = Asc(vbNullChar)

End Sub

I'm adding the address of each of these Byte arrays to the "pointer" array via VarPtr, and then passing a "pointer" to the "pointer" array to the API with VarPtr (since it is declared with ByVal in the API declaration), as well as ensuring I'm passing the strings as C++ compatible character arrays via the byte arrays. It seems like this should cover it, but it still doesn't work. Any ideas?
 
>a "pointer to an array of pointers" to null terminated ANSI strings

Don't break it down like that. Break it down like:

A pointer to an array of "pointers to null-terminated ANSI strings"

And then remember that (simplifying here, but it is enough for the purposes of this) a VB string is a pointer to an ANSI string (so you'd only need to add a CHR$(0) to the make it a pointer to a null-terminated ANSI string)

And then remember that if you pass the first element of an array by reference you are actually passing a pinter to that first element, and that subsequent elements all reside sequentially in memory...

So it should be something like:

StartService ServiceName, lNumberOfArguments, ByRef StringArray(0)

Only you can't put "ByRef" into the call itself, so you need to change the declaration of ServiceStartA. Personally I'd create an addtional version as:

Private Declare Function StartServiceWithParams Lib "advapi32.dll" Alias "StartServiceA" (ByVal hService As Long, ByVal dwNumServiceArgs As Long, lpServiceArgVectors As Long) As Long
 
Unfortunately that doesn't work - I should have clarified that I did try passing it an array of strings as well (that's the first thing I tried). I just mentioned the other two specifically because those are the non-standard things I tried. VB stores it's strings as unicode. When you are calling an API that expects a pointer to a string (as a LONG) you modify the call to pass a string "ByVal". When you call the function, VB actually does the conversion to an ANSI string from unicode behind the scenes to make the call. It apparently doesn't do that for you when you attempt to pass it a string array. As it turns out, the solution is the second thing I mentioned in my original question - converting the unicode to byte arrays, then placing the pointers to those arrays in an array of LONG and passing a pointer to that array to the function. The reason the call wasn't working for me was that I was trying to start a third party service and the documentation as to the required parameters was wrong. Basically I was passing the arguments successfully, but I was passing an argument that they had since deprecated (it used to be the first argument and now isn't even an argument) so all the subsequent arguments were "in the wrong place". I'm putting an example of the code below for anyone else who might need it. I've made it so it can handle up to 10 arguments. It's problematic making it truly dynamic as far as the number of arguments it can handle since you are putting pointers to the byte array variables in the array and therefore can't re-use the same byte array for the different arguments - you have to declare the byte arrays beforehand. Anyway, here's the code:

====================================

' Force declaration of all variables
Option Explicit

' Declare APIs
Private Declare Function OpenSCManager Lib "advapi32.dll" Alias "OpenSCManagerA" (ByVal lpMachineName As String, ByVal lpDatabaseName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function OpenService Lib "advapi32.dll" Alias "OpenServiceA" (ByVal lngSCManager As Long, ByVal lpServiceName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function StartService Lib "advapi32.dll" Alias "StartServiceA" (ByVal hService As Long, ByVal dwNumServiceArgs As Long, ByVal lpServiceArgVectors As Long) As Long
Private Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject As Long) As Long

' Define constants
' (some extras in here from what I'm using, but someone else might need
' them for other service stuff)

' Service Control
Private Const SERVICE_CONTROL_STOP = &H1
Private Const SERVICE_CONTROL_PAUSE = &H2
Private Const SERVICES_ACTIVE_DATABASE = "ServicesActive"

' Service State - for CurrentState
Private Const SERVICE_STOPPED = &H1
Private Const SERVICE_START_PENDING = &H2
Private Const SERVICE_STOP_PENDING = &H3
Private Const SERVICE_RUNNING = &H4
Private Const SERVICE_CONTINUE_PENDING = &H5
Private Const SERVICE_PAUSE_PENDING = &H6
Private Const SERVICE_PAUSED = &H7
Private Const SERVICE_ACTIVE = &H1
Private Const SERVICE_INACTIVE = &H2

'Service Control Manager object specific access types
Private Const STANDARD_RIGHTS_REQUIRED = &HF0000
Private Const SC_MANAGER_CONNECT = &H1
Private Const SC_MANAGER_CREATE_SERVICE = &H2
Private Const SC_MANAGER_ENUMERATE_SERVICE = &H4
Private Const SC_MANAGER_LOCK = &H8
Private Const SC_MANAGER_QUERY_LOCK_STATUS = &H10
Private Const SC_MANAGER_MODIFY_BOOT_CONFIG = &H20
Private Const SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE Or SC_MANAGER_ENUMERATE_SERVICE Or SC_MANAGER_LOCK Or SC_MANAGER_QUERY_LOCK_STATUS Or SC_MANAGER_MODIFY_BOOT_CONFIG)

'Service object specific access types
Private Const SERVICE_QUERY_CONFIG = &H1
Private Const SERVICE_CHANGE_CONFIG = &H2
Private Const SERVICE_QUERY_STATUS = &H4
Private Const SERVICE_ENUMERATE_DEPENDENTS = &H8
Private Const SERVICE_START = &H10
Private Const SERVICE_STOP = &H20
Private Const SERVICE_PAUSE_CONTINUE = &H40
Private Const SERVICE_INTERROGATE = &H80
Private Const SERVICE_USER_DEFINED_CONTROL = &H100
Private Const SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or SERVICE_QUERY_CONFIG Or SERVICE_CHANGE_CONFIG Or SERVICE_QUERY_STATUS Or SERVICE_ENUMERATE_DEPENDENTS Or SERVICE_START Or SERVICE_STOP Or SERVICE_PAUSE_CONTINUE Or SERVICE_INTERROGATE Or SERVICE_USER_DEFINED_CONTROL)
Private Const SERVICE_WIN32_OWN_PROCESS As Long = &H10
Private Const SERVICE_WIN32_SHARE_PROCESS As Long = &H20
Private Const SERVICE_WIN32 As Long = SERVICE_WIN32_OWN_PROCESS + SERVICE_WIN32_SHARE_PROCESS

Private Const SERVICE_DEMAND_START As Long = &H3
Private Const SERVICE_ERROR_NORMAL As Long = &H1

Private Const ERROR_MORE_DATA = 234

' Define UDTs
Private Type SERVICE_STATUS
dwServiceType As Long
dwCurrentState As Long
dwControlsAccepted As Long
dwWin32ExitCode As Long
dwServiceSpecificExitCode As Long
dwCheckPoint As Long
dwWaitHint As Long
End Type

Public Function startComputerService(strComputerName As String, strServiceName As String) As Long

' Declare local variable(s)
Dim udtServiceStatus As SERVICE_STATUS
Dim lngServiceManagerHandle As Long
Dim lngServiceHandle As Long
Dim lngReturnCode As Long
Dim lngAPIReturn As Long

' Initialize the return code
lngReturnCode = 0

' Open the computer Service Manager
lngServiceManagerHandle = OpenSCManager(strComputerName, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS)

' If we successfully opened the service manager, call the API
' to start the specified service
If lngServiceManagerHandle <> 0 Then
lngServiceHandle = OpenService(lngServiceManagerHandle, strServiceName, SERVICE_ALL_ACCESS)
If lngServiceHandle <> 0 Then
lngAPIReturn = StartService(lngServiceHandle, 0, 0)
If lngAPIReturn = 0 Then
lngReturnCode = Err.LastDllError
End If
CloseServiceHandle lngServiceHandle
Else
lngReturnCode = 9992 ' Service not opened
End If
CloseServiceHandle lngServiceManagerHandle
Else
lngReturnCode = 9991 ' Service Manager not opened
End If

' Return the appropriate return code
startComputerService = lngReturnCode

End Function

Public Function startComputerServiceWithArgs(strComputerName As String, strServiceName As String, ByRef strArgArray() As String) As Long

' Declare local variable(s)
Dim udtServiceStatus As SERVICE_STATUS
Dim lngServiceManagerHandle As Long
Dim lngNumberOfArguments As Long
Dim strArgumentValue As String
Dim intArgsUBound As Integer
Dim intArrayIndex As Integer
Dim lngServiceHandle As Long
Dim lngParameters() As Long
Dim bytArgument0() As Byte
Dim bytArgument1() As Byte
Dim bytArgument2() As Byte
Dim bytArgument3() As Byte
Dim bytArgument4() As Byte
Dim bytArgument5() As Byte
Dim bytArgument6() As Byte
Dim bytArgument7() As Byte
Dim bytArgument8() As Byte
Dim bytArgument9() As Byte
Dim lngReturnCode As Long
Dim lngAPIReturn As Long

' Initialize variable(s)
lngReturnCode = 0
intArgsUBound = 9
lngNumberOfArguments = 0

' Open the computer Service Manager
lngServiceManagerHandle = OpenSCManager(strComputerName, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS)

' Adjust the intArgsUBound variable if there are less than 10 entries
' This way we can make sure we don't try to pass more than 10 args
If UBound(strArgArray) < intArgsUBound Then
intArgsUBound = UBound(strArgArray)
End If

' Build the argument list array to pass to StartService
For intArrayIndex = 0 To intArgsUBound
ReDim Preserve lngParameters(lngNumberOfArguments) As Long
Select Case intArrayIndex
Case 0
unicodeToByteArray bytArgument0, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument0(0))
Case 1
unicodeToByteArray bytArgument1, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument1(0))
Case 2
unicodeToByteArray bytArgument2, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument2(0))
Case 3
unicodeToByteArray bytArgument3, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument3(0))
Case 4
unicodeToByteArray bytArgument4, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument4(0))
Case 5
unicodeToByteArray bytArgument5, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument5(0))
Case 6
unicodeToByteArray bytArgument6, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument6(0))
Case 7
unicodeToByteArray bytArgument7, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument7(0))
Case 8
unicodeToByteArray bytArgument8, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument8(0))
Case 9
unicodeToByteArray bytArgument9, strArgArray(intArrayIndex)
lngParameters(lngNumberOfArguments) = VarPtr(bytArgument9(0))
End Select
lngNumberOfArguments = lngNumberOfArguments + 1
Next

' If we successfully opened the service manager, call the API
' to start the specified service
If lngServiceManagerHandle <> 0 Then
lngServiceHandle = OpenService(lngServiceManagerHandle, strServiceName, SERVICE_ALL_ACCESS)
If lngServiceHandle <> 0 Then
lngAPIReturn = StartService(lngServiceHandle, lngNumberOfArguments, VarPtr(lngParameters(0)))
If lngAPIReturn = 0 Then
lngReturnCode = Err.LastDllError
End If
CloseServiceHandle lngServiceHandle
Else
lngReturnCode = 9992 ' Service not opened
End If
CloseServiceHandle lngServiceManagerHandle
Else
lngReturnCode = 9991 ' Service Manager not opened
End If

' Return the appropriate return code
startComputerServiceWithArgs = lngReturnCode

End Function

Private Sub unicodeToByteArray(ByRef arrBytes() As Byte, strStringToConvert As String)

' Declare local variable(s)
Dim lngStringLength As Long
Dim lngStringLengthB As Long
Dim lngStringPosition As Long

' Get the length of the string in characters
lngStringLength = Len(strStringToConvert)
lngStringLengthB = LenB(strStringToConvert)

' ReDim the byte array with the string length so
' we duplicate how VB converts BSTRs to ABSTRs for
' API calls
ReDim arrBytes(lngStringLengthB) As Byte

' Populate each position in the array with the
' ASCII value of the appropriate letter
For lngStringPosition = 1 To lngStringLength
arrBytes(lngStringPosition - 1) = Asc(Mid(strStringToConvert, lngStringPosition, 1))
Next

End Sub
 
And if you use StartServiceW rather than StartServiceA?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top