简体   繁体   中英

Attempt to load the C runtime library incorrectly in Winamp's in_midi.dll

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.

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM