问题
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.
回答1:
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!
来源:https://stackoverflow.com/questions/30121116/attempt-to-load-the-c-runtime-library-incorrectly-in-winamps-in-midi-dll