Beginner's Basic Plugin Guide

From Winamp Developer Wiki
Revision as of 05:03, 10 June 2009 by Culix (Talk | contribs)

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 (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.

All of the examples in this guide will assume you named your plugin 'gen_myplugin', but you can call it whatever you like.

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>

// winamp_imms.h
 
#pragma once
 
using namespace System;
 
namespace winamp_imms {
 
	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 "winamp_imms.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, 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 GPPHDR_NAME "My first generic Winamp plugin!"
 
 
// main structure with plugin information, version, name...
typedef struct {
  int version;                   // version of the plugin, defined in "gen_empty.h"
  char *description;             // name/title of the plugin, defined in "gen_empty.h"
  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;               // (?)
  HINSTANCE hDllInstance;        // (?)
} winampGeneralPurposePlugin;
 
 
extern winampGeneralPurposePlugin *gen_plugins[256];
typedef winampGeneralPurposePlugin * (*winampGeneralPurposePluginGetter)();
 
#endif

<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...
winampGeneralPurposePlugin plugin = {
  GPPHDR_VER,  // version of the plugin, defined in "gen_myplugin.h"
  GPPHDR_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
};
 
 
// 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"Quite 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>

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 in gen_myplugin.cpp. If so, congrats! Your plugin works.
  3. You check to see your plugin name by clicking Options -> Preferences -> Plugins -> General purpose. Your plugin name should appear in that window.

If you exit Winamp, you should now also see a Quit message pop up when the program exits.

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!


Troubleshooting

  • I get an error message that looks like:
1>winamp_imms.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>winamp_imms.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.
  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

For more information see here and here.

TODO: if this is set up for all VS installs, maybe it should be a regular step in the guide?

  • * I get an error like
"1>.\winamp_imms.cpp(29) : 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>.\winamp_imms.cpp(41) : 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?

  • 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?