Safrax
Dec 29th, 2003, 10:52 PM
I apolgize for the length. My program is an intermediary program. It sits between a winlirc server. Winlirc is a program at interprets the signals from an infrared remote control and then sends its interpretation to all clients connected to the server. My program connects to the winlirc server, waits for something to be sent to it from the winlirc server, processes the input, and then uses the processed input to send a command, using some pieces of the winamp api, to foobar2000.
This program has been a lot of learning for me and much of it I still don't fully understand. Sockets being the least understood of the whole program. I have a few bugs left to work out. One of which happens to be in the timers. Any time a timer is run it starts sucking up 100% of my cpu. I need some way for a delay between processing input from the winlirc server that doesn't eat cpu time like mad. Another bug which I think stems somewhat from the way sockets are handled seems to process two seperate inputs and runs them both at the same time, which shouldn't be possible. And yes much of this is hard coded. I have no intention to put the work into this program to make it capable of being configured without having the source code and visual studio.
I'd appreciate any comments you may have on this code or any ways to make it better. It's hard to learn how to do things the right way without some guidance. :)
Also my comments suck.. or they are sucky attempts at humor, just ignore em. :P
Option Strict On
Module Module1
'Global variable describing if the program is active or not.
'will be set by the system tray icon.
Dim gIsActive As Boolean = True
Dim mBytes(1024) As Byte
'Dim mTrayIconCreated As Boolean = False
'Supar sekret MAGICAL MSDN code...
'creates a new socket
Dim LIRCSocket As New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp)
'Resolves a hostname
Dim ipHostInfo As System.Net.IPHostEntry = System.Net.Dns.Resolve("172.16.1.69")
'Does something
Dim ipas As System.Net.IPAddress = ipHostInfo.AddressList(0)
'ipas is the ipaddress of the target machine, 8765 is the socket we want to connect to on the target machine
Dim RemoteIP As New System.Net.IPEndPoint(ipas, 8765)
Dim Start, Finish As Double
'if this is true then we are connected to the lirc server
Public mIsConnected As Boolean
'Holds the last key that was pressed
'Dim mLastKeyPressed As String = ""
'holes the number of times the last key was pressed
'Dim mLastKeyPressedTimes As Integer
'Sets whether or not the program is finished.
Public mIsFinished As Boolean = False
'Volume of Foobar
'Public mVolume As Integer
'Is foobar paused or not? This variable has the answer!
Dim mFoobarPaused As Boolean
#Region "WINAMP API"
' Window Messages to use with SendMessageByString and SendMessageLong.
Public Const WM_COMMAND As Integer = &H111
Public Const WM_USER As Integer = &H400
'' WM_COMMAND Winamp API IDs.
Public Const WA_NEXT As Integer = 40048
Public Const WA_PAUSE As Integer = 40046
Public Const WA_PLAY As Integer = 40045
Public Const WA_PREVIOUS As Integer = 40044
Public Const WA_STOP As Integer = 40047
Public Const WA_VOLUME_DOWN As Integer = 40059
Public Const WA_VOLUME_UP As Integer = 40058
#End Region
' Declare our API calls.
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Private Declare Function apiGetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal sCaption As String, ByVal lCaptionSize As Integer) As Integer
Private Declare Function apiGetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Integer) As Integer
Sub connect()
'I have no clue what the heck this stuff does.
'Stolen from MSDN's Socket stuff
Try
LIRCSocket.Connect(RemoteIP)
Catch
End Try
End Sub
Public Function Winamp_Hwnd() As Integer
' Used to get the handle of the Winamp window.
Winamp_Hwnd = FindWindow("Winamp v1.x", Microsoft.VisualBasic.vbNullString)
End Function
Public Function Foobar_Hwnd() As Integer
' Used to get the handle of the Foobar2000 window.
Foobar_Hwnd = FindWindow("FOOBAR2000_CLASS", Microsoft.VisualBasic.vbNullString)
End Function
Public Function GetWindowCaption(ByRef WhWnd As Integer) As String
'API WRAPPER TO GET WINDOW CAPTION INFO
Dim lLength As Integer
Dim sCaption As String
Dim lRetValue As Integer
'Get length of windows Caption
lLength = apiGetWindowTextLength(WhWnd) + 1
'Create a string of blanks to pass into apiGetWindowText
sCaption = New String(CType(" ", Char), lLength + 1)
'Get Windows Caption
lRetValue = apiGetWindowText(WhWnd, sCaption, lLength)
GetWindowCaption = Microsoft.VisualBasic.Left(sCaption, lLength)
End Function
Public Function ProcessSocket() As String
'this variable holds the string that will be received and stripped from the socket buffer thing
' Dim SocketData As String
Dim BytesRec As Integer
BytesRec = LIRCSocket.Receive(mBytes)
'assign what the remote program sent to a variable
ProcessSocket = (System.Text.Encoding.ASCII.GetString(mBytes, 0, 33))
'This strips out the first 20 characters in the stuff recieved from the socket
ProcessSocket = ProcessSocket.Remove(0, 20)
'This removes anything after the first 4 characters
ProcessSocket = ProcessSocket.Remove(4, (ProcessSocket.Length - 4))
End Function
Function Foobar_Status() As String
Dim FoobarHandle As Integer
Dim FoobarTitle As String
FoobarHandle = Foobar_Hwnd()
FoobarTitle = GetWindowCaption(FoobarHandle)
If FoobarTitle.StartsWith("foobar2000") Then
Foobar_Status = "STOPPED"
Else
Foobar_Status = "PLAYING"
End If
End Function
Sub main()
'Checks to see if the program is connected to the WinLIRC server
'if it isn't it creates a connection.
If mIsConnected = False Then
connect()
mIsConnected = True
End If
Do Until mIsFinished = True
Dim CommandData As String
Do Until CommandData <> ""
CommandData = ProcessSocket()
Loop
'holds the processed data from the winlirc socket
'Calls the function to process the data from the socket opened to the winlirc server
CommandData = ProcessSocket()
'Holds the Command to send to the window
Dim WinampUserCommand As Integer
Select Case CommandData
Case Is = "play"
'play/pause support could probably be implemented with more special handling
'checking to see if foo is playing or not
If Foobar_Status() = "PLAYING" Then
If mFoobarPaused <> True Then
WinampUserCommand = WA_PAUSE
mFoobarPaused = True
Else
WinampUserCommand = WA_PAUSE
mFoobarPaused = False
End If
Else
WinampUserCommand = WA_PLAY
End If
Case Is = "stop"
WinampUserCommand = WA_STOP
Case Is = "next"
WinampUserCommand = WA_NEXT
Case Is = "back"
WinampUserCommand = WA_PREVIOUS
Case Is = "mute"
'This case will require special handling to set the volume to 0 and then reset it to previous levels
'Assuming it will work with foobar...
Case Is = "volu"
WinampUserCommand = WA_VOLUME_UP
Case Is = "vold"
WinampUserCommand = WA_VOLUME_DOWN
Case Is = "+10 "
WinampUserCommand = WA_STOP
mIsFinished = True
Case Else
mIsFinished = True
End Select
Call SendMessage(Winamp_Hwnd(), WM_COMMAND, WinampUserCommand, 0)
Start = Microsoft.VisualBasic.DateAndTime.Timer
Finish = Start + 0.5 ' Set end time for 5-millisecond duration.
Do While Microsoft.VisualBasic.DateAndTime.Timer < Finish
' Do other processing while waiting for 5 seconds to elapse.
Loop
CommandData = Microsoft.VisualBasic.vbNullString
WinampUserCommand = Microsoft.VisualBasic.vbNull
Loop
Shutdown()
End Sub
Sub Shutdown()
'This shuts down the socket and closes it like a good program
'It COULD crash so we use some exception handling
Try
LIRCSocket.Shutdown(System.Net.Sockets.SocketShutdown.Both)
LIRCSocket.Close()
Catch
'MsgBox("something really really bad happened when trying to close the socket...")
End Try
End Sub
End Module
This program has been a lot of learning for me and much of it I still don't fully understand. Sockets being the least understood of the whole program. I have a few bugs left to work out. One of which happens to be in the timers. Any time a timer is run it starts sucking up 100% of my cpu. I need some way for a delay between processing input from the winlirc server that doesn't eat cpu time like mad. Another bug which I think stems somewhat from the way sockets are handled seems to process two seperate inputs and runs them both at the same time, which shouldn't be possible. And yes much of this is hard coded. I have no intention to put the work into this program to make it capable of being configured without having the source code and visual studio.
I'd appreciate any comments you may have on this code or any ways to make it better. It's hard to learn how to do things the right way without some guidance. :)
Also my comments suck.. or they are sucky attempts at humor, just ignore em. :P
Option Strict On
Module Module1
'Global variable describing if the program is active or not.
'will be set by the system tray icon.
Dim gIsActive As Boolean = True
Dim mBytes(1024) As Byte
'Dim mTrayIconCreated As Boolean = False
'Supar sekret MAGICAL MSDN code...
'creates a new socket
Dim LIRCSocket As New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp)
'Resolves a hostname
Dim ipHostInfo As System.Net.IPHostEntry = System.Net.Dns.Resolve("172.16.1.69")
'Does something
Dim ipas As System.Net.IPAddress = ipHostInfo.AddressList(0)
'ipas is the ipaddress of the target machine, 8765 is the socket we want to connect to on the target machine
Dim RemoteIP As New System.Net.IPEndPoint(ipas, 8765)
Dim Start, Finish As Double
'if this is true then we are connected to the lirc server
Public mIsConnected As Boolean
'Holds the last key that was pressed
'Dim mLastKeyPressed As String = ""
'holes the number of times the last key was pressed
'Dim mLastKeyPressedTimes As Integer
'Sets whether or not the program is finished.
Public mIsFinished As Boolean = False
'Volume of Foobar
'Public mVolume As Integer
'Is foobar paused or not? This variable has the answer!
Dim mFoobarPaused As Boolean
#Region "WINAMP API"
' Window Messages to use with SendMessageByString and SendMessageLong.
Public Const WM_COMMAND As Integer = &H111
Public Const WM_USER As Integer = &H400
'' WM_COMMAND Winamp API IDs.
Public Const WA_NEXT As Integer = 40048
Public Const WA_PAUSE As Integer = 40046
Public Const WA_PLAY As Integer = 40045
Public Const WA_PREVIOUS As Integer = 40044
Public Const WA_STOP As Integer = 40047
Public Const WA_VOLUME_DOWN As Integer = 40059
Public Const WA_VOLUME_UP As Integer = 40058
#End Region
' Declare our API calls.
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Private Declare Function apiGetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal sCaption As String, ByVal lCaptionSize As Integer) As Integer
Private Declare Function apiGetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Integer) As Integer
Sub connect()
'I have no clue what the heck this stuff does.
'Stolen from MSDN's Socket stuff
Try
LIRCSocket.Connect(RemoteIP)
Catch
End Try
End Sub
Public Function Winamp_Hwnd() As Integer
' Used to get the handle of the Winamp window.
Winamp_Hwnd = FindWindow("Winamp v1.x", Microsoft.VisualBasic.vbNullString)
End Function
Public Function Foobar_Hwnd() As Integer
' Used to get the handle of the Foobar2000 window.
Foobar_Hwnd = FindWindow("FOOBAR2000_CLASS", Microsoft.VisualBasic.vbNullString)
End Function
Public Function GetWindowCaption(ByRef WhWnd As Integer) As String
'API WRAPPER TO GET WINDOW CAPTION INFO
Dim lLength As Integer
Dim sCaption As String
Dim lRetValue As Integer
'Get length of windows Caption
lLength = apiGetWindowTextLength(WhWnd) + 1
'Create a string of blanks to pass into apiGetWindowText
sCaption = New String(CType(" ", Char), lLength + 1)
'Get Windows Caption
lRetValue = apiGetWindowText(WhWnd, sCaption, lLength)
GetWindowCaption = Microsoft.VisualBasic.Left(sCaption, lLength)
End Function
Public Function ProcessSocket() As String
'this variable holds the string that will be received and stripped from the socket buffer thing
' Dim SocketData As String
Dim BytesRec As Integer
BytesRec = LIRCSocket.Receive(mBytes)
'assign what the remote program sent to a variable
ProcessSocket = (System.Text.Encoding.ASCII.GetString(mBytes, 0, 33))
'This strips out the first 20 characters in the stuff recieved from the socket
ProcessSocket = ProcessSocket.Remove(0, 20)
'This removes anything after the first 4 characters
ProcessSocket = ProcessSocket.Remove(4, (ProcessSocket.Length - 4))
End Function
Function Foobar_Status() As String
Dim FoobarHandle As Integer
Dim FoobarTitle As String
FoobarHandle = Foobar_Hwnd()
FoobarTitle = GetWindowCaption(FoobarHandle)
If FoobarTitle.StartsWith("foobar2000") Then
Foobar_Status = "STOPPED"
Else
Foobar_Status = "PLAYING"
End If
End Function
Sub main()
'Checks to see if the program is connected to the WinLIRC server
'if it isn't it creates a connection.
If mIsConnected = False Then
connect()
mIsConnected = True
End If
Do Until mIsFinished = True
Dim CommandData As String
Do Until CommandData <> ""
CommandData = ProcessSocket()
Loop
'holds the processed data from the winlirc socket
'Calls the function to process the data from the socket opened to the winlirc server
CommandData = ProcessSocket()
'Holds the Command to send to the window
Dim WinampUserCommand As Integer
Select Case CommandData
Case Is = "play"
'play/pause support could probably be implemented with more special handling
'checking to see if foo is playing or not
If Foobar_Status() = "PLAYING" Then
If mFoobarPaused <> True Then
WinampUserCommand = WA_PAUSE
mFoobarPaused = True
Else
WinampUserCommand = WA_PAUSE
mFoobarPaused = False
End If
Else
WinampUserCommand = WA_PLAY
End If
Case Is = "stop"
WinampUserCommand = WA_STOP
Case Is = "next"
WinampUserCommand = WA_NEXT
Case Is = "back"
WinampUserCommand = WA_PREVIOUS
Case Is = "mute"
'This case will require special handling to set the volume to 0 and then reset it to previous levels
'Assuming it will work with foobar...
Case Is = "volu"
WinampUserCommand = WA_VOLUME_UP
Case Is = "vold"
WinampUserCommand = WA_VOLUME_DOWN
Case Is = "+10 "
WinampUserCommand = WA_STOP
mIsFinished = True
Case Else
mIsFinished = True
End Select
Call SendMessage(Winamp_Hwnd(), WM_COMMAND, WinampUserCommand, 0)
Start = Microsoft.VisualBasic.DateAndTime.Timer
Finish = Start + 0.5 ' Set end time for 5-millisecond duration.
Do While Microsoft.VisualBasic.DateAndTime.Timer < Finish
' Do other processing while waiting for 5 seconds to elapse.
Loop
CommandData = Microsoft.VisualBasic.vbNullString
WinampUserCommand = Microsoft.VisualBasic.vbNull
Loop
Shutdown()
End Sub
Sub Shutdown()
'This shuts down the socket and closes it like a good program
'It COULD crash so we use some exception handling
Try
LIRCSocket.Shutdown(System.Net.Sockets.SocketShutdown.Both)
LIRCSocket.Close()
Catch
'MsgBox("something really really bad happened when trying to close the socket...")
End Try
End Sub
End Module