Difference between revisions of "Beginner's Basic Plugin Guide"

From Winamp Developer Wiki
Jump to: navigation, search
(move credits to their own section)
m (clarification)
 
(21 intermediate revisions by 4 users not shown)
Line 20: Line 20:
 
# When Visual Studio opens, select File -> New -> Project
 
# When Visual Studio opens, select File -> New -> Project
 
# For creating Winamp plugins, we want to create code that compiles into a .dll, which Visual Studio 2008 refers to as a "Class Library". So in the 'New Project' window select Visual C++ -> CLR -> Class Library.
 
# For creating Winamp plugins, we want to create code that compiles into a .dll, which Visual Studio 2008 refers to as a "Class Library". So in the 'New Project' window select Visual C++ -> CLR -> Class Library.
# Give your project a name and select a location. Make sure the Solution box says "Create New Solution".
+
# Give your project a name (see below) and select a location.
  
'''Note:''' Winamp classifies plugins based on the first part of their .dll filename, so you will need to properly name the file if you want Winamp to recognize it (TODO: can someone confirm this?!). Thus, to make our basic plugin we want to name the first part of the file 'gen_'. '''Be sure to start your plugin name with 'gen_' to make it work properly.'''
+
'''Note:''' Winamp classifies plugins based on the first part of their .dll filename, so you will need to properly name the file if you want Winamp to recognize it (details: the Winamp client searches for and loads plugins based on the first part of their filenames.  This saves time and space instead of loading all dlls and then detecting which ones are real plugins). Thus, to make our basic plugin we want to name the first part of the file 'gen_'. '''Be sure to start your plugin name with 'gen_' to make it work properly.'''
  
All of the examples in this guide will assume you named your plugin 'gen_myplugin', but you can call it whatever you like.
+
All of the examples in this guide will assume you named your plugin 'gen_myplugin', but you can call it whatever you like as long as it starts with 'gen_' (for generic plugin) and ends with '.dll'.
  
 
Once you click okay Visual Studio should create a bunch of boilerplate code for you, and throw some files in your project with names like "resource.h", "stdafx.h", "AssemblyInfo.cpp", and so on. The main files we want to work with are called 'gen_myplugin.h' and 'gen_myplugin.cpp'. If you double-click to view them you should see code that looks something like this:
 
Once you click okay Visual Studio should create a bunch of boilerplate code for you, and throw some files in your project with names like "resource.h", "stdafx.h", "AssemblyInfo.cpp", and so on. The main files we want to work with are called 'gen_myplugin.h' and 'gen_myplugin.cpp'. If you double-click to view them you should see code that looks something like this:
Line 30: Line 30:
 
<nowiki><contents of gen_myplugin.h></nowiki>
 
<nowiki><contents of gen_myplugin.h></nowiki>
 
<source lang="cpp">
 
<source lang="cpp">
// winamp_imms.h
+
// gen_myplugin.h
  
 
#pragma once
 
#pragma once
Line 36: Line 36:
 
using namespace System;
 
using namespace System;
  
namespace winamp_imms {
+
namespace gen_myplugin {
  
 
public ref class Class1
 
public ref class Class1
Line 52: Line 52:
 
#include "stdafx.h"
 
#include "stdafx.h"
  
#include "winamp_imms.h"
+
#include "gen_myplugin.h"
  
 
</source>
 
</source>
Line 63: Line 63:
  
 
=== Basic plugin code ===
 
=== Basic plugin code ===
To create your plugin, just copy/paste the following code into gen_myplugin.h and gen_myplugin.cpp.
+
To create your plugin, delete automatically generated code in both gen_myplugin.h and gen_myplugin.cpp then just copy/paste the following code into gen_myplugin.h and gen_myplugin.cpp.
  
 
<nowiki><basic gen_myplugin.h></nowiki>
 
<nowiki><basic gen_myplugin.h></nowiki>
Line 78: Line 78:
  
 
// plugin name/title (change this to something you like)
 
// plugin name/title (change this to something you like)
#define GPPHDR_NAME "My first generic Winamp plugin!"
+
#define PLUGIN_NAME "My first generic Winamp plugin!"
  
  
 
// main structure with plugin information, version, name...
 
// main structure with plugin information, version, name...
 
typedef struct {
 
typedef struct {
   int version;                  // version of the plugin, defined in "gen_empty.h"
+
   int version;                  // version of the plugin structure
   char *description;            // name/title of the plugin, defined in "gen_empty.h"
+
   char *description;            // name/title of the plugin  
 
   int (*init)();                // function which will be executed on init event
 
   int (*init)();                // function which will be executed on init event
 
   void (*config)();              // function which will be executed on config event
 
   void (*config)();              // function which will be executed on config event
 
   void (*quit)();                // function which will be executed on quit event
 
   void (*quit)();                // function which will be executed on quit event
   HWND hwndParent;              // (?)
+
   HWND hwndParent;              // hwnd of the Winamp client main window (stored by Winamp when dll is loaded)
   HINSTANCE hDllInstance;        // (?)
+
   HINSTANCE hDllInstance;        // hinstance of this plugin DLL. (stored by Winamp when dll is loaded)  
 
} winampGeneralPurposePlugin;
 
} winampGeneralPurposePlugin;
  
  
extern winampGeneralPurposePlugin *gen_plugins[256];
+
#endif //gen_myplugin_h
typedef winampGeneralPurposePlugin * (*winampGeneralPurposePluginGetter)();
+
 
+
#endif
+
 
</source>
 
</source>
 
<nowiki><end basic gen_myplugin.h></nowiki>
 
<nowiki><end basic gen_myplugin.h></nowiki>
Line 126: Line 123:
  
 
// this structure contains plugin information, version, name...
 
// this structure contains plugin information, version, name...
 +
// GPPHDR_VER is the version of the winampGeneralPurposePlugin (GPP) structure
 
winampGeneralPurposePlugin plugin = {
 
winampGeneralPurposePlugin plugin = {
 
   GPPHDR_VER,  // version of the plugin, defined in "gen_myplugin.h"
 
   GPPHDR_VER,  // version of the plugin, defined in "gen_myplugin.h"
   GPPHDR_NAME, // name/title of the plugin, defined in "gen_myplugin.h"
+
   PLUGIN_NAME, // name/title of the plugin, defined in "gen_myplugin.h"
 
   init,        // function name which will be executed on init event
 
   init,        // function name which will be executed on init event
 
   config,      // function name which will be executed on config event
 
   config,      // function name which will be executed on config event
 
   quit,        // function name which will be executed on quit event
 
   quit,        // function name which will be executed on quit event
 +
  0,          // handle to Winamp main window, loaded by winamp when this dll is loaded
 +
  0            // hinstance to this dll, loaded by winamp when this dll is loaded
 
};
 
};
  
Line 155: Line 155:
 
   //If everything works you should see this message when you quit Winamp once your plugin has been installed.
 
   //If everything works you should see this message when you quit Winamp once your plugin has been installed.
 
   //You can change this later to do whatever you want (including nothing)
 
   //You can change this later to do whatever you want (including nothing)
   MessageBox(0, L"Quite event triggered for gen_myplugin.", L"", MB_OK);
+
   MessageBox(0, L"Quit event triggered for gen_myplugin.", L"", MB_OK);
 
}
 
}
  
Line 166: Line 166:
 
</source>
 
</source>
 
<nowiki><end basic gen_myplugin.cpp></nowiki>
 
<nowiki><end basic gen_myplugin.cpp></nowiki>
 +
 +
=== winampGeneralPurposePlugin===
 +
You might be wondering why the last two fields in the winampGeneralPurposePlugin structure are left empty.  You might have noticed the reference that is returned as part of the winampGetGeneralPurposePlugin() method.  This is the method that the Winamp.exe calls when loading these gen_*.dll files.  As part of loading the plugin, the Winamp.exe will store the handle to its main window and the hinstance for the plugin dll into these two fields.  Since these are references, these values become available for use by the plugin.
 +
=== Edit dependencies ===
 +
 +
By default Visual Studio removes some files we need to compile our code. Let's add those back in:
 +
 +
# Right-click on your project (the folder called 'gen_myplugin' in the Solution Explorer window on the left) and select Properties
 +
# Expand the options to select Configuration Properties -> Linker -> Input
 +
# In the "Additional Dependancies" box, delete the text that says '$(NoInherit)', so the box is blank.
 +
# Click 'Apply', then 'OK', and then save your project.
  
 
=== Compiling the code ===
 
=== Compiling the code ===
  
Once you have pasted this code into your .h and .cpp files, save your changes and compile the project. If everything works, you should see a message in Visual Studio's 'Output' window that says something like
+
Once you have pasted this code into your .h and .cpp files, save your changes and compile the project. You can do this by pressing F7, or selecting Build -> Build Solution. If everything works, you should see some messages in Visual Studio's 'Output' window, with the final line saying something like
  
 
"========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped =========="
 
"========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped =========="
Line 180: Line 191:
  
 
# Copy your plugin .dll file into C:\Program Files\Winamp\Plugins\
 
# Copy your plugin .dll file into C:\Program Files\Winamp\Plugins\
# Close and re-open Winamp. If your plugin is installed correctly, you should see a popup window with the Init message you typed in gen_myplugin.cpp. If so, congrats! Your plugin works.
+
# Close and re-open Winamp. If your plugin is installed correctly, you should see a popup window with the Init message you typed inside gen_myplugin.cpp. If so, congrats! Your plugin works.
# You check to see your plugin name by clicking Options -> Preferences -> Plugins -> General purpose. Your plugin name should appear in that window.
+
# You can check to see your plugin name by clicking Options -> Preferences -> Plugins -> General purpose. Your plugin name should appear in that window.
 +
# Click on your plugin to hilight it and then select the 'Configure Selected Plugin' button.  This should cause the 'config' message to appear.
 +
# If you exit Winamp, you should now see the Quit message pop up as the program exits.
 +
 
 +
=== Bonus Step - automatically copy the .dll ===
 +
If you want you can tell Visual Studio to copy your .dll to the Winamp plugins folder as part of the build process. It's optional but can save you copying it every time. To do this:
 +
 
 +
# Right-click on your project and select Properties
 +
# Open 'Configuration Properties' -> 'Build Events' -> 'Post Build Event'
 +
# In the Command Line type <pre>copy "$(OutDir)\$(TargetFileName)" "C:\Program Files\Winamp\Plugins"</pre>
 +
# Enter some Description, such as "copy dll to winamp plugins folder"
 +
# Click 'OK'
 +
 
 +
The next time you build your project the .dll will be copied over automatically. Be careful - this means you will always overwrite any existing .dll. Once you are finished making your plugin you may want to remove this step. Also, keep a backup copy.
 +
 
 +
== What's next? ==
 +
 
 +
So what's next? More guides still need to be written. Eventually there may be a guide for each type of plugin: [[Input_Plugin|Input]], [[Output_Plugin|Output]], [[Visualization_Plugin|Visualization]], [[DSP_Plugin|Audio Effect/DSP]], [[General_Purpose_Plugin|General Purpose]], [[Media_Library_Plugin|Media Library]] and [[Portable_Plugin|Portables]].
  
If you exit Winamp, you should now also see a Quit message pop up when the program exits.
+
If you're looking to write a plugin, you may want to start by reading through the [[SDK_Contents]] and trying to find a code example that's similar to what you want to do.
  
That's it for the basic guide! More will have to be written later if you're looking for what to do next. Happy hacking!
+
Taking this page as a start of a tutorial on plugins, let's move on to actually calling the Winamp 2 API methods from within our newly created plugin. [[Basic Plugin Guide - Tutorial #2]]
  
 
----
 
----
Line 193: Line 221:
  
 
<pre>
 
<pre>
1>winamp_imms.obj : error LNK2028: unresolved token (0A00001D) "extern "C" int __stdcall  
+
1>gen_myplugin.obj : error LNK2028: unresolved token (0A00001D) "extern "C" int __stdcall  
 
MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 
MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 
  referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)"
 
  referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)"
 
  (?MessageBox@@$$J0YAHPAUHWND__@@PB_W1I@Z)
 
  (?MessageBox@@$$J0YAHPAUHWND__@@PB_W1I@Z)
  
1>winamp_imms.obj : error LNK2019: unresolved external symbol "extern "C" int __stdcall
+
1>gen_myplugin.obj : error LNK2019: unresolved external symbol "extern "C" int __stdcall
 
  MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 
  MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 
  referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)"  
 
  referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)"  
Line 204: Line 232:
 
</pre>
 
</pre>
  
:'''Answer:''' By default Visual Studio Express 2008 doesn't include some files you need in the linker. For example, with our basic code we include windows.h, which is part of user32.dll. However, there is usually a '$(NoInherit)' command in our linker options that tell us not to include user32.dll.
+
:'''Answer:''' By default Visual Studio Express 2008 doesn't include some files you need in the linker. For example, with our basic code we include windows.h, which is part of user32.dll. However, there is usually a '$(NoInherit)' command in our linker options that tell us not to include user32.dll. To fix this, try the following:
  
 
# Right-click on your project and select Properties
 
# Right-click on your project and select Properties
Line 211: Line 239:
 
# Save and recompile
 
# Save and recompile
  
For more information see [http://stackoverflow.com/questions/739952/c-cli-linker-gives-unresolved-token-for-win32-function here] and [http://stackoverflow.com/questions/721387/i-get-lnk2028-when-trying-to-wrap-native-c-class-using-managed-c here].
+
Note:
 +
In Visual Studio Express 2010 the option is empty per default.
 +
This can be solved by changing the Configuration -> Properties -> Linker -> Input -> "Additional Dependancies" to  the following:  
 +
"kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)"
  
TODO: if this is set up for all VS installs, maybe it should be a regular step in the guide?
+
For more information see [http://stackoverflow.com/questions/739952/c-cli-linker-gives-unresolved-token-for-win32-function here] and [http://stackoverflow.com/questions/721387/i-get-lnk2028-when-trying-to-wrap-native-c-class-using-managed-c here].
  
 
* '''* I get an error like'''
 
* '''* I get an error like'''
 
<pre>
 
<pre>
"1>.\winamp_imms.cpp(29) : error C2146: syntax error : missing ';' before identifier 'plugin'"
+
"1>.\gen_myplugin.cpp(25) : error C2146: syntax error : missing ';' before identifier 'plugin'"
 
</pre>
 
</pre>
  
Line 260: Line 291:
  
 
<pre>
 
<pre>
1>.\winamp_imms.cpp(41) : error C2664: 'MessageBox' : cannot convert parameter 2 from 'const char [5]' to 'LPCTSTR'
+
1>.\gen_myplugin.cpp(42) : error C2664: 'MessageBox' : cannot convert parameter 2 from 'const char [5]' to 'LPCTSTR'
 
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
 
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
 
</pre>
 
</pre>
Line 269: Line 300:
  
 
TODO: What's the right way of doing this for plugins?
 
TODO: What's the right way of doing this for plugins?
 +
<pre>    The answer here is to leave the Visual Studio option alone (set to use UNICODE)
 +
    and use the 'L' on the front of the strings.  While you will find some parts of
 +
    Winamp still use ANSI strings, it is still meant to be an International application
 +
    and that means UNICODE.</pre>
  
 
* '''My plugin isn't working / My plugin doesn't show up in the Preferences menu.'''
 
* '''My plugin isn't working / My plugin doesn't show up in the Preferences menu.'''
Line 275: Line 310:
  
 
TODO: How about other names/plugins? What's the real answer?
 
TODO: How about other names/plugins? What's the real answer?
 +
<pre>    There are two commonly used methods to create plugins.  The first is what is known as
 +
    Winamp 2, which we will get into shortly.  Winamp 2 requires that plugins be prepended
 +
    with an identifier to their purpose, i.e., in_, out_, gen_, ml_, etc.
 +
 +
    The second method is known as Wasabi, which is actually a framework used to share
 +
    information between application components.  Wasabi files do not need a prefix.
 +
</pre>
 +
[[Category:Articles]]

Latest revision as of 21:53, 4 March 2013

So you want to write a plugin for Winamp but don't know where to start? This is the page for you. We'll walk you through creating a very basic plugin that does nothing. You can then expand on the code to do whatever you want.

Notes

This guide assumes you're developing using Visual Studio in a Windows environment. It also assumes you have some familiarity with programming. For people that don't, try using google. We'll have to expand this guide in the future to give out more pointers.

Basic credits

The following code has been created based on samples from several forum threads. Many thanks go out to forum members Kaboon, kichik, baafie, burek021, and bananskib for their code examples and advice.

What you need

To begin, you'll need a tool that lets you write code. Check out the Tools page. You can get a free copy of Visual Studio C++ Express here. You'll probably also want a copy of Winamp itself, to test your plugin; you can get that on the Winamp site here.

  1. Download Visual C++ Express.
  2. Double-click the file (it's probably called something like 'vcsetup.exe') and run the installer.
  3. You're practically good to go!

Creating a project

The first thing we need to do is create a place to store our code. In Visual Studio this is called a 'project'.

  1. Launch Visual Studio by clicking Start -> Programs -> Microsoft Visual C++ Express Edition -> Microsoft Visual C++ Express Edition
  2. When Visual Studio opens, select File -> New -> Project
  3. For creating Winamp plugins, we want to create code that compiles into a .dll, which Visual Studio 2008 refers to as a "Class Library". So in the 'New Project' window select Visual C++ -> CLR -> Class Library.
  4. Give your project a name (see below) and select a location.

Note: Winamp classifies plugins based on the first part of their .dll filename, so you will need to properly name the file if you want Winamp to recognize it (details: the Winamp client searches for and loads plugins based on the first part of their filenames. This saves time and space instead of loading all dlls and then detecting which ones are real plugins). Thus, to make our basic plugin we want to name the first part of the file 'gen_'. Be sure to start your plugin name with 'gen_' to make it work properly.

All of the examples in this guide will assume you named your plugin 'gen_myplugin', but you can call it whatever you like as long as it starts with 'gen_' (for generic plugin) and ends with '.dll'.

Once you click okay Visual Studio should create a bunch of boilerplate code for you, and throw some files in your project with names like "resource.h", "stdafx.h", "AssemblyInfo.cpp", and so on. The main files we want to work with are called 'gen_myplugin.h' and 'gen_myplugin.cpp'. If you double-click to view them you should see code that looks something like this:

<contents of gen_myplugin.h>

// gen_myplugin.h
 
#pragma once
 
using namespace System;
 
namespace gen_myplugin {
 
	public ref class Class1
	{
		// TODO: Add your methods for this class here.
	};
}

<end contents of gen_myplugin.h>

<contents of gen_myplugin.cpp>

// This is the main DLL file.
 
#include "stdafx.h"
 
#include "gen_myplugin.h"

<end contents of gen_myplugin.cpp>


We want to edit these files to make a basic plugin.

Creating a basic plugin

Basic plugin code

To create your plugin, delete automatically generated code in both gen_myplugin.h and gen_myplugin.cpp then just copy/paste the following code into gen_myplugin.h and gen_myplugin.cpp.

<basic gen_myplugin.h>

#ifndef gen_myplugin_h
//---------------------------------------------------------------------------
#define gen_myplugin_h
#include <windows.h>
 
 
// plugin version (don't touch this)
#define GPPHDR_VER 0x10
 
// plugin name/title (change this to something you like)
#define PLUGIN_NAME "My first generic Winamp plugin!"
 
 
// main structure with plugin information, version, name...
typedef struct {
  int version;                   // version of the plugin structure
  char *description;             // name/title of the plugin 
  int (*init)();                 // function which will be executed on init event
  void (*config)();              // function which will be executed on config event
  void (*quit)();                // function which will be executed on quit event
  HWND hwndParent;               // hwnd of the Winamp client main window (stored by Winamp when dll is loaded)
  HINSTANCE hDllInstance;        // hinstance of this plugin DLL. (stored by Winamp when dll is loaded) 
} winampGeneralPurposePlugin;
 
 
#endif //gen_myplugin_h

<end basic gen_myplugin.h>

<basic gen_myplugin.cpp>

/*
 
Winamp generic plugin template code.
This code should be just the basics needed to get a plugin up and running.
You can then expand the code to build your own plugin.
 
Updated details compiled June 2009 by culix, based on the excellent code examples
and advice of forum members Kaboon, kichik, baafie, burek021, and bananskib.
Thanks for the help everyone!
 
*/
 
#include "stdafx.h"
#include <windows.h>
#include "gen_myplugin.h"
 
 
// these are callback functions/events which will be called by Winamp
int  init(void);
void config(void);
void quit(void);
 
 
// this structure contains plugin information, version, name...
// GPPHDR_VER is the version of the winampGeneralPurposePlugin (GPP) structure
winampGeneralPurposePlugin plugin = {
  GPPHDR_VER,  // version of the plugin, defined in "gen_myplugin.h"
  PLUGIN_NAME, // name/title of the plugin, defined in "gen_myplugin.h"
  init,        // function name which will be executed on init event
  config,      // function name which will be executed on config event
  quit,        // function name which will be executed on quit event
  0,           // handle to Winamp main window, loaded by winamp when this dll is loaded
  0            // hinstance to this dll, loaded by winamp when this dll is loaded
};
 
 
// event functions follow
 
int init() {
  //A basic messagebox that tells you the 'init' event has been triggered.
  //If everything works you should see this message when you start Winamp once your plugin has been installed.
  //You can change this later to do whatever you want (including nothing)
  MessageBox(plugin.hwndParent, L"Init event triggered for gen_myplugin. Plugin installed successfully!", L"", MB_OK);
  return 0;
}
 
void config() {
  //A basic messagebox that tells you the 'config' event has been triggered.
  //You can change this later to do whatever you want (including nothing)
  MessageBox(plugin.hwndParent, L"Config event triggered for gen_myplugin.", L"", MB_OK);
}
 
void quit() {
  //A basic messagebox that tells you the 'quit' event has been triggered.
  //If everything works you should see this message when you quit Winamp once your plugin has been installed.
  //You can change this later to do whatever you want (including nothing)
  MessageBox(0, L"Quit event triggered for gen_myplugin.", L"", MB_OK);
}
 
 
// This is an export function called by winamp which returns this plugin info.
// We wrap the code in 'extern "C"' to ensure the export isn't mangled if used in a CPP file.
extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() {
  return &plugin;
}

<end basic gen_myplugin.cpp>

winampGeneralPurposePlugin

You might be wondering why the last two fields in the winampGeneralPurposePlugin structure are left empty. You might have noticed the reference that is returned as part of the winampGetGeneralPurposePlugin() method. This is the method that the Winamp.exe calls when loading these gen_*.dll files. As part of loading the plugin, the Winamp.exe will store the handle to its main window and the hinstance for the plugin dll into these two fields. Since these are references, these values become available for use by the plugin.

Edit dependencies

By default Visual Studio removes some files we need to compile our code. Let's add those back in:

  1. Right-click on your project (the folder called 'gen_myplugin' in the Solution Explorer window on the left) and select Properties
  2. Expand the options to select Configuration Properties -> Linker -> Input
  3. In the "Additional Dependancies" box, delete the text that says '$(NoInherit)', so the box is blank.
  4. Click 'Apply', then 'OK', and then save your project.

Compiling the code

Once you have pasted this code into your .h and .cpp files, save your changes and compile the project. You can do this by pressing F7, or selecting Build -> Build Solution. If everything works, you should see some messages in Visual Studio's 'Output' window, with the final line saying something like

"========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped =========="

If so, congratulations! You have built your first plugin :) If instead you get an error message, please see the Troubleshooting section below.

Testing the plugin

If your project builds successfully, it will output a file called 'gen_myplugin.dll' into a 'Debug' or 'Release' folder in the same location you created your project. Look for that file now. We want to copy this file to C:\Program Files\Winamp\Plugins\, which is the folder Winamp examines to find all of its plugins.

  1. Copy your plugin .dll file into C:\Program Files\Winamp\Plugins\
  2. Close and re-open Winamp. If your plugin is installed correctly, you should see a popup window with the Init message you typed inside gen_myplugin.cpp. If so, congrats! Your plugin works.
  3. You can check to see your plugin name by clicking Options -> Preferences -> Plugins -> General purpose. Your plugin name should appear in that window.
  4. Click on your plugin to hilight it and then select the 'Configure Selected Plugin' button. This should cause the 'config' message to appear.
  5. If you exit Winamp, you should now see the Quit message pop up as the program exits.

Bonus Step - automatically copy the .dll

If you want you can tell Visual Studio to copy your .dll to the Winamp plugins folder as part of the build process. It's optional but can save you copying it every time. To do this:

  1. Right-click on your project and select Properties
  2. Open 'Configuration Properties' -> 'Build Events' -> 'Post Build Event'
  3. In the Command Line type
    copy "$(OutDir)\$(TargetFileName)" "C:\Program Files\Winamp\Plugins"
  4. Enter some Description, such as "copy dll to winamp plugins folder"
  5. Click 'OK'

The next time you build your project the .dll will be copied over automatically. Be careful - this means you will always overwrite any existing .dll. Once you are finished making your plugin you may want to remove this step. Also, keep a backup copy.

What's next?

So what's next? More guides still need to be written. Eventually there may be a guide for each type of plugin: Input, Output, Visualization, Audio Effect/DSP, General Purpose, Media Library and Portables.

If you're looking to write a plugin, you may want to start by reading through the SDK_Contents and trying to find a code example that's similar to what you want to do.

Taking this page as a start of a tutorial on plugins, let's move on to actually calling the Winamp 2 API methods from within our newly created plugin. Basic Plugin Guide - Tutorial #2


Troubleshooting

  • I get an error message that looks like:
1>gen_myplugin.obj : error LNK2028: unresolved token (0A00001D) "extern "C" int __stdcall 
MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)"
 (?MessageBox@@$$J0YAHPAUHWND__@@PB_W1I@Z)

1>gen_myplugin.obj : error LNK2019: unresolved external symbol "extern "C" int __stdcall
 MessageBoxW(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" (?MessageBoxW@@$$J216YGHPAUHWND__@@PB_W1I@Z)
 referenced in function "extern "C" int __cdecl MessageBox(struct HWND__ *,wchar_t const *,wchar_t const *,unsigned int)" 
(?MessageBox@@$$J0YAHPAUHWND__@@PB_W1I@Z)
Answer: By default Visual Studio Express 2008 doesn't include some files you need in the linker. For example, with our basic code we include windows.h, which is part of user32.dll. However, there is usually a '$(NoInherit)' command in our linker options that tell us not to include user32.dll. To fix this, try the following:
  1. Right-click on your project and select Properties
  2. Expand the options to select Configuration Properties -> Linker -> Input
  3. In the "Additional Dependancies" box, delete the text that says '$(NoInherit)'.
  4. Save and recompile

Note: In Visual Studio Express 2010 the option is empty per default. This can be solved by changing the Configuration -> Properties -> Linker -> Input -> "Additional Dependancies" to the following: "kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)"

For more information see here and here.

  • * I get an error like
"1>.\gen_myplugin.cpp(25) : error C2146: syntax error : missing ';' before identifier 'plugin'"
Answer: This could be a namespace error. On this line
winampGeneralPurposePlugin plugin = {

we are creating a variable called 'plugin'. The variable's type, rather than being an integer ('int'), floating point number ('float'), or some other type, is of type 'winampGeneralPurposePlugin', which is a type we have created. This type is created in the header file with the code

typedef struct {
  int version;                   
  char *description;             
  int (*init)();                 
  void (*config)();              
  void (*quit)();                
  HWND hwndParent;               
  HINSTANCE hDllInstance;        
} winampGeneralPurposePlugin;

If you used Visual Studio's auto-generated code, you might have a line that looks like this:

namespace myplugin {


this line is telling Visual Studio to put everything inside the 'myplugin' namespace (read up on namespaces here). We get rid of the default boilerplate code, including the namespace, to make everything work.

Alternatively, you can change the declaration of the 'plugin' variable to say

myplugin::winampGeneralPurposePlugin plugin = {

This tells Visual Studio where to look for the winampGeneralPurposePlugin type (i.e., to look inside the 'myplugin' namespace). If you are new to programming or plugin creation, however, you probably don't need (or want) to mess with namespaces.

  • I get an error like
1>.\gen_myplugin.cpp(42) : error C2664: 'MessageBox' : cannot convert parameter 2 from 'const char [5]' to 'LPCTSTR'
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Answer: You need to change the string you pass to the MessageBox function to be of the right type. See the MSDN FAQ entry here. You can most likely fix this by putting a capital 'L' in front of the string.
e.g. change
MessageBox(plugin.hwndParent, "Somestring", "", MB_OK);
to
MessageBox(plugin.hwndParent, L"Somestring", L"", MB_OK);

TODO: What's the right way of doing this for plugins?

     The answer here is to leave the Visual Studio option alone (set to use UNICODE) 
     and use the 'L' on the front of the strings.  While you will find some parts of 
     Winamp still use ANSI strings, it is still meant to be an International application 
     and that means UNICODE.
  • My plugin isn't working / My plugin doesn't show up in the Preferences menu.
Answer:Did you name your plugin with the correct prefix, such as 'gen_myplugin'? If not it may not show up in the window.

TODO: How about other names/plugins? What's the real answer?

    There are two commonly used methods to create plugins.  The first is what is known as 
    Winamp 2, which we will get into shortly.  Winamp 2 requires that plugins be prepended 
    with an identifier to their purpose, i.e., in_, out_, gen_, ml_, etc.

    The second method is known as Wasabi, which is actually a framework used to share 
    information between application components.  Wasabi files do not need a prefix.