Code:
#include <Windows.h>
#include <Shlwapi.h>
#include <vector>
#include <tchar.h>
#pragma comment(lib, "Winmm.lib")
#pragma comment(lib, "Shlwapi.lib")
struct MusicPlayerEntry
{
LPTSTR SongName;
LPTSTR Path;
};
class MusicPlayer
{
public:
static std::vector<MusicPlayerEntry*> Entries;
static void PopulateMusic(LPTSTR directory, LPTSTR extension);
static void Cleanup();
static bool IsPlaying();
static bool IsPaused();
static void PlayMusic(MusicPlayerEntry* entry);
static void PauseMusic();
static void ResumeMusic();
static void StopMusic();
static void SetVolume(int value);
static int GetCurrentPosition();
static void SetCurrentPosition(int position);
static void FormatMilliseconds(int milliseconds, LPTSTR output, size_t bufferSize);
static int LengthMilliseconds, Volume;
private:
static bool AliasDefined;
static void AddEntry(LPTSTR filePath, LPTSTR songName);
static HMODULE GetCurrentModuleHandle();
static BOOL DirectoryExists(LPTSTR directory);
};
player.cpp
Code:
#include "player.h"
std::vector<MusicPlayerEntry*> MusicPlayer::Entries;
int MusicPlayer::LengthMilliseconds = 0;
int MusicPlayer::Volume = 1000;
bool MusicPlayer::AliasDefined = false;
void MusicPlayer::PopulateMusic(LPTSTR directory, LPTSTR extension)
{
if(PathIsRelative(directory))
{
TCHAR buffer[MAX_PATH];
GetModuleFileName(GetCurrentModuleHandle(), buffer, MAX_PATH);
PathRemoveFileSpec(buffer);
PathCombine(buffer, buffer, directory);
directory = buffer;
}
if(!DirectoryExists(directory))
return;
TCHAR wildcardPath[MAX_PATH];
PathCombine(wildcardPath, directory, _T("*"));
WIN32_FIND_DATA findData;
HANDLE findHandle = FindFirstFile(wildcardPath, &findData);
do
{
LPTSTR currentExtension = PathFindExtension(findData.cFileName);
if(!lstrcmp(currentExtension, extension))
{
LPTSTR path = new TCHAR[MAX_PATH];
PathCombine(path, directory, findData.cFileName);
LPTSTR songName = new TCHAR[MAX_PATH];
lstrcpy(songName, findData.cFileName);
PathStripPath(songName);
PathRemoveExtension(songName);
AddEntry(path, songName);
}
} while (FindNextFile(findHandle, &findData) != 0);
}
void MusicPlayer::Cleanup()
{
StopMusic();
for(unsigned int i = 0; i < Entries.size(); i++)
{
delete Entries[i]->SongName;
delete Entries[i]->Path;
}
Entries.clear();
}
HMODULE MusicPlayer::GetCurrentModuleHandle()
{
static int var = 0;
MEMORY_BASIC_INFORMATION mbi;
if(!VirtualQuery(&var, &mbi, sizeof(mbi)))
return NULL;
return static_cast<HMODULE>(mbi.AllocationBase);
}
void MusicPlayer::FormatMilliseconds(int milliseconds, LPTSTR output, size_t bufferSize)
{
_stprintf_s(output, bufferSize, _T("%d:%02d"), milliseconds / 1000 / 60, milliseconds / 1000 % 60);
}
bool MusicPlayer::IsPlaying()
{
TCHAR buffer[20];
mciSendString(_T("status currentSong mode"), buffer, 20, NULL);
return !lstrcmp(buffer, _T("playing"));
}
bool MusicPlayer::IsPaused()
{
TCHAR buffer[20];
mciSendString(_T("status currentSong mode"), buffer, 20, NULL);
return !lstrcmp(buffer, _T("paused"));
}
void MusicPlayer::PlayMusic(MusicPlayerEntry* entry)
{
StopMusic();
TCHAR buffer[MAX_PATH + 40];
_stprintf_s(buffer, _T("open \"%s\" type mpegvideo alias currentSong"), entry->Path);
mciSendString(buffer, NULL, 0, NULL);
mciSendString(_T("play currentSong"), NULL, 0, NULL);
TCHAR length[128];
mciSendString(_T("status currentSong length"), length, 128, NULL);
LengthMilliseconds = _ttoi(length);
AliasDefined = true;
}
void MusicPlayer::PauseMusic()
{
if(IsPlaying())
mciSendString(_T("pause currentSong"), NULL, 0, NULL);
}
void MusicPlayer::ResumeMusic()
{
if(IsPaused())
mciSendString(_T("resume currentSong"), NULL, 0, NULL);
}
void MusicPlayer::StopMusic()
{
if(AliasDefined)
mciSendString(_T("close currentSong"), NULL, 0, NULL);
}
int MusicPlayer::GetCurrentPosition()
{
if(IsPlaying())
{
TCHAR length[128];
mciSendString(_T("status currentSong position"), length, 128, NULL);
return _ttoi(length);
}
return 0;
}
void MusicPlayer::SetCurrentPosition(int position)
{
if(IsPlaying())
{
TCHAR buffer[60];
_stprintf_s(buffer, _T("play currentSong from %d"), position);
mciSendString(buffer, NULL, 0, NULL);
}
}
void MusicPlayer::SetVolume(int value)
{
if(IsPlaying() && value >= 0 && value <= 1000)
{
TCHAR buffer[40];
_stprintf_s(buffer, _T("setaudio currentSong volume to %i"), value);
mciSendString(buffer, NULL, 0, NULL);
Volume = value;
}
}
BOOL MusicPlayer::DirectoryExists(LPTSTR dirName)
{
DWORD attribs = GetFileAttributes(dirName);
if (attribs == INVALID_FILE_ATTRIBUTES)
return false;
return (attribs & FILE_ATTRIBUTE_DIRECTORY);
}
void MusicPlayer::AddEntry(LPTSTR filePath, LPTSTR songName)
{
MusicPlayerEntry* entry = new MusicPlayerEntry();
entry->SongName = songName;
entry->Path = filePath;
Entries.push_back(entry);
}
Here's a little "Demo DLL" source that you can try out to test some of the class' features.
Code:
#include <Windows.h>
#include "player.h"
DWORD WINAPI Main(LPVOID)
{
unsigned int currentEntryIndex = 0;
// Find all .mp3 files inside the Music folder next to the DLL.
MusicPlayer::PopulateMusic(_T("Music"), _T(".mp3"));
// Check if there were any .mp3 files found.
if(!MusicPlayer::Entries.size()) return 0;
while(true)
{
// If no song is playing, play one.
if(!MusicPlayer::IsPlaying())
{
// Check that we've played all songs.
if(currentEntryIndex < MusicPlayer::Entries.size())
{
// Play the next song available.
MusicPlayerEntry* entry = MusicPlayer::Entries[currentEntryIndex++];
MusicPlayer::PlayMusic(entry);
// Display song information.
TCHAR buffer[255];
TCHAR formattedTime[20];
// Convert the milliseconds to minutes and seconds.
MusicPlayer::FormatMilliseconds(MusicPlayer::LengthMilliseconds, formattedTime, 20);
_stprintf_s(buffer, _T("Current song is: %s\nLength: %s"), entry->SongName, formattedTime);
MessageBox(NULL, buffer, _T("Information"), MB_OK);
}
else
{
// Exit.
MessageBox(NULL, _T("All songs have been played!"), _T("Done"), MB_OK);
break;
}
}
Sleep(1000);
}
return 0;
}
BOOL APIENTRY DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hDll);
CreateThread(NULL, NULL, Main, NULL, NULL, NULL);
break;
case DLL_PROCESS_DETACH:
// Cleanup stuff here, only do it when the player is no longer needed.
MusicPlayer::Cleanup();
}
return TRUE;
}
I've attached the built Demo DLL which you can inject into any process and mess around with.