Valhalla Legends Forums Archive | Battle.net Bot Development References | Adding Winamp Support

AuthorMessageTime
Moonshine
I was bored so I decided to make a class in C++ that handles the various Winamp API functions that one would expect to find in a bot's winamp controls. I looked around on this forum and found only messy / non-classed code on it, so here it is:

http://thedragonmaster.net/fatal-error/MoonShine/WinampManager.zip

I included a test prog (main.cpp) so you can see how everything works. This was done in MSVS 2003 .NET, btw -- So if the spacing is all off, I apologize. You can use this code however you want, blah blah blah.. just give me credit please.

Hope this helps save some peoples' time, later.
February 23, 2004, 2:32 AM
DarkMinion
You spent way too much time on that...NullSoft provides a header file with all the constants & you only need to use SendMessage to manipulate it.
February 23, 2004, 4:57 AM
DarkMinion
But it is neat, I'll give you that :)
February 23, 2004, 5:00 AM
Yoni
You're using GetWindowText to get the song title - that's not a good idea, since people may do silly things, like make the title scroll in the taskbar and then you'd be getting eww.

Here is my code for getting the song title:

[code]// The following 2 lines are taken from Nullsoft's header file
#define WM_WA_IPC WM_USER
#define IPC_GETLISTPOS 125
#define IPC_GETPLAYLISTTITLE 212

// The following are mine
#define GetWinampWindow() \
   ((HWND)(FindWindow("Winamp v1.x", NULL)))

// Winamp 2.05+
#define GetWinampListPos(Winamp) \
   ((DWORD)(SendMessage(((HWND)(Winamp)), WM_WA_IPC, 0, IPC_GETLISTPOS)))

// Winamp 2.04+, pointer is in Winamp address space
#define GetWinampSongTitleRemote(Winamp, Index) \
   ((char *)(SendMessage(((HWND)(Winamp)), WM_WA_IPC, ((WPARAM)(Index)), IPC_GETPLAYLISTTITLE)))

char *GetWinampSongTitleLocal(HWND Winamp, DWORD Index) // Winamp 2.04+, pointer is in local address space
{
   static char SongTitle[1024];

   DWORD WinampProcessID;
   HANDLE WinampProcess;
   char *SongTitleRemote;
   char *Ret = NULL; // Return value

   // Get process ID
   GetWindowThreadProcessId(Winamp, &WinampProcessID);

   // Open process
   if(WinampProcess = OpenProcess(PROCESS_VM_READ, FALSE, WinampProcessID)) {
      // Get pointer
      if(SongTitleRemote = GetWinampSongTitleRemote(Winamp, Index))
         // Try to read it
         if(ReadProcessMemory(WinampProcess, SongTitleRemote, SongTitle, sizeof(SongTitle), NULL))
            // Success
            Ret = SongTitle;

      CloseHandle(WinampProcess);
   }

   return Ret;
}[/code]

Note it uses a static buffer, so use with care or modify as needed.

Example for getting the current song's title:
[code]HWND Winamp = GetWinampWindow();
Temp = GetWinampSongTitleLocal(Winamp, GetWinampListPos(Winamp));
if(Temp)
strcpy(CurrentSongTitle, Temp);[/code]

Edit: Added a few missing definitions.
February 23, 2004, 6:05 AM
Moonshine
Heh, I also figured it would be a bad idea to use GetWindowText as well, but believe it or not I got the code from winamp's development site itself. However I agree your way is more "Idiot proof," so I'll add that to my code (I'll leave it up to people to change the zip's version if they feel the need, the getwindowtext way should work in theory, since it's preached by nullsoft themselves).
February 23, 2004, 4:53 PM
Stealth
Here's a port to VB. Enjoy. :)

[code]'// Original code by Yoni[vL], thanks to NSDN [Nullsoft]
'// Ported to Visual Basic 6 by Stealth (stealth@stealthbot.net)
'// Thanks to Skywing[vL], Kp and thuscelackpiss who helped me straighten out ReadProcessMemory

'// modWinampTitle.bas
Option Explicit

'// Constants
Private Const WM_USER& = &H400
Private Const WM_WA_IPC = WM_USER
Private Const IPC_GETLISTPOS& = 125
Private Const IPC_GETPLAYLISTTITLE& = 212
Private Const PROCESS_VM_READ = (&H10)

'// API declarations
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _
ByVal blnheritHandle As Long, _
ByVal dwAppProcessId As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, _
ByVal lpBaseAddress As Long, _
ByVal lpBuffer As String, ByVal nSize As Long, _
ByRef lpNumberOfBytesWritten As Long) As Long

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, _
ByRef lpdwProcessId As Long) As Long

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

'// The following are mine (Yoni's ;)
Private Function GetWinampWindow() As Long
GetWinampWindow = (FindWindow("Winamp v1.x", vbNullString))
End Function

'// Winamp 2.05+
Private Function GetWinampListPos(ByVal hWndWinamp As Long) As Long
GetWinampListPos = SendMessage(hWndWinamp, WM_WA_IPC, 0&, IPC_GETLISTPOS)
End Function

'// Winamp 2.04+, pointer is in Winamp address space
Private Function GetWinampSongTitleRemote(ByVal hWndWinamp As Long, ByVal Index As Long) As Long
GetWinampSongTitleRemote = SendMessage(hWndWinamp, WM_WA_IPC, Index, IPC_GETPLAYLISTTITLE)
End Function

Private Function GetWinampSongTitleLocal(ByVal hWndWinamp As Long, ByVal Index As Long) As String '// Winamp 2.04+, pointer is in local address space

Dim SongTitle As String
Dim WinampProcessID As Long
Dim WinampProcessHandle As Long
Dim ProcessHandle As Long
Dim SongTitleRemote As Long
Dim Ret As String

SongTitle = String(1024, vbNullChar)

'// Get process ID
Call GetWindowThreadProcessId(hWndWinamp, WinampProcessID)

'// Open process
ProcessHandle = OpenProcess(PROCESS_VM_READ, False, WinampProcessID)

If (ProcessHandle > 0) Then
'// Get pointer
SongTitleRemote = GetWinampSongTitleRemote(hWndWinamp, Index)

If (SongTitleRemote > 0) Then
'// Try to read it
If (ReadProcessMemory(ProcessHandle, SongTitleRemote, SongTitle, ByVal Len(SongTitle), 0&) > 0) Then
'// Success
Ret = Left$(SongTitle, InStr(1, SongTitle, Chr(0)) - 1)
End If
End If

Call CloseHandle(ProcessHandle)
End If

GetWinampSongTitleLocal = Ret
End Function

Public Function GetCurrentSongTitle(Optional ShowTrackNumber As Boolean = False) As String
Dim hWnd As Long
Dim sBuf As String

hWnd = GetWinampWindow()
sBuf = GetWinampSongTitleLocal(hWnd, GetWinampListPos(hWnd))

If ShowTrackNumber Then
sBuf = (GetWinampListPos(hWnd) + 1) & ": " & sBuf
End If

GetCurrentSongTitle = sBuf
End Function
[/code]

Usage:
[code]
sWinampSong = GetCurrentSongTitle() 'no track number
sWinampSong = GetCurrentSongTitle(True) 'track number
[/code]

[Edit: When prepending the track number it needs to be +1.]
February 24, 2004, 12:00 AM

Search