Make Class disposable

StewartBW 1,145 Reputation points
2024-06-09T16:31:59.73+00:00

Hello,

Here's a class I use to log off the user, etc, the usage:

Dim IExitWindows As New WinExitClass
IExitWindows.PowerOffWindows()

How can I make this class disposable?

Couldn't figure out how to add disposable so instead of:

Dim IExitWindows As New WinExitClass

I could use

Using IExitWindows As New WinExitClass

Thanks in advance :)

The code:

Public Class WinExitClass
    Private Declare Function ExitWindowsEx Lib "user32" (ByVal uFlags As Integer, ByVal dwReserved As Integer) As Integer
    Private Declare Function GetCurrentProcess Lib "kernel32" () As Integer
    Private Declare Function OpenProcessToken Lib "advapi32" (ByVal ProcessHandle As Integer, ByVal DesiredAccess As Integer, ByRef TokenHandle As Integer) As Integer
    Private Declare Function LookupPrivilegeValue Lib "advapi32" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, ByRef lpLuid As LUID) As Integer
    Private Declare Function AdjustTokenPrivileges Lib "advapi32" (ByVal TokenHandle As Integer, ByVal DisableAllPrivileges As Integer, ByRef NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Integer, ByRef PreviousState As TOKEN_PRIVILEGES, ByRef ReturnLength As Integer) As Integer
    Private Const EWX_FORCE As Integer = 4
    Private Structure LUID
        Dim UsedPart As Integer
        Dim IgnoredForNowHigh32BitPart As Integer
    End Structure
    Private Structure TOKEN_PRIVILEGES
        Dim PrivilegeCount As Integer
        Dim TheLuid As LUID
        Dim Attributes As Integer
    End Structure
    Private Sub AdjustToken()
        On Error Resume Next
        Const TOKEN_ADJUST_PRIVILEGES As Short = &H20S
        Const TOKEN_QUERY As Short = &H8S
        Const SE_PRIVILEGE_ENABLED As Short = &H2S
        Dim hdlProcessHandle As Integer
        Dim hdlTokenHandle As Integer
        Dim tmpLuid As LUID
        Dim tkp As TOKEN_PRIVILEGES
        Dim tkpNewButIgnored As TOKEN_PRIVILEGES
        Dim lBufferNeeded As Integer
        hdlProcessHandle = GetCurrentProcess()
        OpenProcessToken(hdlProcessHandle, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, hdlTokenHandle)
        LookupPrivilegeValue("", "SeShutdownPrivilege", tmpLuid)
        tkp.PrivilegeCount = 1
        tkp.TheLuid = tmpLuid
        tkp.Attributes = SE_PRIVILEGE_ENABLED
        AdjustTokenPrivileges(hdlTokenHandle, Convert.ToInt32(False), tkp, tkpNewButIgnored.ToString.Length, tkpNewButIgnored, lBufferNeeded)
    End Sub
    Friend Sub PowerOffWindows()
        On Error Resume Next
        AdjustToken()
        ExitWindowsEx(EnumExitWindows.WE_POWEROFF Or EWX_FORCE, &HFFFFS)
    End Sub
    Friend Sub SignOutWindows()
        On Error Resume Next
        AdjustToken()
        ExitWindowsEx(EnumExitWindows.WE_LOGOFF Or EWX_FORCE, &HFFFFS)
    End Sub
End Class
VB
VB
An object-oriented programming language developed by Microsoft that is implemented on the .NET Framework. Previously known as Visual Basic .NET.
2,737 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Jack J Jun 24,516 Reputation points Microsoft Vendor
    2024-06-10T02:05:00.69+00:00

    @StewartBW, you could try to implement the interface IDisposable to make the class disposable.

    Here is a code example you could refer to.

    
    Module Module1
        Sub Main()
            'Dim IExitWindows As New WinExitClass
            'IExitWindows.PowerOffWindows()
            Using IExitWindows As New WinExitClass
                ' Insert code to work with resource.
                IExitWindows.PowerOffWindows()
            End Using
        End Sub
    End Module
    Public Class WinExitClass : Implements IDisposable
       ...................
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls
        ' IDisposable
        Protected Overridable Sub Dispose(disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' TODO: dispose managed state (managed objects).
                End If
                ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                ' TODO: set large fields to null.
            End If
            Me.disposedValue = True
        End Sub
        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        Protected Overrides Sub Finalize()
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(False)
            MyBase.Finalize()
        End Sub
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    End Class
    
    
    

    Hope it could help you.

    Best Regards,

    Jack


    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. KOZ6.0 6,490 Reputation points
    2024-06-10T02:36:21.1266667+00:00

    Sorry, I was mistaken. RevertToSelf was not necessary.

    If you want to make it IDisposable, you should run RevertToSelf.

    Public Class WinExitClass
        Implements IDisposable
    
        ・
        ・
        ・
    
        Private disposedValue As Boolean
    
        Protected Overridable Sub Dispose(disposing As Boolean)
            If Not disposedValue Then
                disposedValue = True
                ’RevertToSelf()
            End If
        End Sub
    
        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(disposing:=True)
            GC.SuppressFinalize(Me)
        End Sub
    
        '<DllImport("advapi32.dll", SetLastError:=True)>
        'Private Shared Function RevertToSelf() As Boolean
        'End Function
    
    End Class
    

    This code is a modified version of VB6 code. There may be problems in the future when running on 64bit.:(

    ■ Using VB6 style API declarations can corrupt strings. Ex:

    Declare Function MessageBox Lib "user32.dll" Alias "MessageBoxA" _
        (hWnd As IntPtr, text As String, caption As String, options As UInteger) As Integer
    
    Sub Main()
        Dim msg As String = "ABCDEFG"
        MessageBox(IntPtr.Zero, msg, vbNullString, 0)
        Debug.Print(msg) ’ ABC...
    End Sub
    

    Rewrite declarations to use DllImport.

    ■ The token is still open. Close it.

    Private Sub AdjustToken()
        ・
        ・
        ・
        CloseHandle(hdlTokenHandle)
    End Sub
    
    <DllImport("kernel32.dll", SetLastError:=True)>
    Private Shared Function CloseHandle(hObject As IntPtr) As Boolean
    End Function
    

  3. RLWA32 45,701 Reputation points
    2024-06-10T06:33:15.36+00:00

    By taking advantage of existing managed classes that handle windows identity some of the calls to the Windows API needed to adjust token privileges can be removed. The managed classes return the needed resources which implement IDisposable. So the problem can be reduced to properly disposing of those resources and making an entire class implement IDisposable isn't needed for a solution.

    The following example implements a VB class that internally handles those disposble resources within a Using block in the EnablePrivilege method.

    For example, a console application (.Net FW 4.8)

    Imports System.ComponentModel
    Imports Microsoft.Win32.SafeHandles
    Imports System.Runtime.InteropServices
    Imports System.Security.Principal
    
    Module Module1
    
        Sub Main()
            WinExitClass.LogoffWindows()
            'WinExitClass.PowerOffWindows()
        End Sub
    
        NotInheritable Class WinExitClass
            <StructLayout(LayoutKind.Sequential)>
            Private Structure LUID
                Public LowPart As UInteger
                Public HighPart As Integer
            End Structure
    
            <StructLayout(LayoutKind.Sequential)>
            Private Structure LUID_AND_ATTRIBUTES
                Public Luid As LUID
                Public Attributes As UInteger
            End Structure
    
            <StructLayout(LayoutKind.Sequential)>
            Private Structure TOKEN_PRIVILEGES
                Public PrivilegeCount As UInteger
                Public Privileges As LUID_AND_ATTRIBUTES
            End Structure
    
            <DllImport("Advapi32.dll", CallingConvention:=CallingConvention.StdCall, ExactSpelling:=True, SetLastError:=True)>
            Private Shared Function AdjustTokenPrivileges(TokenHandle As SafeAccessTokenHandle,
                                       DisableAllPrivileges As Boolean,
                                       ByRef NewState As TOKEN_PRIVILEGES,
                                       BufferLength As UInteger,
                                       PreviousStat As IntPtr,
                                       ReturnLength As IntPtr) As Boolean
            End Function
    
            <DllImport("Advapi32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Unicode, SetLastError:=True)>
            Private Shared Function LookupPrivilegeValue(Systemname As String, lpName As String, ByRef lpLuid As LUID) As Boolean
            End Function
    
            <DllImport("User32.dll", CallingConvention:=CallingConvention.StdCall, ExactSpelling:=True, SetLastError:=True)>
            Private Shared Function ExitWindowsEx(uFlags As UInteger, reason As UInteger) As Boolean
            End Function
    
            Const EWX_FORCE As UInteger = &H4
            Const EWX_LOGOFF As UInteger = &H0
            Const EWX_POWEROFF As UInteger = &H8
            Const SHUTDOWN_REASON_MAJOR_OTHER As UInteger = &H0
            Private Sub New()
    
            End Sub
    
            Shared Sub PowerOffWindows()
                Try
                    EnablePrivilege()
                    ExitWindowsEx(EWX_POWEROFF Or EWX_FORCE, SHUTDOWN_REASON_MAJOR_OTHER)
                Catch ex As Exception
                    Console.WriteLine(ex.Message)
                End Try
            End Sub
    
            Shared Sub LogoffWindows()
                Try
                    EnablePrivilege()
                    ExitWindowsEx(EWX_LOGOFF Or EWX_FORCE, SHUTDOWN_REASON_MAJOR_OTHER)
                Catch ex As Exception
                    Console.WriteLine(ex.Message)
                End Try
            End Sub
    
            Private Shared Sub EnablePrivilege()
                Const SE_PRIVILEGE_ENABLED As UInteger = 2
                Const ERROR_NOT_ALL_ASSIGNED = 1300
    
                Using wi As WindowsIdentity = WindowsIdentity.GetCurrent(TokenAccessLevels.Query Or TokenAccessLevels.AdjustPrivileges),
                    accesstoken As SafeAccessTokenHandle = wi.AccessToken
    
                    Dim tp As TOKEN_PRIVILEGES
                    tp.PrivilegeCount = 1
                    tp.Privileges.Attributes = SE_PRIVILEGE_ENABLED
    
                    If LookupPrivilegeValue(String.Empty, "SeShutDownPrivilege", tp.Privileges.Luid) = False Then
                        Throw New Win32Exception()
                    End If
    
                    If AdjustTokenPrivileges(accesstoken, False, tp, 0, IntPtr.Zero, IntPtr.Zero) = False Or
                        Marshal.GetLastWin32Error() = ERROR_NOT_ALL_ASSIGNED Then
                        Throw New Win32Exception()
                    End If
    
                End Using
            End Sub
        End Class
    
    End Module
    
    0 comments No comments

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.