• Welcome to Valhalla Legends Archive.
 

Adding Winamp Support

Started by Moonshine, February 22, 2004, 08:32 PM

Previous topic - Next topic

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.

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.

DarkMinion

But it is neat, I'll give you that  :)

Yoni

#3
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:

// 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;
}


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

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


Edit: Added a few missing definitions.

Moonshine

#4
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).

Stealth

#5
Here's a port to VB. Enjoy. :)

'// Original code by Yoni[vL], thanks to NSDN [Nullsoft]
'// Ported to Visual Basic 6 by Stealth ([email protected])
'// 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


Usage:

sWinampSong = GetCurrentSongTitle() 'no track number
sWinampSong = GetCurrentSongTitle(True) 'track number


[Edit: When prepending the track number it needs to be +1.]
- Stealth
Author of StealthBot