Close service handle

StewartBW 1,720 Reputation points
2025-06-14T13:12:50.3866667+00:00

Hi,

I'm using this code to change the service start type, I think not possible in .NET 4.8 natively?

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Private Shared Function ChangeServiceConfig(<[In]()> ByVal hService As SafeHandle, ByVal dwServiceType As UInteger, ByVal dwStartType As UInteger, ByVal dwErrorControl As UInteger,
                                           <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpBinaryPathName As String, <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpLoadOrderGroup As String,
                                           ByVal lpdwTagId As IntPtr, <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpDependencies As String, <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpServiceStartName As String,
                                           <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpPassword As String, <[In](), MarshalAs(UnmanagedType.LPWStr)> ByVal lpDisplayName As String) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

usage:
Using MyService As New ServiceController("BlahService")
    ChangeServiceConfig(MyService.ServiceHandle, CUInt(SERVICE_NO_CHANGE), SERVICE_DISABLED, SERVICE_NO_CHANGE)
    MyService.Close
End Using

But AI recommended: If you use ServiceController.ServiceHandle and pass it to a Win32 API function like ChangeServiceConfig, you are responsible for calling CloseServiceHandle on that handle when you are finished with it, so:

<DllImport("advapi32.dll", SetLastError:=True)>
Private Shared Function CloseServiceHandle(ByVal hSCObject As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

new usage:
Using MyService As New ServiceController("BlahService")
    ChangeServiceConfig(MyService.ServiceHandle, CUInt(SERVICE_NO_CHANGE), SERVICE_DISABLED, SERVICE_NO_CHANGE)
    MyService.Close
    CloseServiceHandle(MyService.ServiceHandle)
End Using

I just don't know how to fix the error:

Value of type 'System.Runtime.InteropServices.SafeHandle' cannot be converted to 'System.IntPtr'

Thanks for help :)

VB
VB
An object-oriented programming language developed by Microsoft that is implemented on the .NET Framework. Previously known as Visual Basic .NET.
2,889 questions
{count} votes

Accepted answer
  1. Marcin Policht 49,255 Reputation points MVP Volunteer Moderator
    2025-06-14T13:38:33.1766667+00:00

    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:

    1. 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
    
    1. 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

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.