/*********************\ /*********************\ /*********************\
|** **||** **||** **|
|* WinAmp DDE Plugin *||* WinAmp DDE Plugin *||* WinAmp DDE Plugin *|
|** *||* *||* **|
\***********************|* Version: 0.1a *|***********************/
/* Coder: Skizo *\
|* Date: 27/MaY/2oo5 *|
|* *|
\*************************/
//
// ### BEFORE YOU START READING THIS ###
//
//
// I feel the need to tell you something, that probably
// nobody told you before. When you comment the code
// you have to write WHAT it does or it should do, and
// NOT HOW it does so. The HOW thing works can be simply
// understood by reading the code.
//
// A good code can be seen by the quality and quantity
// of the comments. Remember: WHAT, never HOW!
//
#include "stdafx.h"
#include "gen.h"
#include "wa_ipc.h"
#include "resource.h"
#include "WinAmp DDE.h"
#include <stdio.h>
#define PLUGIN_DESC "WinAmp DDE"
#define PLUGIN_BY " [ Skizo ]"
#define PLUGIN_VER " v0.1"
winampGeneralPurposePlugin plugin = { GPPHDR_VER, PLUGIN_DESC PLUGIN_VER, init, config, quit, };
winampGeneralPurposePlugin *winampGetGeneralPurposePlugin() { return &plugin; }
#pragma comment(linker, "/EXPORT:winampGetGeneralPurposePlugin=?winampGetGeneralPurposePlugin@@YAPAUwinampGeneralPurposePlugin@@XZ")
//
// You might ask how did I got that longass name. Well, fact is that I tried
// to use a standard version of the export thing in the code, which is
//
// __declspec( dllexport ) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; }
//
// That worked, but instead of giving me the "winampGeneralPurposePlugin" as the export name,
// it had the winampGetGeneralPurposePlugin@@YAPAUwinampGeneralPurposePlugin@@XZ name instead.
// Considered the fact you need to know how to use a PE Editor to understand how to get the
// export name list, I'll tell you a couple thing:
//
// 1. LordPE > PE Editor > Directories > ExportTable
// 2. Google. You have to find LordPE, after all ;)
//
// Once you got that, you can simply do as I wrote: /Export:exportedname=functionname, which
// in this case is
//
// /EXPORT:winampGetGeneralPurposePlugin=?winampGetGeneralPurposePlugin@@YAPAUwinampGeneralPurposePlugin@@XZ
//
// Of course there are several other methods, but the several others are not in my style.
// This works, so for me is way more than enough. Although it might be (and probably is)
// compiler dependant. But now you know how to fix that, don't you?
//
//
// That's all you got to know about that :]
//
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
//
// You should check the "WinAmp DDE.h" to understand this
// particoular usage of the "goto", since it's not the
// standard one, being overridden.
//
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//
// Let's look for an open WinAmp application
//
HWND WA_hWnd = FindWindow("Winamp v1.x",NULL);
//
// Actually no minimum version needed
//
if (0 == SendMessage(WA_hWnd,WM_WA_IPC,0,IPC_GETVERSION))
{
MessageBox(WA_hWnd,"WinAmp DDE requires WinAmp X.XX","WinAmp DDE [ Error ]",MB_ICONSTOP);
break;
}
//
// Tries to register the DDE Server, and if it fails it
// tells the system to stop loading the DLL
//
if (DMLERR_NO_ERROR != DdeInitialize(&idInst, DdeCallback, APPCLASS_STANDARD | CBF_FAIL_ADVISES |
CBF_FAIL_EXECUTES | CBF_FAIL_SELFCONNECTIONS |
CBF_SKIP_ALLNOTIFICATIONS, 0))
{
Error:
MessageBox(WA_hWnd,"Unable to initialize DDE","WinAmp DDE [ Error ]",MB_ICONSTOP);
break;
}
if (!(DDEServerName = DdeCreateStringHandle(idInst, ServerName, CP_WINANSI)))
goto Error
if (!(DDETopicEvaluate = DdeCreateStringHandle(idInst, EvaluateName, CP_WINANSI)))
goto Error
if (0 == DdeNameService(idInst, DDEServerName, 0, DNS_REGISTER))
goto Error
return true;
//
// We do not need these now...
//
/* case DLL_PROCESS_DETACH:
MessageBox(0,"DLL_PROCESS_DETACH",NULL,0);
break;
case DLL_THREAD_ATTACH:
MessageBox(0,"DLL_THREAD_ATTACH",NULL,0);
break;
case DLL_THREAD_DETACH:
MessageBox(0,"DLL_THREAD_DETACH",NULL,0);
break;/**/
}
return false;
}
//
// Starts the Dialog
//
void config() { DialogBox(plugin.hDllInstance,MAKEINTRESOURCE(IDD_CONFIG),plugin.hwndParent,ConfigProc); }
void quit()
{
//
// Unregisters DDE Server and save configuration
//
DdeNameService(idInst, DDEServerName, 0, DNS_UNREGISTER);
DdeFreeStringHandle(idInst, DDETopicEvaluate);
DdeFreeStringHandle(idInst, DDEServerName);
DdeUninitialize(idInst);
config_write();
}
int init()
{
//
// General initialization, configuration reading and
// WinAmp's WndProc bypass
//
hwnd_winamp = plugin.hwndParent;
config_read();
lpWndProcOld = (WNDPROC)SetWindowLong(hwnd_winamp,GWL_WNDPROC,(LONG)WndProc);
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//
// No messages to process right now... maybe in a future...
//
return CallWindowProc(lpWndProcOld,hwnd,message,wParam,lParam);
}
/* *\
** **
** CONFIGURATION **
** **
\* */
BOOL CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
{
//
// Dialog initialization
//
case WM_INITDIALOG:
SetWindowText(hwndDlg,PLUGIN_DESC " Plugin" PLUGIN_VER PLUGIN_BY);
SetDlgItemText(hwndDlg, IDC_SERVERNAME, ServerName);
SetDlgItemText(hwndDlg, IDC_TOPIC1NAME, EvaluateName);
SetDlgItemText(hwndDlg, IDC_TOPIC1TYPE, "XTYP_REQUEST");
break;
//
// Would you like to quit? ;)
//
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hwndDlg,0);
}
break;
}
return false;
}
//
// We have no settings to save right now, but since we are
// planning (or better I am planning) to add more stuff,
// this functions can be soon used.
//
void config_read()
{
/* ini_file=(char*)SendMessage(plugin.hwndParent,WM_USER,0,334);
if ((unsigned int)ini_file < 65536) ini_file="winamp.ini";
/**/
// config_enabled = GetPrivateProfileInt(PLUGIN_DESC,"Enabled",config_enabled,ini_file);
}
void config_write()
{
/* char string[32];
// wsprintf(string,"%d",config_enabled);
WritePrivateProfileString(PLUGIN_DESC,"Enabled",string,ini_file);/**/
}
/* *\
** **
** DDE SERVER **
** **
\* */
HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hconv, HSZ hszTopic, HSZ hszItem, HDDEDATA hdata, DWORD dwData1, DWORD dwData2)
{
HDDEDATA ret = 0;
switch (uType)
{
//
// Allow DDE connections
//
case XTYP_CONNECT:
ret = (HDDEDATA)true;
break;
//
// Answer the DDE Requests. If the Topic is correct, obviously :P
//
case XTYP_REQUEST:
//
// We use the DdeCmpStringHandles instead of the strcmp
// for two reasons: 1st we already have the strings in HSZ
// format, 2nd this way it's case insensitive as the DDE
// protocol is supposed to be.
//
if (DdeCmpStringHandles(hszTopic, DDETopicEvaluate) == 0)
{
if (hwnd_winamp != NULL)
{
if (0 != SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING))
{
int index = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS); char* string;
char Message[2048]; Message[0] = 0;
string = (char*)SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE);
strcat(Message,string);
strcat(Message,"\r\n");
string = (char*)SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE);
strcat(Message,string);
ret = DdeCreateDataHandle(idInst, (BYTE*)&Message[0], strlen(Message)+1, 0, hszItem, CF_TEXT, 0);
}
else
ret = DdeCreateDataHandle(idInst, (BYTE*)"\1[ NOTHiNG ]", 13, 0, hszItem, CF_TEXT, 0);
}
else
ret = DdeCreateDataHandle(idInst, (BYTE*)"\1Error: unavaible WinAmp window handle...", 42, 0, hszItem, CF_TEXT, 0);
if (ret == 0)
ret = DdeCreateDataHandle(idInst, (BYTE*)"\1Unknown Error...", 18, 0, hszItem, CF_TEXT, 0);
}
break;
//
// No pokes this time...
//
case XTYP_POKE:
// ret = DDE_FACK; // Return FACK if poke message is processed
ret = DDE_FNOTPROCESSED;
break;
}
return ret;
}