Click here to Skip to main content
15,886,806 members
Articles / Programming Languages / C++

WinAmp DDE

Rate me:
Please Sign up or sign in to vote.
3.15/5 (9 votes)
28 May 200510 min read 56.8K   1.4K   14  
Learn how to setup a DDE server, export function names through pragmas, create and use dialogs without MFC, override language operators, bypass a window's WndProc function, and last but not the least, create a general WinAmp plugin.
 /*********************\  /*********************\  /*********************\
|**                   **||**                   **||**                   **|
|*  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;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Italy Italy

When he was 2 years old some friends of his mother made him play with arcade machines, and was almost clear what his path would have been. When he was 10 years old he's been given a 80286 as birthday present, and from that day on it's been a crescendo of programming techniques. Started with batch ms-dos file, that in a short while got interfaced with .com files created with debug.exe embedded ms-dos file... and then gw-basic, turbo pascal, quick basic, assembly, vb, vc++, delphi, java, plus a bunch of scripting languages, php included.
His other main passion is music and art in general, and has been awarded several times for his graphics and songs. He founded and still leads a demogroup named 3D, which stands for DBC Demo Division, and from time to time he takes the chance to attend demoparties, and eventually, submit artworks to some of the competitions offered by the different parties.
He's currently workin as a sales agent. Nothing to do with computers, but still he has to gain some money to buy some food...


Comments and Discussions