You are on page 1of 12

Controlar El Joystick Desde Visual Basic con el API

Este ejemplo puede detectar los joystick instalados en el sistema y si


están conectados. También saber cuantos botones y ejes tiene. Con el
apoyo de graficas y sonido muestra el estado del joystick seleccionado.
Produce diferentes sonidos cuando se presionan los botones o se mueven
las palancas del joystick y/o pad. Muestra los valores que detecta por
medio del registro de consulta, en este caso el JOYINFOEX de la función
joyGetPosEx. Se puede cambiar ID del joystick en tiempo de ejecución si
hay más de uno conectados al sistema.
Para el desarrollo del mismo utilicé varias funciones:
joyGetPosEx
joyGetDevCaps
joyGetNumDevs
Con sus respectivas estructuras de registros:
JOYINFOEX
JOYCAPS

1
Adicionalmente utilicé otra función del API para reproducir archivos WAV,
por ejemplo: Al pulsar un botón del joystick.
sndPlaySound

Código fuente
frmJoystick

Option Explicit
'*******************************************************************
'Title : How work with some Joystick API functions
' (For Learning Uses)
'Author : DrakerDG
'Date : 09-05-2005
'e-mail : drakerdg@yahoo.com
'*******************************************************************

Private Sub Form_Load()


'Init values
xZbk = -2000
xPOV = JOY_POVCENTERED
cmbJoy.Tag = "-1"
DrawMk 'Go to DrawMk Sub...
DrawOd 'Go to DrawOd Sub...
InitJoy 'Go to InitJoy Sub...
End Sub
Private Sub Form_Resize()
If frmJoystick.WindowState = 0 Then
Sound MPathX(App.Path) & "snd\Up.wav", _
SND_ASYNC + SND_NODEFAULT
Else
Sound MPathX(App.Path) & "snd\Dn.wav", _
SND_ASYNC + SND_NODEFAULT
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
End 'Exit App
End Sub

Private Sub mnuCJoy_Click()


'Go to Game Device Window
Dim Fso, Fbt, Fex As Variant
On Error Resume Next
Set Fso = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
'Create .bat file
Set Fbt = Fso.CreateTextFile(MPathX(App.Path) & "Joy.bat", True)
'Add line code to Joy.bat file
Fbt.WriteLine ("joy.cpl")
Fbt.Close
'Execute the .bat file to get Game Device Window
Fex = Shell(MPathX(App.Path) & "Joy.bat", vbHide)
End Sub
Private Sub mnuExit_Click()
On Error Resume Next
Kill (MPathX(App.Path) & "Joy.bat") 'Erase the .bat file
End

End Sub

2
Private Sub tbrJoy_ButtonClick(ByVal Button As MSComctlLib.Button)
'Toolbar click event
Select Case Button.Index
Case 3
mnuExit_Click
Case 5
mnuCJoy_Click
End Select
End Sub
Private Sub tmrScan_Timer()
'The scan joystick sub
Dim IDJoy As Long 'ID Joystick Value

With InfoJoyEX
DisButtX cmbJoy.ListIndex
IDJoy = joyGetPosEx(cmbJoy.ListIndex, InfoJoyEX) 'Get joystick
info.
stbInf.Panels(1).Text = " Joystick Status: " & JoyEst(IDJoy)
'Update the status bar
If .dwPOV <> Val(lblInfoEX(10).Caption) Then
bPOV = True
End If
'*** Update labels with InfoJoyEX ***
lblInfoEX(13).Caption = Str(IDJoy)
lblInfoEX(0).Caption = .dwSize
lblInfoEX(1).Caption = .dwFlags
lblInfoEX(2).Caption = .dwXpos
lblInfoEX(3).Caption = .dwYpos
lblInfoEX(4).Caption = .dwZpos
lblInfoEX(5).Caption = .dwRpos
lblInfoEX(6).Caption = .dwUpos
lblInfoEX(7).Caption = .dwVpos
lblInfoEX(8).Caption = .dwButtons
lblInfoEX(9).Caption = .dwButtonNumber
lblInfoEX(10).Caption = .dwPOV
lblInfoEX(11).Caption = .dwReserved1
lblInfoEX(12).Caption = .dwReserved2

'*** Update graphical controls ***


ButtonsX .dwButtons
DrawAr .dwZpos
DrawCz 1, .dwXpos, .dwYpos
DrawCz 2, .dwRpos, .dwUpos
DrawCd .dwPOV

End With
End Sub

'****************************************
'<<<< Personalized Sub-procedures. >>>>
'****************************************
Public Sub ButtonsX(ByVal xBs As Long)
'Init buttons scan procedure...
'For 1 to 32 buttons.
SetButtX xBs, 0 'Go to SetButX Sub...
SetButtX xBs, 1
SetButtX xBs, 2
SetButtX xBs, 3
SetButtX xBs, 4
SetButtX xBs, 5

3
SetButtX xBs, 6
SetButtX xBs, 7
SetButtX xBs, 8
SetButtX xBs, 9
SetButtX xBs, 10
SetButtX xBs, 11
SetButtX xBs, 12
SetButtX xBs, 13
SetButtX xBs, 14
SetButtX xBs, 15
SetButtX xBs, 16
SetButtX xBs, 17
SetButtX xBs, 18
SetButtX xBs, 19
SetButtX xBs, 20
SetButtX xBs, 21
SetButtX xBs, 22
SetButtX xBs, 23
SetButtX xBs, 24
SetButtX xBs, 25
SetButtX xBs, 26
SetButtX xBs, 27
SetButtX xBs, 28
SetButtX xBs, 29
SetButtX xBs, 30
SetButtX xBs, 31
End Sub
Public Sub DisButtX(ByVal xNbu As Integer)
'Set Enebled o Disabled the joystick buttons
Dim xRj As Long
Dim xCo As Integer
If (xNbu >= 0) And (xNbu <> Val(cmbJoy.Tag)) Then
xRj = joyGetDevCaps(cmbJoy.ListIndex, CapX, Len(CapX)) 'With
joyGetDevCaps function
For xCo = 0 To 31
If xCo < CapX.wNumButtons Then
Butt(xCo).BackColor = G01
Butt(xCo).BorderColor = BLK
NoX(xCo).ForeColor = BLK
Else
Butt(xCo).BackColor = G02
Butt(xCo).BorderColor = G01
NoX(xCo).ForeColor = G01
End If
Next xCo
cmbJoy.Tag = Trim(Str(xNbu))
End If
End Sub
Public Sub DrawAr(ByVal dwRes As Long)
'Move de arrow of the Z Axis and play sound (scroll left or scroll
right)
Dim Med, R1, R2, Ang As Integer
If dwRes <> xZbk Then
Med = Int(Joy(3).ScaleWidth / 2)
R1 = Joy(3).ScaleWidth - 25
R2 = Joy(3).ScaleWidth - 33
Joy(3).Cls
DrawOd
Joy(3).Scale (-1 * Med, R1 + 15)-(Med, -15)
Ang = Int(50 + dwRes * 80 / 65535)
Joy(3).DrawWidth = 2

4
Joy(3).Line (0, 0)- _
(R1 * Cos(Ang * PI / 180), R1 * Sin(Ang * PI / 180)),
R01
Ang = Ang + 180
Joy(3).Line (0, 0)- _
(10 * Cos(Ang * PI / 180), 10 * Sin(Ang * PI / 180)),
R01
Joy(3).Circle (0, 0), 5, BLK
If ((dwRes - SenX) > xZbk) Then
If InSn Then
InSn = False
Sound MPathX(App.Path) & "snd\Dn.wav", _
SND_ASYNC + SND_NODEFAULT
End If
End If
If ((dwRes + SenX) < xZbk) Then
If InSn Then
InSn = False
Sound MPathX(App.Path) & "snd\Up.wav", _
SND_ASYNC + SND_NODEFAULT
End If
End If
Joy(3).DrawWidth = 1
Else
InSn = True
End If
xZbk = dwRes
End Sub
Public Sub DrawCd(ByVal dwPO As Long)
'Select what direction in POV is pressed
Dim xCa As Integer
If dwPO <> xPOV Then
For xCa = 0 To 7
If Arw(xCa).Visible Then
Arw(xCa).Visible = False
End If
Next xCa

Select Case dwPO


Case JOY_POVFORWARD 'Dec 0
DrawPOV 0
Case JOY_POVFRDRHT 'Dec 4500
DrawPOV 1
Case JOY_POVRIGHT 'Dec 9000
DrawPOV 2
Case JOY_POVBRDRHT 'Dec 13500
DrawPOV 3
Case JOY_POVBACKWARD 'Dec 18000
DrawPOV 4
Case JOY_POVBRDLFT 'Dec 22500
DrawPOV 5
Case JOY_POVLEFT 'Dec 27000
DrawPOV 6
Case JOY_POVFRDLFT 'Dec 31500
DrawPOV 7
End Select
End If
xPOV = dwPO

End Sub
Public Sub DrawCz(ByVal xInd As Integer, ByVal Xval As Long, ByVal
Yval As Long)

5
'Show de "+" pointer position for the X+Y Axis an U+R Axis.
Dim Xwi, Yhe As Long
Xwi = Int(Xval * (Joy(xInd).ScaleWidth - 1) / 65535)
Yhe = Int(Yval * (Joy(xInd).ScaleHeight - 1) / 65535)
Joy(xInd).Cls

Joy(xInd).Line (Xwi - 10, Yhe)- _


(Xwi + 10, Yhe), R01
Joy(xInd).Line (Xwi, Yhe - 10)- _
(Xwi, Yhe + 10), R01
If Xval < XY4D Then
If bXYs(xInd, 0) Then
Sound MPathX(App.Path) & "snd\Ax.wav", _
SND_ASYNC + SND_NODEFAULT
bXYs(xInd, 0) = False
End If
Else
bXYs(xInd, 0) = True
End If
If Xval > XY4U Then
If bXYs(xInd, 1) Then
Sound MPathX(App.Path) & "snd\Ax.wav", _
SND_ASYNC + SND_NODEFAULT
bXYs(xInd, 1) = False
End If
Else
bXYs(xInd, 1) = True
End If
If Yval < XY4D Then
If bXYs(xInd, 2) Then
Sound MPathX(App.Path) & "snd\Ax.wav", _
SND_ASYNC + SND_NODEFAULT
bXYs(xInd, 2) = False
End If
Else
bXYs(xInd, 2) = True
End If
If Yval > XY4U Then
If bXYs(xInd, 3) Then
Sound MPathX(App.Path) & "snd\Ax.wav", _
SND_ASYNC + SND_NODEFAULT
bXYs(xInd, 3) = False
End If
Else
bXYs(xInd, 3) = True
End If
End Sub
Public Sub DrawMk()
'Draw the POV-Graph marks ans set the arrow positions.
Dim Med, R1, R2, Ang As Integer
Med = Int(Joy(0).ScaleWidth / 2)
R1 = Med - 10
R2 = Med - 2
Joy(0).Scale (-1 * Med, Med)-(Med, -1 * Med)
For Ang = 0 To 360 Step 45
Joy(0).Line (R1 * Cos(Ang * PI / 180), R1 * Sin(Ang * PI /
180))- _
(R2 * Cos(Ang * PI / 180), R2 * Sin(Ang * PI /
180)), BLK
Next Ang

6
Arw(0).Move R1 * Cos(90 * PI / 180) - 1 * Arw(0).Width / 2, R1 *
Sin(90 * PI / 180)
Arw(1).Move R1 * Cos(45 * PI / 180) - 1 * Arw(0).Width, R1 * Sin(45
* PI / 180)
Arw(2).Move R1 * Cos(0) - 1 * Arw(0).Width, R1 * Sin(0) + 1 *
Arw(0).Width / 2
Arw(3).Move R1 * Cos(315 * PI / 180) - 1 * Arw(0).Width, R1 *
Sin(315 * PI / 180) + Arw(0).Width
Arw(4).Move R1 * Cos(270 * PI / 180) - 1 * Arw(0).Width / 2, R1 *
Sin(270 * PI / 180) + Arw(0).Width
Arw(5).Move R1 * Cos(225 * PI / 180), R1 * Sin(225 * PI / 180) +
Arw(0).Width
Arw(6).Move R1 * Cos(180 * PI / 180), Arw(0).Width / 2
Arw(7).Move R1 * Cos(135 * PI / 180), R1 * Sin(135 * PI / 180)
Joy(0).AutoRedraw = False
End Sub
Public Sub DrawOd()
'Draw the Z-Graph marks.
Dim Med, R1, R2, Ang As Integer
Med = Int(Joy(3).ScaleWidth / 2)
R1 = Joy(3).ScaleWidth - 25
R2 = Joy(3).ScaleWidth - 33
Joy(3).Scale (-1 * Med, R1 + 15)-(Med, -15)
For Ang = 50 To 130 Step 10
Joy(3).Line (R1 * Cos(Ang * PI / 180), R1 * Sin(Ang * PI /
180))- _
(R2 * Cos(Ang * PI / 180), R2 * Sin(Ang * PI /
180)), _
IIf(Ang < 80, R01, BLK)
Next Ang
'Joy(3).AutoRedraw = False
End Sub
Public Sub DrawPOV(ByVal xInd As Integer)
'Show the arrow in the POV
If Not (Arw(xInd).Visible) Then
Arw(xInd).Visible = True
End If
If bPOV Then
bPOV = False
Sound MPathX(App.Path) & "snd\XY.wav", _
SND_ASYNC + SND_NODEFAULT
End If

End Sub
Public Sub InitJoy()
'Get the joystick number in the system and about information.
Dim xJa As Long
Dim xRj As Long
Dim IDJk As Long
Dim xJn, Lin, Lsu As Integer
Dim sDJ As String
SIDJ(1) = -1
SIDJ(2) = -1
InfoJoyEX.dwFlags = JOY_RETURNALL 'Dec 128: Request for status
buttons.
InfoJoyEX.dwSize = &H40 'Dec 64
xJa = joyGetNumDevs() - 1
ReDim AIDJ(0 To xJa, 0 To 1) As Integer

7
x = "No of joysticks: " & Str(xJa) & ". " & Str(UBound(AIDJ)) &
Chr(13)
For xJn = 0 To UBound(AIDJ)
xRj = joyGetDevCaps(xJn, CapX, Len(CapX))
x.Caption = x.Caption & Str(xRj) & ", "
sDJ = "Joistick " & Trim(Str(xJn + 1))
Select Case xRj
Case JOYERR_NOERROR
sDJ = sDJ & Str(CapX.wNumAxes) & " Axes"
sDJ = sDJ & Str(CapX.wNumButtons) & " Buttons"
IDJk = joyGetPosEx(xJn, InfoJoyEX)
sDJ = sDJ & " " & JoyEst(IDJk)
AIDJ(xJn, 0) = IDJk
Case Else
AIDJ(xJn, 0) = xRj
sDJ = sDJ & " (Not Driver Instaled)"
End Select
'AIDJ(xJn, 1) = CapX.wNumAxes
'AIDJ(xJn, 2) = CapX.wNumButtons
'AIDJ(xJn, 3) = CapX.wPid
cmbJoy.AddItem sDJ
Next xJn
cmbJoy.ListIndex = 0
End Sub

Public Sub SetButtX(ByVal xBi As Integer, ByVal nInd As Integer)


'Set the status button and play sound only one time.
If Val(Mid(DecBin(xBi, FSIZE), FSIZE - nInd, 1)) = 1 Then
If Butt(nInd).BackColor = G01 Then
Butt(nInd).BackColor = R01
End If
If BnSn(nInd) Then '
BnSn(nInd) = False
Sound MPathX(App.Path) & "snd\s" & IIf(nInd < 9,
Trim(Str(nInd)), "X") & ".wav", _
SND_ASYNC + SND_NODEFAULT
End If
Else
If Butt(nInd).BackColor = R01 Then
Butt(nInd).BackColor = G01
End If
BnSn(nInd) = True
End If
End Sub

Public Sub GetSetJK(ByVal Index As Integer)


Dim xRj As Long 'Return function joyGetDevCaps
Dim IDJk As Long 'Return function JoyGetPosEx
Dim sDJ As String 'Strin to listbox
Dim iCn As Integer 'For Next counter
Dim iAr As Integer 'Array counter

Dim iCn As Integer


For iCn = 0 To UBound(AIDJ)
xRj = joyGetDevCaps(xJn, CapX, Len(CapX))
Select Case xRj
Case JOYERR_NOERROR
sDJ = "Joistick " & Trim(Str(iCn + 1)) 'Write
Joystick number
sDJ = sDJ & Str(CapX.wNumAxes) & " Axes" 'Write
Axes number
sDJ = sDJ & Str(CapX.wNumButtons) & " Buttons" 'Write

8
Buttons number
IDJk = joyGetPosEx(iCn, InfoJoyEX) 'Get
Joystick status
sDJ = sDJ & " " & JoyEst(IDJk) 'Write
Joystick status
AIDJ(iAr, 0) = IDJk
cmbJoy.AddItem sDJ
Case Else
AIDJ(xJn, 0) = -1
End Select
Next iCn
End Sub

modJoystick

Option Explicit
'*** Color constants ***
Public Const R01 = &HFF 'Red
Public Const G01 = &H808080 'Gray
Public Const G02 = &HC0C0C0 'Ligth Gray
Public Const BLK = &H0 'Black
'*** Math constants ***
Public Const PI = 3.14159265358979
'*** File constants ***
Public Const FSIZE = 32
'*** joyGetPosEx Constants ***
Public Const JOY_POVCENTERED = &HFFFF
Public Const JOY_POVFORWARD = &H0
Public Const JOY_POVFRDRHT = &H1194
Public Const JOY_POVRIGHT = &H2328
Public Const JOY_POVBRDRHT = &H34BC
Public Const JOY_POVBACKWARD = &H4650
Public Const JOY_POVBRDLFT = &H57E4
Public Const JOY_POVLEFT = &H6978
Public Const JOY_POVFRDLFT = &H7B0C
Public Const JOY_RETURNALL = &HFF
'*** sndPlaySound constants ***
Public Const SND_ASYNC = &H1
Public Const SND_NODEFAULT = &H2
Public Const SND_SYNC = &H0
'*** Calibrate constants ***
Public Const SenX = 1023 ' Sensitivity range to POV.
Public Const XY4D = 16383 ' Sensitivity Down limit to
X+Y Axis.
Public Const XY4U = 49152 ' Sensitivity Up limit to X+Y
Axis.
'*** Joystick constants ***
Public Const MMSYSERR_BASE As Long = &H0
Public Const MMSYSERR_NODRIVER = (MMSYSERR_BASE + 6) ' No
device driver present
Public Const MMSYSERR_INVALPARAM = (MMSYSERR_BASE + 11) ' Invalid
parameter passed
Public Const JOYERR_NOERROR = &H0 ' No error
Public Const JOYERR_BASE = &HA0
Public Const JOYERR_PARMS = (JOYERR_BASE + 5) ' Bad ID
parameters
Public Const JOYERR_NOCANDO = (JOYERR_BASE + 6) ' Request not

9
completed
Public Const JOYERR_UNPLUGGED = (JOYERR_BASE + 7) ' Joystick is
unplugged
'*** Joystick registers ***
Type JOYINFOEX
dwSize As Long ' size of structure
dwFlags As Long ' flags to indicate what to
return
dwXpos As Long ' x position
dwYpos As Long ' y position
dwZpos As Long ' z position
dwRpos As Long ' rudder/4th axis position
dwUpos As Long ' 5th axis position
dwVpos As Long ' 6th axis position
dwButtons As Long ' button states
dwButtonNumber As Long ' current button number
pressed
dwPOV As Long ' point of view state
dwReserved1 As Long ' reserved for communication
between winmm driver
dwReserved2 As Long ' reserved for future
expansion
End Type

Type JOYCAPS
wMid As Integer ' Manufacturer identifier of the
device driver for the MIDI output device
wPid As Integer ' Product Identifier Product of the
MIDI output device.
szPname As String * 32
wXmin As Long
wXmax As Long
wYmin As Long
wYmax As Long
wZmin As Long
wZmax As Long
wNumButtons As Long
wPeriodMin As Long ' Smallest polling frequency
supported when captured by the joySetCapture function.
wPeriodMax As Long ' Largest polling frequency supported
when captured by the joySetCapture function.
wRmin As Long
wRmax As Long
wUmin As Long
wUmax As Long
wVmin As Long
wVmax As Long
wCaps As Long
wMaxAxes As Long
wNumAxes As Long
wMaxButtons As Long
szRegKey As String * 32
szOEMVxD As String * 260
End Type
'*** Joystick API functions ***
Declare Function joyGetPosEx Lib "WinMM.dll" (ByVal uJoyID As Long, _
ByRef pji As JOYINFOEX) As Long
Declare Function joyGetDevCaps Lib "WinMM.dll" Alias "joyGetDevCapsA"
_
(ByVal ID As Long, ByRef lpCaps As JOYCAPS, ByVal
uSize As Long) As Long
Declare Function joyGetNumDevs Lib "WinMM.dll" () As Long
Declare Function sndPlaySound Lib "WinMM.dll" Alias "sndPlaySoundA" _
(ByVal lpszSoundName As String, ByVal uFlags As Long)
As Long

10
Declare Function joySetCapture Lib "WinMM.dll" ( _
ByVal hWnd As Long, ByVal uJoyID As Long, _
ByVal uPeriod As Long, ByVal fChanged As Long) As
Long
Declare Function joyReleaseCapture Lib "WinMM.dll" ( _
ByVal uJoyID As Long) As Long

'*** Public Vars ***


Public InfoJoyEX As JOYINFOEX 'Info-Joystick Register
Public CapX As JOYCAPS 'Caps-Joystick Register
Public AIDJ() As Integer 'All ID Joystick matrix
Public SIDJ(1 To 2) As Integer 'ID Joystick selected
Public xZbk As Long 'BackUp key of Z position
Public xPOV As Long 'BackUp key of POV position
Public InSn As Boolean 'Play Sound key of Z change position
Public BnSn(0 To 31) As Boolean 'Play Sound key of Button pressed
(binary string)
Public bPOV As Boolean 'Play sound key of POV status
Public bXYs(0 To 3, 0 To 3) As Boolean 'Play sound key of XY status
'********************************************************************
************

Public Sub Sound(ByVal SName As String, ByVal F1 As Long)


'Play sound procedure
Dim ID As Long
ID = sndPlaySound(SName, F1)
End Sub
Function DecBin(ByVal decim As Long, ByVal tam As Integer) As String
'*** Convert integer number to binary string ***
Dim co As Integer, binar As String
binar = ""
Do Until decim = 0
If (decim Mod 2) = 1 Then
binar = "1" + binar
Else
binar = "0" + binar
End If
decim = decim \ 2
Loop
For co = (Len(binar) + 1) To tam
binar = "0" + binar
Next co
DecBin = binar
End Function
Public Function JoyEst(ByVal IDx As Long) As String
'Indentify the Joystick status.
Dim xRes As String
Select Case IDx
Case JOYERR_NOERROR
xRes = "Ready"
Case MMSYSERR_NODRIVER
xRes = "No device driver present"
Case MMSYSERR_INVALPARAM
xRes = "Invalid parameter passed"
Case JOYERR_PARMS
xRes = "Invalid ID"
Case JOYERR_NOCANDO
xRes = "Request not completed"
Case JOYERR_UNPLUGGED

11
xRes = "Unplugged"
Case Else
xRes = "Unknown"
End Select
JoyEst = "(" & xRes & ") " & IDx 'Cambio
End Function
Public Function MPathX(ByVal sPhX As String) As String
'Add inverted slash if this not exist
Dim sPhY As String
sPhY = sPhX
If Mid(sPhY, Len(sPhY), 1) <> "\" Then
sPhY = sPhY & "\"
End If
MPathX = sPhY
End Function

Puede descargar el ejemplo del código de la siguiente dirección:


http://files.myopera.com/DrakerDG/ZIP/JoystickEX.zip

12