Beginner's Basic Plugin Guide

From Winamp Developer Wiki
Jump to: navigation, search

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.