Basic Plugin Guide - Tutorial
Accessing information from Winamp
Our first page Beginner's Basic Plugin Guide showed you how to create a general plugin dll that will be invoked by Winamp when it is loaded, configured and quit. While this is really cool, it lacks something. Let's fix that now.
Keeping it simple
In order to keep these explanations simple, I'd rather not add a lot of code to create a Windows UI. We will eventually create a UI, but first let's invoke some Winamp 2 API methods. Just to see how they work.
When we last left our hero....err plugin, we had created three methods 'init', 'config' and 'quit'. 'init' is invoked by Winamp when the plugin is first loaded. 'quit' is invoked by Winamp when the plugin is unloaded, i.e., when Winamp is closing. 'config' is invoked when someone configures the plugin. A user can configure a plugin by selecting Options->Preferences->Plug-ins->General Purpose, clicking on the plug-in in the right hand pane and then pressing the 'Configure selected plug-in' button. Note that I will refer to this as 'configuring the plugin' from now on as that much typing is annoying.
While we would normally add code to 'init' to set up our UI and event notification system, let's keep it even simpler than that (we'll cover the basics of that in a future tutorial). Let's add the API calls to the 'config' method for now. Just to see how they work.
The top secret, knows everything, key to the executive washroom file is called wa_ipc.h. This file defines constants for all of the Winamp 2 API methods that are made available for external developers. And they are fairly well documented (gasp). Yes, I know this sounds impossible, when do developers actually write documentation? Well trust me this file is very well documented so it does happen.
This header can be found in the SDK under the Winamp SDK\Winamp directory. Add these two lines to the top of gen_myplugin.cpp:
#include "wa_ipc.h" #include <stdio.h>
But wait! This isn't enough. There's always a catch. The compiler has no idea where to find wa_ipc.h. So we have to tell it. Navigate to Project->gen_myplugin Properties->Configuration Properties->C/C++->General and enter the path to the Winamp directory under the base directory where you installed the Winamp SDK. Now Visual studio should be able to find the header.
Calling an API Method
Hmmm, what should we do....How about we get the version of Winamp that is currently running? Sounds like a useful thing for a plug-in to do. Place the following code into the 'config' method, immediately under the MessageBox about the config event.
wchar_t msg; int version = SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSION); int majVersion = WINAMP_VERSION_MAJOR(version); int minVersion = WINAMP_VERSION_MINOR(version); wsprintf(msg,L"The version of Winamp is: %x\n" L"Major version: %x\nMinor version: %x\n", version, majVersion, minVersion ); MessageBox(plugin.hwndParent,msg,L"Winamp Version",MB_OK);
Compile the plugin and move the dll over into the Winamp plug-ins directory (make sure to exit Winamp before trying to copy the dll, Windows can be so possessive.) Start Winamp. After Winamp starts and you see the message about the 'init' being called, 'configure' the plugin and you should see the 'config' event message, press OK. If you've done everything correctly you should now see the version message box appear.
Examining the Code
The first line declares a buffer of 1024 wchar_t. This buffer is used to format output that we want to add to the MessageBox.
The second line uses Windows SendMessage API to send a message, WM_WA_IPC (second parameter), in this case to plugin.hwndParent (first parameter). WM_WA_IPC indicates a message that Winamp is supposed to handle rather than Windows itself. The third parameter is usually used to pass a parameter (known as a WPARAM in Windows) and is set to 0. The fourth parameter (known as an LPARAM in Windows) is a constant that identifies what we want Winamp to do for us, in this case 'get me the version of winamp'.
But 'Hold on Thar Partner' you might be saying. Where did that plugin.hwndParent come from? We didn't set it in our plug-in. Turns out that Winamp stored the handle to its main window into the plugin structure when it loaded it. Notice the method used to retrieve the plugin (winampGetGeneralPurposePlugin) actually returns a reference to the plugin? Cool huh, Winamp can then load the hwndParent and hDllInstance and it becomes available to the plug-in. The point is that we are sending messages to the Winamp main window so that it can give us the info we're looking for.
The next two lines use macros defined in WA_IPC.h to break apart the version into major and minor numbers. Another useful feature when you need to know the version of Winamp to determine whether a feature is supported.
The rest of the new lines format a wide string (wsprintf) so we can use it in MessageBox to print it to the screen.
Examining the wa_ipc.h file
How did I know what to use for WPARAM and LPARAM? Well the wa_ipc.h file of course. Go ahead and open it in Visual Studio. The first thing you'll notice is blocks of comments. Blocks of comments? OH YEAH. Let's just look at the comments for five minutes, I'll wait......Seriously, if you scroll down in this file you'll see the Winamp 2 API methods. Searching for IPC_GETVERSION will take you right to the description of how the method works.
Sometimes the method description will indicate that a method is only available at a certain level of Winamp, such as 'requires Winamp 5.0+', i.e. the reason we needed the IPC_GETVERSION.
You may also notice that some methods have both 'normal' and 'wide' versions. 'normal' versions take ANSI/ASCII parameters while 'wide' versions take UNICODE chars and strings.
Adding another API
Let's try another API call. Put the following after the previous MessageBox:
char *winampVersion; winampVersion = (char *)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSIONSTRING); MessageBoxA(NULL,winampVersion,"Winamp Version 2",MB_OK);
The SendMessage in this code is requesting a string version of the Winamp executable. It will return a char * to a string such as '5.57 build 2514 Beta'. This is useful for displaying the version and build number, such as in the Help->About dialog.
The next line invokes the Windows MessageBoxA API to display a ANSI string. I meant to do that. No, no, I really did. I wanted to point out that some Winamp 2 API methods return simple char pointers not wchar_t pointers. This means that there may be some conversion required before using strings. Just in case, the "A" on the end of the method is a specialized version of MessageBox that takes ANSI strings rather than UNICODE strings.
Replace the line with the call to MessageBoxA with the following:
wsprintf(msg,L"String Version: %S\n",winampVersion); MessageBox(plugin.hwndParent,msg,L"Winamp Version 3", MB_OK);
See it works. Notice the '%S' in wsprintf. "%S" says the input value is an ANSI string and wsprintf can handle it correctly.