0

我正在尝试加载一个 Winamp 输入插件并在 C# 中使用它。根据 Winamp SDK,这是加载插件的正确方法:

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

所以我在 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();

但是,在带有 的行上LoadLibrary,会显示一个错误(不是异常)窗口:

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

并且hmod为空。

显然,该插件试图加载 msvcrt90.dll,但即使我在目录中有它,这个错误也会一直显示。

问题解决了,下一个问题出现了。

4

1 回答 1

3

解决了。

将清单添加到您的项目。在清单粘贴的最后:

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

(应该已经有一个评论的例子<dependency>

这将使用 WinSxS 中的 Microsoft.VC90.CRT。转到项目的属性,并检查是否在 Application->Resources 中选择了您的 Manifest。

现在进入项目的属性,并禁用 Enable the Visual Studio hosting project.

您需要将其放在in_midi.dll.exe 的同一文件夹中。

现在它应该可以工作了。

作为参考,我正在使用:

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");
    }
}

如果您需要,我可以将整个项目压缩后传给您。

对于第二个问题......遗憾的是,该函数返回一个指向 a 的指针struct。这是 C# 中的一个问题。如果您只需要从该结构中读取,那将很容易,但您必须对其进行修改。您可以使用Marshal.PtrToStructure+ Marshal.StructureToPtr,但我认为这不是一个好主意(有需要编组的委托和字符串......我不想考虑会发生什么)。上次我需要这样做时,我进行了全手动编组:

[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? :)
}

我已经为您需要实现的功能放置了一个设置器。

像这样使用它:

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

注意评论!

于 2015-05-08T15:46:58.763 回答