0

I am trying to load a Winamp input plugin and work with it in C#. According to the Winamp SDK, this is the proper way to load a plugin:

in_mp3_lib = LoadLibraryW(path);
if (in_mp3_lib)
{
    PluginGetter pluginGetter = (PluginGetter)GetProcAddress(in_mp3_lib, "winampGetInModule2");
    if (pluginGetter)
    {
        in_mp3 = pluginGetter();
    }
}

So I've created a similar code in C#:

[DllImport("kernel32", SetLastError=true, CharSet=CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

delegate IntPtr PluginGetter();

IntPtr hmod = LoadLibrary("in_midi.dll");
var getmod = (PluginGetter)Marshal.GetDelegateForFunctionPointer(GetProcAddress(hmod, "winampGetInModule2"), typeof(PluginGetter));
IntPtr modptr = getmod();

However, on the line with LoadLibrary, an error (not exception) window is shown:

---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Runtime Error!

Program: C:\Users\...

R6034

An application has made an attempt to load the C runtime library incorrectly.
Please contact the application's support team for more information.

---------------------------
OK   
---------------------------

And hmod is null.

Apparently, the plugin tries to load msvcrt90.dll, but this error keeps showing even if I have it in the directory.

Problem solved, next one has arised.

Community
  • 1
  • 1
IS4
  • 11,945
  • 2
  • 47
  • 86
  • It's an issue with the app manifest. It needs to include the msvcr90 dependency. – David Heffernan May 08 '15 at 10:18
  • 1
    @David How do I do that? – IS4 May 08 '15 at 10:30
  • 1
    Or maybe not. Perhaps the issue is that the wrong msvcr90 is loaded. Perhaps you need to remove the dll from the app directory and let it be found by WinSxS. This whole area is a minefield. – David Heffernan May 08 '15 at 11:06
  • @David The same error if loaded from WinSxS. – IS4 May 08 '15 at 11:43
  • @IllidanS4 Do you have a working Winamp on your pc? – xanatos May 08 '15 at 12:18
  • @IllidanS4 Because if you do have it, and it do works, then your PC isn't totally borked :-) If you didn't have it, or it didn't work, there was the possibility that your PC was not-ok. Try copying the msvcr* from the Winamp folder to your local folder, if there are (I don't have a copy of Winamp here) – xanatos May 08 '15 at 12:22
  • @xanatos It loads msvcr90.dll, but it's the same error. – IS4 May 08 '15 at 12:25
  • @IllidanS4 Have you configured your app to be x86? – xanatos May 08 '15 at 12:26
  • @xanatos Yes, it's the default x86 setting. – IS4 May 08 '15 at 12:28
  • This is really unfair. @xanatos answered the question you asked. Please accept the answer. It's as if you expect him to debug your entire program. You have enough rep here that you should know better. I reverted to the original question. – David Heffernan May 08 '15 at 20:48
  • @David I am sorry, but I thought it unnecessary to make two questions about basically the same thing (and that is "Loading Winamp's in_midi.dll from .NET", as the title says). Besides, one wanting the same thing would find the whole answer here, not on two separate pages. And xanatos got +1 from me, and I was going to mark that an answer in some time, shouldn't we find the solution to the second problem. – IS4 May 08 '15 at 21:04
  • This site works by asking questions one at a time. Xanatos gave you a super answer. This question is done. Feel free to ask another one. Then it's all good. – David Heffernan May 08 '15 at 21:06
  • @David Alrighty then. – IS4 May 08 '15 at 21:08

1 Answers1

3

Solved.

Add a manifest to your project. At the end of the manifest paste:

<dependency>
  <dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.4926" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b" />
  </dependentAssembly>
</dependency>

(there should already be a commented example <dependency>)

This will use the Microsoft.VC90.CRT that is in WinSxS. Go to the properties of your project, and check that in Application->Resources your Manifest is selected.

Now go in the properties of your project, and disable Enable the Visual Studio hosting project.

You'll need to put the in_midi.dll in the same folder of your .exe .

Now it should work.

For reference, I'm using:

class Program
{
    [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true, SetLastError = true)]
    static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate IntPtr PluginGetter();

    static void Main(string[] args)
    {
        IntPtr hmod = LoadLibrary("in_midi.dll");

        if (hmod == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        IntPtr proc = GetProcAddress(hmod, "winampGetInModule2");

        if (proc == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        PluginGetter getmod = (PluginGetter)Marshal.GetDelegateForFunctionPointer(proc, typeof(PluginGetter));

        IntPtr modptr = getmod();

        if (modptr == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        Console.WriteLine("Success");
    }
}

If you need, I can pass you the whole project zipped.

For the second question... Sadly the function returns a pointer to a struct. This is a problem in C#. If you only needed to read from that struct, it would be easy, but you have to modify it. You could use the Marshal.PtrToStructure + Marshal.StructureToPtr, but I don't think it would be a good idea (there are delegates and strings that need marshaling... I don't want to think what would happen). The last time I needed to do it, I did full manual marshalling:

[StructLayout(LayoutKind.Sequential)]
public class In_Module //ported from IN2.H in the Winamp SDK, struct size 152
{
    [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    private static readonly int OffsetOfMethodTable = sizeof(int) + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + sizeof(int) + sizeof(int);

    IntPtr Ptr;

    public void LoadMidiModule()
    {
        IntPtr hmod = LoadLibrary("in_midi.dll");

        if (hmod == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        IntPtr proc = GetProcAddress(hmod, "winampGetInModule2");

        if (proc == IntPtr.Zero)
        {
            throw new Win32Exception();
        }

        PluginGetter getmod = (PluginGetter)Marshal.GetDelegateForFunctionPointer(proc, typeof(PluginGetter));

        Ptr = getmod();

        if (Ptr == IntPtr.Zero)
        {
            throw new Exception();
        }

        hDllInstance = hmod;

        config = GetDelegate<ConfigFunc>(0 * IntPtr.Size);
        about = GetDelegate<AbountFunc>(1 * IntPtr.Size);
        init = GetDelegate<InitFunc>(2 * IntPtr.Size);
        quit = GetDelegate<QuitFunc>(3 * IntPtr.Size);
        getFileInfo = GetDelegate<GetFileInfoFunc>(4 * IntPtr.Size);
        infoBox = GetDelegate<InfoBoxFunc>(5 * IntPtr.Size);
        isOurFile = GetDelegate<IsOurFileFunc>(6 * IntPtr.Size);
        play = GetDelegate<PlayFunc>(7 * IntPtr.Size);
        pause = GetDelegate<PauseFunc>(8 * IntPtr.Size);
        unPause = GetDelegate<UnPauseFunc>(9 * IntPtr.Size);
        isPaused = GetDelegate<IsPausedFunc>(10 * IntPtr.Size);
        stop = GetDelegate<StopFunc>(11 * IntPtr.Size);
        getLength = GetDelegate<GetLengthFunc>(12 * IntPtr.Size);
        getOutputTime = GetDelegate<GetOutputTimeFunc>(13 * IntPtr.Size);
        setOutputTime = GetDelegate<SetOutputTimeFunc>(14 * IntPtr.Size);
        setVolume = GetDelegate<SetVolumeFunc>(15 * IntPtr.Size);
        setPan = GetDelegate<SetPanFunc>(16 * IntPtr.Size);
        savsaInit = GetDelegate<SAVSAInitFunc>(17 * IntPtr.Size);
        savsaDeInit = GetDelegate<SAVSADeInitFunc>(18 * IntPtr.Size);
        saAddPCMData = GetDelegate<SAAddPCMDataFunc>(19 * IntPtr.Size);
        saGetMode = GetDelegate<SAGetModeFunc>(20 * IntPtr.Size);
        saAdd = GetDelegate<SAAddFunc>(21 * IntPtr.Size);
        vsaAddPCMData = GetDelegate<VSAAddPCMDataFunc>(22 * IntPtr.Size);
        vsaGetMode = GetDelegate<VSAGetModeFunc>(23 * IntPtr.Size);
        vsaAdd = GetDelegate<VSAAddFunc>(24 * IntPtr.Size);
        vsaSetInfo = GetDelegate<VSASetInfoFunc>(25 * IntPtr.Size);
        dsp_isactive = GetDelegate<DSP_isactiveFunc>(26 * IntPtr.Size);
        dsp_dosamples = GetDelegate<DSP_dosamplesFunc>(27 * IntPtr.Size);
        eqSet = GetDelegate<EQSetFunc>(28 * IntPtr.Size);
        setInfo = GetDelegate<SetInfoFunc>(29 * IntPtr.Size);
    }

    private TDelegate GetDelegate<TDelegate>(int offset)
    {
        IntPtr ptr = Marshal.ReadIntPtr(Ptr, OffsetOfMethodTable + offset);

        if (ptr == IntPtr.Zero)
        {
            return default(TDelegate);
        }

        return (TDelegate)(object)Marshal.GetDelegateForFunctionPointer(ptr, typeof(TDelegate));
    }

    private void SetDelegate<TDelegate>(TDelegate del, ref TDelegate field, int offset)
    {
        field = del;
        IntPtr ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)del);
        Marshal.WriteIntPtr(Ptr, OffsetOfMethodTable + offset, ptr);
    }

    public int version
    {
        get
        {
            return Marshal.ReadInt32(Ptr, 0);
        }
    }

    public string description
    {
        get
        {
            return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(Ptr, sizeof(int)));
        }
    }

    public IntPtr hMainWindow
    {
        get
        {
            return Marshal.ReadIntPtr(Ptr, sizeof(int) + IntPtr.Size);
        }

        set
        {
            Marshal.WriteIntPtr(Ptr, sizeof(int) + IntPtr.Size, value);
        }
    }

    public IntPtr hDllInstance
    {
        get
        {
            return Marshal.ReadIntPtr(Ptr, sizeof(int) + IntPtr.Size + IntPtr.Size);
        }

        set
        {
            Marshal.WriteIntPtr(Ptr, sizeof(int) + IntPtr.Size + IntPtr.Size, value);
        }
    }

    public string FileExtensions
    {
        get
        {
            return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(Ptr, sizeof(int) + IntPtr.Size + IntPtr.Size + IntPtr.Size));
        }
    }

    public int is_seekable
    {
        get
        {
            return Marshal.ReadInt32(Ptr, sizeof(int) + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size);
        }
    }

    public int UsesOutputPlug
    {
        get
        {

            return Marshal.ReadInt32(Ptr, sizeof(int) + IntPtr.Size + IntPtr.Size + IntPtr.Size + IntPtr.Size + sizeof(int));
        }
    }

    private ConfigFunc config;

    public ConfigFunc Config
    {
        get
        {
            return config;
        }
    }

    private AbountFunc about;

    public AbountFunc About
    {
        get
        {
            return about;
        }
    }

    private InitFunc init;

    public InitFunc Init
    {
        get
        {
            return init;
        }
    }

    private QuitFunc quit;

    public QuitFunc Quit
    {
        get
        {
            return quit;
        }
    }

    private GetFileInfoFunc getFileInfo;

    public GetFileInfoFunc GetFileInfo
    {
        get
        {
            return getFileInfo;
        }
    }

    private InfoBoxFunc infoBox;

    public InfoBoxFunc InfoBox
    {
        get
        {
            return infoBox;
        }
    }

    private IsOurFileFunc isOurFile;

    public IsOurFileFunc IsOurFile
    {
        get
        {
            return isOurFile;
        }
    }

    private PlayFunc play;

    public PlayFunc Play
    {
        get
        {
            return play;
        }
    }

    private PauseFunc pause;

    public PauseFunc Pause
    {
        get
        {
            return pause;
        }
    }

    private UnPauseFunc unPause;

    public UnPauseFunc UnPause
    {
        get
        {
            return unPause;
        }
    }

    private IsPausedFunc isPaused;

    public IsPausedFunc IsPaused
    {
        get
        {
            return isPaused;
        }
    }

    private StopFunc stop;

    public StopFunc Stop
    {
        get
        {
            return stop;
        }
    }

    private GetLengthFunc getLength;

    public GetLengthFunc GetLength
    {
        get
        {
            return getLength;
        }
    }

    private GetOutputTimeFunc getOutputTime;

    public GetOutputTimeFunc GetOutputTime
    {
        get
        {
            return getOutputTime;
        }
    }

    private SetOutputTimeFunc setOutputTime;

    public SetOutputTimeFunc SetOutputTime
    {
        get
        {
            return setOutputTime;
        }
    }

    private SetVolumeFunc setVolume;

    public SetVolumeFunc SetVolume
    {
        get
        {
            return setVolume;
        }
    }

    private SetPanFunc setPan;

    public SetPanFunc SetPan
    {
        get
        {
            return setPan;
        }
    }

    private SAVSAInitFunc savsaInit;

    public SAVSAInitFunc SAVSAInit
    {
        get
        {
            return savsaInit;
        }

        set
        {
            SetDelegate(value, ref savsaInit, 17 * IntPtr.Size);
        }
    }

    private SAVSADeInitFunc savsaDeInit;

    public SAVSADeInitFunc SAVSADeInit
    {
        get
        {
            return savsaDeInit;
        }

        set
        {
            SetDelegate(value, ref savsaDeInit, 18 * IntPtr.Size);
        }
    }

    private SAAddPCMDataFunc saAddPCMData;

    public SAAddPCMDataFunc SAAddPCMData
    {
        get
        {
            return saAddPCMData;
        }

        set
        {
            SetDelegate(value, ref saAddPCMData, 19 * IntPtr.Size);
        }
    }

    private SAGetModeFunc saGetMode;

    public SAGetModeFunc SAGetMode
    {
        get
        {
            return saGetMode;
        }

        set
        {
            SetDelegate(value, ref saGetMode, 20 * IntPtr.Size);
        }
    }

    private SAAddFunc saAdd;

    public SAAddFunc SAAdd
    {
        get
        {
            return saAdd;
        }

        set
        {
            SetDelegate(value, ref saAdd, 21 * IntPtr.Size);
        }
    }

    private VSAAddPCMDataFunc vsaAddPCMData;

    public VSAAddPCMDataFunc VSAAddPCMData
    {
        get
        {
            return vsaAddPCMData;
        }

        set
        {
            SetDelegate(value, ref vsaAddPCMData, 22 * IntPtr.Size);
        }
    }

    private VSAGetModeFunc vsaGetMode;

    public VSAGetModeFunc VSAGetMode
    {
        get
        {
            return vsaGetMode;
        }

        set
        {
            SetDelegate(value, ref vsaGetMode, 23 * IntPtr.Size);
        }
    }

    private VSAAddFunc vsaAdd;

    public VSAAddFunc VSAAdd
    {
        get
        {
            return vsaAdd;
        }

        set
        {
            SetDelegate(value, ref vsaAdd, 24 * IntPtr.Size);
        }
    }

    private VSASetInfoFunc vsaSetInfo;

    public VSASetInfoFunc VSASetInfo
    {
        get
        {
            return vsaSetInfo;
        }

        set
        {
            SetDelegate(value, ref vsaSetInfo, 25 * IntPtr.Size);
        }
    }

    private DSP_isactiveFunc dsp_isactive;

    public DSP_isactiveFunc DSP_isactive
    {
        get
        {
            return dsp_isactive;
        }

        set
        {
            SetDelegate(value, ref dsp_isactive, 26 * IntPtr.Size);
        }
    }

    private DSP_dosamplesFunc dsp_dosamples;

    public DSP_dosamplesFunc DSP_dosamples
    {
        get
        {
            return dsp_dosamples;
        }

        set
        {
            SetDelegate(value, ref dsp_dosamples, 27 * IntPtr.Size);
        }
    }

    private EQSetFunc eqSet;

    public EQSetFunc EQSet
    {
        get
        {
            return eqSet;
        }
    }

    private SetInfoFunc setInfo;

    public SetInfoFunc SetInfo
    {
        get
        {
            return setInfo;
        }

        set
        {
            SetDelegate(value, ref setInfo, 29 * IntPtr.Size);
        }
    }

    public IntPtr OutMod
    {
        get
        {
            return Marshal.ReadIntPtr(Ptr, OffsetOfMethodTable + 30 * IntPtr.Size);
        }
    }

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate IntPtr PluginGetter();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ConfigFunc(IntPtr hwndParent);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void AbountFunc(IntPtr hwndParent);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void InitFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void QuitFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public delegate void GetFileInfoFunc(string file, string title, out int length_in_ms);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int InfoBoxFunc(string file, IntPtr hwndParent);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int IsOurFileFunc(string fn);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int PlayFunc(string fn);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void PauseFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void UnPauseFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int IsPausedFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void StopFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int GetLengthFunc();            // get length in ms

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int GetOutputTimeFunc();        // returns current output time in ms. (usually returns outMod->GetOutputTime()

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SetOutputTimeFunc(int time_in_ms); // seeks to point in stream (in ms). Usually you signal your thread to seek, which seeks and calls outMod->Flush()..

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SetVolumeFunc(int volume); // from 0 to 255.. usually just call outMod->SetVolume

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SetPanFunc(int pan);   // from -127 to 127.. usually just call outMod->SetPan

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SAVSAInitFunc(int maxlatency_in_ms, int srate);        // call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open()

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SAVSADeInitFunc(); // call in Stop()

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SAAddPCMDataFunc(IntPtr PCMData, int nch, int bps, int timestamp);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int SAGetModeFunc();        // gets csa (the current type (4=ws,2=osc,1=spec))

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int SAAddFunc(IntPtr data, int timestamp, int csa); // sets the spec data, filled in by winamp

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void VSAAddPCMDataFunc(IntPtr PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int VSAGetModeFunc(out int specNch, out int waveNch); // use to figure out what to give to VSAAdd

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int VSAAddFunc(IntPtr data, int timestamp); // filled in by winamp, called by plug-in

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void VSASetInfoFunc(int srate, int nch); // <-- Correct (benski, dec 2005).. old declaration had the params backwards

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int DSP_isactiveFunc();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int DSP_dosamplesFunc(ref short samples, int numsamples, int bps, int nch, int srate);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void EQSetFunc(int on, byte[] data, int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore.

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void SetInfoFunc(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :)
}

I've already put a setter for the functions that you'll need to implement.

Use it like:

var mod = new In_Module();
mod.LoadMidiModule();

//var form = new Form1();

// Doesn't work, but looking at 
// http://dolphin-emu.googlecode.com/svn-history/r3174/trunk/Externals/MusicMod/Player/Src/InputPlugin.cpp
// it seems that the plugins often try to hook the WindowProc.
// I'm not sure if it is ok in Winforms
//mod.hMainWindow = form.Handle;
mod.Init();

// Note that you will have to implement:

//SAVSAInit
//SAVSADeInit
//SAAddPCMData
//SAGetMode
//SAAdd
//VSAAddPCMData
//VSAGetMode
//VSAAdd
//VSASetInfo
//dsp_dosamples
//dsp_isactive
//SetInfo

Note the comment!

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Thanks, but now I have another problem with the library. See the edit on the question. – IS4 May 08 '15 at 19:48
  • @IllidanS4 Begin by adding to each delegate a `[UnmanagedFunctionPointer(CallingConvention.Cdecl)]`, including the `PluginGetter` – xanatos May 08 '15 at 19:54
  • Done, with the results same. – IS4 May 08 '15 at 19:57
  • @IllidanS4 But then you were doing the opposite, that is useless. But you don't even need the `Marshal` function. See the added text to the response. – xanatos May 08 '15 at 20:25
  • The code you provided unfortunately crashes on `getmod()`. – IS4 May 08 '15 at 20:45
  • 1
    @IllidanS4 I've updated the response. Note that there seems to be a problem if you try to use a WinForm. You could try setting it to `NULL`. Or perhaps using an older version of `in_midi.dll` (I've read the ones from Winamp 2.* where easier, because the interface was less cluttered). The best thing is that you find some C code that uses a Winamp plugin and then try to convert it. Note that first you need to test the C code to be **working**. Using an undocumented C library from a C program is difficult. Trying to make it work through the fiddly PInvoke is suicide. – xanatos May 09 '15 at 06:03