问题
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#, after learning how to add a msvcr90.dll dependency to the manifest:
[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();
In_Module mod = (In_Module)Marshal.PtrToStructure(modptr, typeof(In_Module));
Form form = new Form();
IntPtr hwnd = form.Handle;
mod.hMainWindow = hwnd;
mod.hDllInstance = hmod;
Marshal.StructureToPtr(mod, modptr, false);
mod.Init(); //exception here
[StructLayout(LayoutKind.Sequential)]
public struct In_Module //ported from IN2.H in the Winamp SDK
{
public int version;
public string description;
public IntPtr hMainWindow;
public IntPtr hDllInstance;
public string FileExtensions;
public int is_seekable;
public int UsesOutputPlug;
public ConfigFunc Config;
public AbountFunc About;
public InitFunc Init;
public QuitFunc Quit;
public GetFileInfoFunc GetFileInfo;
public InfoBoxFunc InfoBox;
public IsOurFileFunc IsOurFile;
public PlayFunc Play;
public PauseFunc Pause;
public UnPauseFunc UnPause;
public IsPausedFunc IsPaused;
public StopFunc Stop;
public GetLengthFunc GetLength;
public GetOutputTimeFunc GetOutputTime;
public SetOutputTimeFunc SetOutputTime;
public SetVolumeFunc SetVolume;
public SetPanFunc SetPan;
public SAVSAInitFunc SAVSAInit;
public SAVSADeInitFunc SAVSADeInit;
public SAAddPCMDataFunc SAAddPCMData;
public SAGetModeFunc SAGetMode;
public SAAddFunc SAAdd;
public VSAAddPCMDataFunc VSAAddPCMData;
public VSAGetModeFunc VSAGetMode;
public VSAAddFunc VSAAdd;
public VSASetInfoFunc VSASetInfo;
public dsp_isactiveFunc dsp_isactive;
public dsp_dosamplesFunc dsp_dosamples;
public IntPtr EQSet;
public SetInfoFunc SetInfo;
public IntPtr outMod;
public delegate void ConfigFunc(IntPtr hwndParent);
public delegate void AbountFunc(IntPtr hwndParent);
public delegate void InitFunc();
public delegate void QuitFunc();
public delegate void GetFileInfoFunc(string file, StringBuilder title, out int length_in_ms);
public delegate int InfoBoxFunc(string file, IntPtr hwndParent);
public delegate int IsOurFileFunc(string fn);
public delegate int PlayFunc(string fn);
public delegate void PauseFunc();
public delegate void UnPauseFunc();
public delegate int IsPausedFunc();
public delegate void StopFunc();
public delegate int GetLengthFunc(); // get length in ms
public delegate int GetOutputTimeFunc(); // returns current output time in ms. (usually returns outMod->GetOutputTime()
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()..
public delegate void SetVolumeFunc(int volume); // from 0 to 255.. usually just call outMod->SetVolume
public delegate void SetPanFunc(int pan); // from -127 to 127.. usually just call outMod->SetPan
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()
public delegate void SAVSADeInitFunc(); // call in Stop()
public delegate void SAAddPCMDataFunc(IntPtr PCMData, int nch, int bps, int timestamp);
public delegate int SAGetModeFunc(); // gets csa (the current type (4=ws,2=osc,1=spec))
public delegate int SAAddFunc(IntPtr data, int timestamp, int csa); // sets the spec data, filled in by winamp
public delegate void VSAAddPCMDataFunc(IntPtr PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data
public delegate int VSAGetModeFunc(out int specNch, out int waveNch); // use to figure out what to give to VSAAdd
public delegate int VSAAddFunc(IntPtr data, int timestamp); // filled in by winamp, called by plug-in
public delegate void VSASetInfoFunc(int srate, int nch); // <-- Correct (benski, dec 2005).. old declaration had the params backwards
public delegate int dsp_isactiveFunc();
public delegate int dsp_dosamplesFunc(ref short samples, int numsamples, int bps, int nch, int srate);
//public delegate void EQSetFunc(int on, [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]byte[] data, int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore.
public delegate void SetInfoFunc(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :)
}
However, calling the Init()
function raises an AccessViolationException. I am first getting the structure to the In_Module
object in the memory, updating it, and then putting it back, for the module to update. The exception is thrown without the update code, even if I put [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
on all delegates.
来源:https://stackoverflow.com/questions/30133014/using-winamps-in-midi-dll-in-net