Code zu verschiedenen Themen in .NET

Abspielen einer eingebetteten Klangdatei

Will man in einer Windows-Forms-Anwendung einen Klang wiedergeben, dann wird man im .NET Framework lange nach einer Lösung suchen und keine finden. Das .NET Framework enthält auch in der Version 1.1 keinerlei Unterstützung für die Wiedergabe von Klang- und Videodaten. Oft ist es aber sinnvoll, den Benutzer durch einen Klang auf das Eintreten eines Ereignisses hinzuweisen.

Idealerweise sollte die Klangdatei in die Anwendung eingebettet werden, damit nicht zu viele Dateien ausgeliefert werden müssen. Dies erreicht man, indem man die Klangdatei (im Wave-Format) dem Projekt hinzufügt, die auswählt und im Eigenschaftenfenster als Buildaktion den Wert Eingebettete Ressource wählt. Anschließend kann man die im Folgenden angegebene Klasse SimpleSound benutzen, um einen Klang anhand seines Ressourcenidentifikators wiederzugeben:

Imports System.IO
Imports System.Reflection
Imports System.Runtime.InteropServices

Public Class SimpleSound
    Private Declare Auto Function PlaySound Lib "winmm.dll" ( _
        ByVal pszSound As IntPtr, _
        ByVal hModule As IntPtr, _
        ByVal dwFlags As Int32 _
    ) As Boolean

    Private Const SND_ASYNC As Int32 = &H1
    Private Const SND_MEMORY As Int32 = &H4
    Private Const SND_LOOP As Int32 = &H8
    Private Const SND_PURGE As Int32 = &H40

    Private Shared m_hgData As IntPtr

    Public Shared Sub Play(ByVal Name As String, ByVal [Loop] As Boolean)
        If Not m_hgData.Equals(IntPtr.Zero) Then
            StopPlaying()
        End If
        Dim st As Stream = _
            [Assembly].GetExecutingAssembly().GetManifestResourceStream( _
                Name _
            )
        Dim Length As Integer = CInt(st.Length)
        Dim Data(Length - 1) As Byte
        st.Read(Data, 0, Length)
        st.Close()
        m_hgData = Marshal.AllocHGlobal(Length)
        Marshal.Copy(Data, 0, m_hgData, Length)
        Dim Flags As Int32 = SND_MEMORY Or SND_ASYNC
        If [Loop] Then
            Flags = Flags Or SND_LOOP
        End If
        PlaySound( _
            m_hgData, _
            IntPtr.Zero, _
            Flags _
        )
    End Sub

    Public Shared Sub [Stop]()
        If Not m_hgData.Equals(IntPtr.Zero) Then
            StopPlaying()
        End If
    End Sub

    Private Shared Sub StopPlaying()
        PlaySound(IntPtr.Zero, IntPtr.Zero, SND_PURGE)
        Marshal.FreeHGlobal(m_hgData)
        m_hgData = IntPtr.Zero
    End Sub
End Class
Abspielen einer eingebetteten Klangdatei.

Ein Beispielaufruf könnte SimpleSound.Play("MyRootNamespace.foo.wav", True) lauten, wobei MyRootNamespace für den Stammnamensraum des Projekts und foo.wav für den Namen der Wave-Datei steht. Vor dem Beenden der Anwendung, die die Klasse zum Abspielen eines Klangs verwendet, muß die Methode Stop aufgerufen werden.

Feststellen der Inaktivität des Benutzers

Das .NET Framework bietet keine Unterstützung dafür, festzustellen, ob der Benutzer das System für einen bestimmten Zeitraum nicht mehr bedient hat. Die nachstehende Klasse UserIdleDetector rüstet die fehlende Funktionalität auf Basis der Plattformfunktion GetLastInputInfo nach. Das Ereignis UserIdle wird ausgelöst, wenn eine bestimmte Zeitspanne seit der letzten Benutzereingabe verstrichen ist. Nimmt der Benutzer seine Aktivität wieder auf, wird das Ereignis UserWakeup ausgelöst. Die Auflösung des Detektors ist ungefähr eine Sekunde:

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Timers

Public Class UserIdleDetector
    Inherits Component

    Private Declare Function GetLastInputInfo Lib "user32.dll" ( _
        ByRef plii As LASTINPUTINFO _
    ) As Boolean

    Private Structure LASTINPUTINFO
        Public cbSize As Int32
        Public dwTime As Int32
    End Structure

    Public Delegate Sub UserIdleEventHandler( _
        ByVal sender As Object, _
        ByVal e As EventArgs _
    )
    Public Delegate Sub UserWakeupEventHandler( _
        ByVal sender As Object, _
        ByVal e As EventArgs _
    )

    Public Event UserIdle As UserIdleEventHandler
    Public Event UserWakeup As UserWakeupEventHandler

    Private WithEvents m_Timer As New Timer(1000)

    Private m_Interval As Integer
    Private m_LastInputTime As Integer
    Private m_lii As LASTINPUTINFO

    Public Sub Start(ByVal IdleTimeoutSeconds As Integer)
        m_Interval = IdleTimeoutSeconds * 1000
        m_Timer.Start()
    End Sub

    Public Sub [Stop]()
        m_Timer.Stop()
    End Sub

    Private Function GetIdleTime() As Integer
        m_lii.cbSize = Marshal.SizeOf(m_lii)
        If Not GetLastInputInfo(m_lii) Then
            Throw New Win32Exception
        Else
            If m_lii.dwTime <> m_LastInputTime Then
                m_LastInputTime = m_lii.dwTime
                Return 0
            Else
                Return Environment.TickCount - m_lii.dwTime
            End If
        End If
    End Function

    Private Sub m_Timer_Elapsed( _
        ByVal sender As Object, _
        ByVal e As ElapsedEventArgs _
    ) Handles m_Timer.Elapsed
        Static UserInactive As Boolean
        If GetIdleTime() > m_Interval Then
            If Not UserInactive Then
                UserInactive = True
                RaiseEvent UserIdle(Me, EventArgs.Empty)
            End If
        Else
            If UserInactive Then
                UserInactive = False
                RaiseEvent UserWakeup(Me, EventArgs.Empty)
            End If
        End If
    End Sub
End Class
Die Klasse UserIdleDetector zum Ermitteln der Inaktivität des Benutzers.