• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 956
  • Last Modified:

Use ChangeServiceConfigW from Visual Basic 6

Okay, I'll award as many points as I am allowed for this one as long as the answer contains working code that uses the WIDE versions of the appropriate APIs.

I am having a very frustrating time using the ChangeServiceConfigW API function from Visual Basic 6.  All I want to do is change the "Start Type" of a service (e.g. from Automatic to Manual).  I am successfully calling the following API functions:

OpenSCManagerW()
LockServiceDatabase()
OpenServiceW()
QueryServiceConfigW()  <-  twice.  once to get the buffer size and once to retrieve the data

All of the above calls are in preparation for calling ChangeServiceConfigW().  The problem is that no matter how I call ChangeServiceConfigW(), I get a return value of &H0 and GetLastError() returns "Error 87: The parameter is incorrect."

Here is the Declare statement:

Private Declare Function ChangeServiceConfig Lib "advapi32.dll" Alias "ChangeServiceConfigW" ( _
    ByVal hService As Long, _
    ByVal dwServiceType As Long, _
    ByVal dwStartType As Long, _
    ByVal dwErrorControl As Long, _
    ByRef lpBinaryPathName As Any, _
    ByRef lpLoadOrderGroup As Any, _
    ByRef lpdwTagId As Long, _
    ByRef lpDependencies As Any, _
    ByRef lpServiceStartName As Any, _
    ByRef lpPassword As Any, _
    ByRef lpDisplayName As Any) As Long

I have tried the following:

lngReturnCode = ChangeServiceConfig(lngServiceHandle, SERVICE_NO_CHANGE, 4&, SERVICE_NO_CHANGE, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&)

** Note that the MSDN documentation incorrectly states that if you don't want to change the password or display name that you should pass SERVICE_NO_CHANGE, even though their C++ example correctly shows passing a NULL.

lngReturnCode = ChangeServiceConfig(lngServiceHandle, dwServiceType, dwStartType, dwErrorControl, lpBinaryPathName(0), lpLoadOrderGroup(0), lpdwTagId, lpDependencies(0), lpServiceStartName(0), bytPwd(0), lpDisplayName(0))

** where every parameter is filled with an identicle copy of the information returned from QueryServiceConfigW() (in the case of the arrays, they are byte arrays filled with Unicode strings and null terminators, except for lpDependencies(), which is double-null terminated).

I did find an interesting note at the following link:

http://www.tek-tips.com/gpviewthread.cfm/qid/608867/pid/711/lev2/4/lev3/32

This seemed to indicate that there was a known issue calling this API from VB, but their work around is what prompted me to try the second example above and it didn't help.  Also, it was the ANSI version, not the Unicode version.

Good Luck!

Tony
0
selke
Asked:
selke
  • 3
  • 2
1 Solution
 
cfry001Commented:
Why do you want to use the wide versions?

I have got the ascii version working.
0
 
cfry001Commented:
are you from a non-english speaking country?
0
 
cfry001Commented:
here is some code that works for me with the wide versions:

Option Explicit



'All of the above calls are in preparation for calling ChangeServiceConfigW().  The problem is that no matter how I call ChangeServiceConfigW(), I get a return value of &H0 and GetLastError() returns "Error 87: The parameter is incorrect."

Private Declare Function ChangeServiceConfig Lib "advapi32.dll" Alias "ChangeServiceConfigW" ( _
    ByVal hService As Long, _
    ByVal dwServiceType As Long, _
    ByVal dwStartType As Long, _
    ByVal dwErrorControl As Long, _
    ByRef lpBinaryPathName As Any, _
    ByRef lpLoadOrderGroup As Any, _
    ByRef lpdwTagId As Long, _
    ByRef lpDependencies As Any, _
    ByRef lpServiceStartName As Any, _
    ByRef lpPassword As Any, _
    ByRef lpDisplayName As Any) As Long


'Private Declare Function OpenSCManager Lib "advapi32.dll" Alias "OpenSCManagerW" (ByVal lpMachineName As String, ByVal lpDatabaseName As String, ByVal dwDesiredAccess As Long) As Long
Private Declare Function OpenSCManager Lib "advapi32.dll" Alias "OpenSCManagerW" (ByVal lpMachineName As Long, ByVal lpDatabaseName As Long, ByVal dwDesiredAccess As Long) As Long
Private Declare Function LockServiceDatabase Lib "advapi32.dll" (ByVal hSCManager As Long) As Long
Private Declare Function OpenService Lib "advapi32.dll" Alias "OpenServiceW" (ByVal hSCManager As Long, ByVal lpServiceName As Long, ByVal dwDesiredAccess As Long) As Long
Private Declare Function QueryServiceConfig Lib "advapi32.dll" Alias "QueryServiceConfigA" (ByVal hService As Long, lpServiceConfig As QUERY_SERVICE_CONFIG, ByVal cbBufSize As Long, pcbBytesNeeded As Long) As Long

Private Declare Function GetLastError Lib "kernel32" () As Long


' 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 SERVICES_ACTIVE_DATABASE = "ServicesActive"
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 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)
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_DISABLED As Long = &H4
Private Const SERVICE_DEMAND_START As Long = &H3
Private Const SERVICE_AUTO_START As Long = &H2
Private Const SERVICE_SYSTEM_START As Long = &H1
Private Const SERVICE_BOOT_START As Long = &H0

'Service object specific access types

Private Const SERVICE_NO_CHANGE = &HFFFF


Private Type QUERY_SERVICE_CONFIG
        dwServiceType As Long
        dwStartType As Long
        dwErrorControl As Long
        lpBinaryPathName As String
        lpLoadOrderGroup As String
        dwTagId As Long
        lpDependencies As String
        lpServiceStartName As String
        lpDisplayName As String
End Type


Sub ChangeServiceStartType(service As String, Auto As Boolean)
'OpenSCManagerW()
'LockServiceDatabase()
'OpenServiceW()
'QueryServiceConfigW()  <-  twice.  once to get the buffer size and once to retrieve the data
On Error GoTo errhandler
Dim lret As Long
Dim hSCManager As Long, hService As Long
Dim query As QUERY_SERVICE_CONFIG
Dim bufSize As Long, needed As Long
Dim MachineName As String, sDB As String, sServiceName As String
Dim lpMachineName As Long
Dim lpDB As Long
Dim lpService As Long
    MachineName = "XXXX" & Chr(0)
    sDB = SERVICES_ACTIVE_DATABASE & Chr(0)
    lpMachineName = StrPtr(MachineName)
    'service = service & Chr(0)
    lpDB = StrPtr(sDB)
    sServiceName = service & Chr(0)
    'sServiceName = StrConv(sServiceName, vbUnicode)
    lpService = StrPtr(sServiceName)
   
    hSCManager = OpenSCManager(lpMachineName, lpDB, SC_MANAGER_ALL_ACCESS)
    If hSCManager <> 0 Then
        hService = OpenService(hSCManager, lpService, SERVICE_ALL_ACCESS)
        If hService = 0 Then
            lret = GetLastError()
            Exit Sub
        End If


        lret = ChangeServiceConfig(hService, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&)
        If lret = 0 Then
            lret = GetLastError()
            Exit Sub
        End If

    End If
    Exit Sub
errhandler:
    MsgBox Err.Number & " " & Err.Description
End Sub

Private Sub cmdStartType_Click()
    Call ChangeServiceStartType(txtServiceName, True)
End Sub
0
 
selkeAuthor Commented:
I am using the wide versions because the code I am working on needs to be multi-lingual, including the asian dialects.

Your example worked perfectly, though you are using the samse Declare and call for ChangeServiceConfigW() as I am, so I have to go back and look at what else you may have done differently.  I was passing a NULL to MachineName and DatabaseName when I called OpenSCManagerW(), which worked fine for StartService() and QueryServiceStatusEx(), but perhaps it is causing me trouble here.

Anyway, you did exactly what I asked, so I'll be increasing the award to the maximum available and accepting your answer.

Thanks a million!

Tony
0
 
selkeAuthor Commented:
Here's an FYI ...

AS far as I can tell, the only thing that IW as doing wrong was the following:

You declared the no change constant as:

Private Const SERVICE_NO_CHANGE = &HFFFF

I declared it as:

Private Const SERVICE_NO_CHANGE = &HFFFF&

Now, adding the trailing ampersand is something I habitually do because in the old days, VB was bad at resetting the high bits on a long integer if you only put a small value into it.  It turns out that for the first time in my memory, this practice bit me in the a$$.

Thank you for your help.

Tony
0

Featured Post

[Webinar] Cloud and Mobile-First Strategy

Maybe you’ve fully adopted the cloud since the beginning. Or maybe you started with on-prem resources but are pursuing a “cloud and mobile first” strategy. Getting to that end state has its challenges. Discover how to build out a 100% cloud and mobile IT strategy in this webinar.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now