You're correct — in .NET Framework 4.8, you can't directly set the start type of a service via the System.ServiceProcess.ServiceController
class. You're also correct in turning to P/Invoke with ChangeServiceConfig
.
The problem appears to result from trying to pass a SafeHandle
(i.e., MyService.ServiceHandle
) to a native Win32 function (ChangeServiceConfig
) that expects a raw IntPtr
. However your current declaration expects:
ByVal hService As SafeHandle
Which should be:
ByVal hService As IntPtr
Or alternatively, you can extract the IntPtr
from the SafeHandle
by accessing .DangerousGetHandle()
.
To fix this, try either of the following:
- Update P/Invoke signature
Change the function declaration for ChangeServiceConfig
to take IntPtr
instead of SafeHandle
:
<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Private Shared Function ChangeServiceConfig(
ByVal hService As IntPtr,
ByVal dwServiceType As UInteger,
ByVal dwStartType As UInteger,
ByVal dwErrorControl As UInteger,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpBinaryPathName As String,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpLoadOrderGroup As String,
ByVal lpdwTagId As IntPtr,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpDependencies As String,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpServiceStartName As String,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpPassword As String,
<MarshalAs(UnmanagedType.LPWStr)> ByVal lpDisplayName As String
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
- Use
.ServiceHandle.DangerousGetHandle()
safely
Using MyService As New ServiceController("BlahService")
Dim handle As IntPtr = MyService.ServiceHandle.DangerousGetHandle()
ChangeServiceConfig(handle, CUInt(SERVICE_NO_CHANGE), SERVICE_DISABLED, SERVICE_NO_CHANGE,
Nothing, Nothing, IntPtr.Zero, Nothing, Nothing, Nothing, Nothing)
' Don't call CloseServiceHandle — the SafeHandle owns it and will clean it up
MyService.Close()
End Using
Do not call CloseServiceHandle
yourself - since ServiceController.ServiceHandle
returns a SafeHandle
, you must not call CloseServiceHandle()
yourself — the .NET runtime will handle that for you when the ServiceController
is disposed. Calling CloseServiceHandle()
manually on a SafeHandle
can cause a double free, which can crash your process.
If the above response helps answer your question, remember to "Accept Answer" so that others in the community facing similar issues can easily find the solution. Your contribution is highly appreciated.
hth
Marcin