Difference between revisions of "Basic Plugin Guide - Tutorial"

From Winamp Developer Wiki
Jump to: navigation, search
(wa_ipc.h)
(Another API call)
 
(5 intermediate revisions by one user not shown)
Line 21: Line 21:
  
 
===Calling an API Method===
 
===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.
+
Hmmm, what should we do....What to do?....I know.  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.
  
 
<source lang="cpp">
 
<source lang="cpp">
Line 40: Line 40:
 
</source>
 
</source>
  
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.
+
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.  The first thing you should see is the message about '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===
 
===Examining the Code===
Line 47: Line 47:
 
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'.
 
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.
+
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 winampGetGeneralPurposePlugin(), it is called by Winamp to retrieve the plugin.  This method actually returns a reference to the plugin. Cool huhWinamp 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 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.
 
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===
 
===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.
 
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.
Line 67: Line 68:
 
MessageBoxA(NULL,winampVersion,"Winamp Version 2",MB_OK);
 
MessageBoxA(NULL,winampVersion,"Winamp Version 2",MB_OK);
 
</source>
 
</source>
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 SendMessage in this code is requesting a string containing the version of the Winamp executable.  It will return a char * to a string such as '5.57 build 2514 Beta'.  This is useful for fields 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.
+
The next line invokes the Windows MessageBoxA API to display an 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.  The UNICODE version would have a "W" on the end.  Versions with neither actually depend on whether the preprocessor directive UNICODE is defined (a setting in the project).
  
 
Replace the line with the call to MessageBoxA with the following:
 
Replace the line with the call to MessageBoxA with the following:
Line 76: Line 77:
 
MessageBox(plugin.hwndParent,msg,L"Winamp Version 3", MB_OK);
 
MessageBox(plugin.hwndParent,msg,L"Winamp Version 3", MB_OK);
 
</source>
 
</source>
See it works.  Notice the '%S' in wsprintf.  "%S" says the input value is an ANSI string and wsprintf can handle it correctly.
+
See it works.  Notice the "%S" (upper case S) in wsprintf this time rather than "%s" (lower case s).  "%S" says the input value is an ANSI string and wsprintf can handle it correctly to create the string used in the MessageBox (since UNICODE is defined for this project MessageBox will invoke MessageBoxW).
  
 
===Another API call===
 
===Another API call===
Line 87: Line 88:
 
SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&eFWMS,IPC_ENQUEUEFILE);
 
SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&eFWMS,IPC_ENQUEUEFILE);
 
</source>
 
</source>
You can use a real filename if you have one.  The title and length fields are only used by Winamp in the Winamp playlist until the file is actually played, then Winamp will look inside the file for the real title and length.  
+
You can use a real filename if you have one.  The title and length fields are only used by Winamp in the Winamp playlist until the file is actually played. At that point Winamp will look inside the file for the real title and length.  
  
This example shows how to use that WPARAM thingy (thingy is a technical term).  In this method the WPARAM gives Winamp information to use along with the request, in this case the filename and path, a title and length.  This method will Enqueue the file to be played at the bottom of the Winamp playlist.
+
This example shows how to use that WPARAM thingy (thingy is a technical term).  In this method the WPARAM gives Winamp information to use along with the request, in this case a pointer to a structure.  The enqueueFileWithMetaStruct structure is defined in WA_IPC.h.  The structure contains the path and filename, a title and the length of the song in seconds.  This method will Enqueue the file to be played at the bottom of the Winamp playlist.
  
Compile, copy, restart Winamp and configure your plugin. After four message boxes (less if you became fed up with them all and removed some), you will notice that the specified file has been added to the Winamp Playlist with the specified title and length.  Note, that if the file does not actually exist, Winamp will simply skip over it when you try and play it.
+
Compile, copy, restart Winamp and configure your plugin. After four message boxes (less if you became fed up with them and removed some), you will notice that the specified file has been added to the Winamp Playlist with the specified title and length.  Note, that if the file does not actually exist, Winamp will simply skip over it when you try and play it.
  
 
===What's in a UI===
 
===What's in a UI===
 
Now that I think I've gotten the point across let's get rid of those Message Boxes and create some real UI.  The first thing we need to....hmmm, footsteps....gotta go....talk about this next time.
 
Now that I think I've gotten the point across let's get rid of those Message Boxes and create some real UI.  The first thing we need to....hmmm, footsteps....gotta go....talk about this next time.

Latest revision as of 15:56, 7 August 2009

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. Maybe it needs to do 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.

wa_ipc.h

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, beneath the existing includes:

#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. So, in Visual Studio, navigate to Project->gen_myplugin Properties->Configuration Properties->C/C++->General and enter the path to the Winamp subdirectory under the 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....What to do?....I know. 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[1024];
 
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. The first thing you should see is the message about '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 winampGetGeneralPurposePlugin(), it is called by Winamp to retrieve the plugin. This method 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 containing the version of the Winamp executable. It will return a char * to a string such as '5.57 build 2514 Beta'. This is useful for fields such as in the Help->About dialog.

The next line invokes the Windows MessageBoxA API to display an 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. The UNICODE version would have a "W" on the end. Versions with neither actually depend on whether the preprocessor directive UNICODE is defined (a setting in the project).

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" (upper case S) in wsprintf this time rather than "%s" (lower case s). "%S" says the input value is an ANSI string and wsprintf can handle it correctly to create the string used in the MessageBox (since UNICODE is defined for this project MessageBox will invoke MessageBoxW).

Another API call

Add the following after the previous MessageBox statement:

enqueueFileWithMetaStruct eFWMS = {0};
eFWMS.filename="c:\\test\\folder\\test.mp3";
eFWMS.title = "Test It Good";
eFWMS.length = 300;
SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&eFWMS,IPC_ENQUEUEFILE);

You can use a real filename if you have one. The title and length fields are only used by Winamp in the Winamp playlist until the file is actually played. At that point Winamp will look inside the file for the real title and length.

This example shows how to use that WPARAM thingy (thingy is a technical term). In this method the WPARAM gives Winamp information to use along with the request, in this case a pointer to a structure. The enqueueFileWithMetaStruct structure is defined in WA_IPC.h. The structure contains the path and filename, a title and the length of the song in seconds. This method will Enqueue the file to be played at the bottom of the Winamp playlist.

Compile, copy, restart Winamp and configure your plugin. After four message boxes (less if you became fed up with them and removed some), you will notice that the specified file has been added to the Winamp Playlist with the specified title and length. Note, that if the file does not actually exist, Winamp will simply skip over it when you try and play it.

What's in a UI

Now that I think I've gotten the point across let's get rid of those Message Boxes and create some real UI. The first thing we need to....hmmm, footsteps....gotta go....talk about this next time.