C# Com Interop с визуализацией Windows Media Player (с примером кода)

Я пытаюсь создать плагин Визуализации Windows Media Player (WMP) в C#. Я довольно плохо знаком с представлением C# к COM и, возможно, пропустил что-то основное. Я сохранился с этим в течение 3 дней (приблизительно 20 часов) и не закончил единственную проблему, которую я опишу ниже.

Для тех, кто не знает, визуализация WMP является симпатичными изображениями, которые показывают в медиаплеере при слушании музыки.

Вкратце: WMP назовет определенные методы на моем COM-интерфейсе C#, но не других.

Мне установили WMP 11

Я загрузил Последний Windows SDK, который содержит мастер плагина C++ для компиляции рабочего образца визуализации. Этот образец регистры и работы без проблем в WMP.

dev набор содержит заголовочный файл C++, названный effects.h, который содержит 2 интерфейса, которые должны быть реализованы, чтобы заставить плагин работать с WMP. Это не появляется к намного более сложному, чем это.

Здесь они

MIDL_INTERFACE("D3984C13-C3CB-48e2-8BE5-5168340B4F35")
IWMPEffects : public IUnknown
{
public:
    virtual /* [helpstring][local] */ HRESULT STDMETHODCALLTYPE Render( 
        /* [in] */ TimedLevel *pLevels,
        /* [in] */ HDC hdc,
        /* [in] */ RECT *prc) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE MediaInfo( 
        /* [in] */ LONG lChannelCount,
        /* [in] */ LONG lSampleRate,
        /* [in] */ BSTR bstrTitle) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetCapabilities( 
        /* [out] */ DWORD *pdwCapabilities) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTitle( 
        /* [out] */ BSTR *bstrTitle) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPresetTitle( 
        /* [in] */ LONG nPreset,
        /* [out] */ BSTR *bstrPresetTitle) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPresetCount( 
        /* [out] */ LONG *pnPresetCount) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetCurrentPreset( 
        /* [in] */ LONG nPreset) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetCurrentPreset( 
        /* [out] */ LONG *pnPreset) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE DisplayPropertyPage( 
        /* [in] */ HWND hwndOwner) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GoFullscreen( 
        /* [in] */ BOOL fFullScreen) = 0;

    virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RenderFullScreen( 
        /* [in] */ TimedLevel *pLevels) = 0;

};

MIDL_INTERFACE("695386EC-AA3C-4618-A5E1-DD9A8B987632")
IWMPEffects2 : public IWMPEffects
{
public:
    virtual HRESULT STDMETHODCALLTYPE SetCore( 
        /* [in] */ IWMPCore *pPlayer) = 0;

    virtual HRESULT STDMETHODCALLTYPE Create( 
        /* [in] */ HWND hwndParent) = 0;

    virtual HRESULT STDMETHODCALLTYPE Destroy( void) = 0;

    virtual HRESULT STDMETHODCALLTYPE NotifyNewMedia( 
        /* [in] */ IWMPMedia *pMedia) = 0;

    virtual HRESULT STDMETHODCALLTYPE OnWindowMessage( 
        /* [in] */ UINT msg,
        /* [in] */ WPARAM WParam,
        /* [in] */ LPARAM LParam,
        /* [in] */ LRESULT *plResultParam) = 0;

    virtual HRESULT STDMETHODCALLTYPE RenderWindowed( 
        /* [in] */ TimedLevel *pData,
        /* [in] */ BOOL fRequiredRender) = 0;

};

Поскольку я упомянул, мое знание COM мой не быть лучшим. Это - то, что я сделал для портирования его на C#.

Я преобразовал интерфейсы в следующее

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace WmpTestPlugin
{

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("D3984C13-C3CB-48e2-8BE5-5168340B4F35")]
    public interface IWmpEffects
    {
        int Render(ref TimedLevel pLevels, IntPtr Hdc, ref RECT pRC);
        int MediaInfo(int lChannelCount, int lSampleRate, string bstrTitle);
        int GetCapabilities(ref int pdwCapabilities);
        int GetTitle(ref string bstrTitle);
        int GetPresetTitle([In] int nPreset, [MarshalAs(UnmanagedType.BStr)] ref string bstrPresetTitle);
        int GetPresetCount(ref int count);
        int SetCurrentPreset(int currentpreset);
        int GetCurrentPreset(ref int currentpreset);
        int DisplayPropertyPage(IntPtr hwndOwner);
        int GoFullScreen(bool fFullscreen);
        int RenderFullScreen(ref TimedLevel pLevels);
    }

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("695386EC-AA3C-4618-A5E1-DD9A8B987632")]
    public interface IWmpEffects2 : IWmpEffects
    {
        int SetCore(IntPtr pPlayer);
        int Create(IntPtr hwndParent);
        int Destroy();
        int NotifyNewMedia(IntPtr pMedia);
        int OnWindowMessage(int Msg, int WParam, int LParam, ref int plResultParam);
        int RenderWindowed(ref TimedLevel pData, bool fRequiredRender);
    }

    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct Data
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
        public byte[] Data0;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
        public byte[] Data1;
    }

    [ComVisible(true)]
    public enum PlayerState
    {
        Stop_State,
        Pause_State,
        Play_State
    }

    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct TimedLevel
    {
        public Data Frequency;
        public Data Waveform;
        public PlayerState State;
        public long TimeStamp;
    }

    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
}

Код для класса, реализовывая интерфейсы следующие

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using WmpTestPlugin;
using System.IO;
using System.Windows.Forms;



    [ComVisible(true)]
    [Guid("C476FF24-5E5C-419d-9110-05EC2EED8511")]
    //[ProgId("WmpTestPlugin.WmpTest")]
    [ClassInterface(ClassInterfaceType.None)]
    public class TestPlugin : IWmpEffects2
    {
        [DllImport("user32.dll", EntryPoint = "GetClientRect")]
        private static extern bool getClientRect(IntPtr windowHandle, ref IntPtr rectangle);

        private const int EFFECT_CANGOFULLSCREEN = 1;
        private const int EFFECT_HASPROPERTYPAGE = 2;

        private const int S_OK = 0;
        private const int S_FALSE = 1;
        private const int E_ABORT = unchecked((int)0x80004004);
        private const int E_ACCESSDENIED = unchecked((int)0x80070005);
        private const int E_FAIL = unchecked((int)0x80004005);
        private const int E_HANDLE = unchecked((int)0x80070006);
        private const int E_INVALIDARG = unchecked((int)0x80070057);
        private const int E_NOINTERFACE = unchecked((int)0x80004002);
        private const int E_NOTIMPL = unchecked((int)0x80004001);
        private const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
        private const int E_POINTER = unchecked((int)0x80004003);
        private const int E_UNEXPECTED = unchecked((int)0x8000FFFF);


        public TestPlugin()
        {
            _parentHwnd = IntPtr.Zero;
            _preset = 0;
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : Construct{1}", DateTime.Now.ToString(), Environment.NewLine));

        }

        ~TestPlugin()
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : Deconstruct{1}", DateTime.Now.ToString(), Environment.NewLine));
        }

        #region IWmpEffects2 Members

        /// 
        /// Set WMP core interface
        /// 
        /// 
        /// 
        public int SetCore(IntPtr pPlayer)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : SetCore{1}", DateTime.Now.ToString(), Environment.NewLine));

            // release any existing WMP core interfaces
            //ReleaseCore();

            if (pPlayer == IntPtr.Zero)
                return S_OK;

            _playerCore = pPlayer;

            //connect up any events
            return S_OK;
        }

        /// 
        /// Invoked when the visualization should be initialized.
        ///
        /// If hwndParent != NULL, RenderWindowed() will be called and the visualization
        /// should draw into the window specified by hwndParent. This will be the
        /// behavior when the visualization is hosted in a window.
        ///
        /// If hwndParent == NULL, Render() will be called and the visualization
        /// should draw into the DC passed to Render(). This will be the behavior when
        /// the visualization is hosted windowless (like in a skin for example).
        /// 
        /// 
        /// 
        public int Create(IntPtr hwndParent)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : Create{1}", DateTime.Now.ToString(), Environment.NewLine));

            _parentHwnd = hwndParent;
            return S_OK;
        }

        /// 
        /// Invoked when the visualization should be released.
        /// Any resources allocated for rendering should be released.
        /// 
        /// 
        public int Destroy()
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : Destroy{1}", DateTime.Now.ToString(), Environment.NewLine));
            _parentHwnd = IntPtr.Zero;
            return S_OK;
        }

        /// 
        /// Invoked when a new media stream begins playing.
        /// The visualization can inspect this object for properties (like name or artist)
        /// that might be interesting for visualization.
        /// 
        /// 
        /// 
        public int NotifyNewMedia(IntPtr pMedia)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : NotifyNewMedia{1}", DateTime.Now.ToString(), Environment.NewLine));
            return S_OK;
        }

        /// 
        /// Window messages sent to the parent window.
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public int OnWindowMessage(int Msg, int WParam, int LParam, ref int plResultParam)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : OnWindowMessage{1}", DateTime.Now.ToString(), Environment.NewLine));
            // return S_OK only if the plugin has handled the window message
            // return S_FALSE to let the defWindowProc handle the message

            //if (_parentHwnd == IntPtr.Zero)
                //m_NonWindowedRenderer.OnWindowMessage(&m_RenderContext, msg, WParam, LParam, plResultParam);
            //else
            //    m_WindowedRenderer.OnWindowMessage(&m_RenderContext, msg, WParam, LParam, plResultParam);

            return S_FALSE;
        }

        /// 
        /// Called when an effect should render itself to the screen.
        /// The fRequiredRender flag specifies if an update is required, otherwise the
        /// update is optional. This allows visualizations that are fairly static (for example,
        /// album art visualizations) to only render when the parent window requires it,
        /// instead of n times a second for dynamic visualizations.
        /// 
        /// 
        /// 
        /// 
        public int RenderWindowed(ref TimedLevel pData, bool fRequiredRender)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : RenderWindowed{1}", DateTime.Now.ToString(), Environment.NewLine));
            //windowed

            // NULL parent window should not happen 
            if (_parentHwnd == IntPtr.Zero)
                return E_UNEXPECTED;

            //RECT rect = new RECT();

            //TestPlugin.getClientRect(_parentHwnd, ref rect);

            //do render//

            return S_OK;
        }

        #endregion

        #region IWmpEffects Members

        /// 
        /// Called when an effect should render itself to the screen.
        /// 
        /// 
        /// 
        /// 
        /// 
        public int Render(ref TimedLevel pLevels, IntPtr Hdc, ref RECT pRC)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : Render{1}", DateTime.Now.ToString(), Environment.NewLine));
            //not windowed


            //do render
            return S_OK;
        }

        /// 
        /// Everytime new media is loaded, this method is called to pass the
        /// number of channels (mono/stereo), the sample rate of the media, and the
        /// title of the media
        /// 
        /// 
        /// 
        /// 
        /// 
        public int MediaInfo(int lChannelCount, int lSampleRate, string bstrTitle)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : MediaInfo{1}", DateTime.Now.ToString(), Environment.NewLine));
            return S_OK;
        }

        /// 
        /// Returns the capabilities of this effect. Flags that can be returned are:
        /// EFFECT_CANGOFULLSCREEN  -- effect supports full-screen rendering
        /// EFFECT_HASPROPERTYPAGE  -- effect supports a property page
        /// 
        /// 
        /// 
        public int GetCapabilities(ref int pdwCapabilities)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GetCapabilities{1}", DateTime.Now.ToString(), Environment.NewLine));
            //no capabilities
            pdwCapabilities = EFFECT_HASPROPERTYPAGE;

            return S_OK;
        }

        /// 
        /// Invoked when a host wants to obtain the title of the effect
        /// 
        /// 
        /// 
        public int GetTitle(ref string bstrTitle)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GetTitle{1}", DateTime.Now.ToString(), Environment.NewLine));
            bstrTitle = "Test Wmp C# Plugin";
            return S_OK;
        }

        /// 
        /// Invoked when a host wants to obtain the title of the given preset
        /// 
        /// 
        /// 
        /// 
        public int GetPresetTitle(int nPreset, ref string bstrPresetTitle)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GetPresetTitle{1}", DateTime.Now.ToString(), Environment.NewLine));
            //bstrPresetTitle = "Default";
            return S_OK;
        }

        /// 
        /// Invoked when a host wants to obtain the number of supported presets
        /// 
        /// 
        /// 
        public int GetPresetCount(ref int count)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GetPresetCount{1}", DateTime.Now.ToString(), Environment.NewLine));
            count = 1;
            return S_OK;
        }



        /// 
        /// Invoked when a host wants to obtain the index of the current preset
        /// 
        /// 
        /// 
        public int SetCurrentPreset(int currentpreset)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : SetCurrentPreset{1}", DateTime.Now.ToString(), Environment.NewLine));
            _preset = currentpreset;
            return S_OK;
        }

        /// 
        /// Invoked when a host wants to obtain the index of the current preset
        /// 
        /// 
        /// 
        public int GetCurrentPreset(ref int currentpreset)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GetCurrentPreset{1}", DateTime.Now.ToString(), Environment.NewLine));
            currentpreset = _preset;
            return S_OK;
        }

        /// 
        /// Invoked when a host wants to display the property page for the effect
        /// 
        /// 
        /// 
        public int DisplayPropertyPage(IntPtr hwndOwner)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : DisplayPropertyPage, Owner={1}{2}", DateTime.Now.ToString(), hwndOwner.ToString(), Environment.NewLine));
            MessageBox.Show("Hello Me!");
            return S_OK;
        }

        public int GoFullScreen(bool fFullscreen)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : GoFullScreen{1}", DateTime.Now.ToString(), Environment.NewLine));
            return S_OK;
        }

        public int RenderFullScreen(ref TimedLevel pLevels)
        {
            File.AppendAllText("C:\\wmp.txt", string.Format("{0} : RenderFullScreen{1}", DateTime.Now.ToString(), Environment.NewLine));
            return S_OK;
        }

        #endregion

        private IntPtr _parentHwnd;
        private int _preset;

        private IntPtr _playerCore;

    }

Поскольку Вы видите, что мой код довольно пуст, не что иное как тупики действительно. Моя отладка проста, но делает задание.

После того как это компилируется со строгим именем, в котором это может быть зарегистрировано:

regasm assemblyname.dll/tlb

затем заглядывавший gac.

Откройте regedit и добавьте следующую информацию:

Под HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MediaPlayer\Objects\Effects

Новый ключ: WmpTestPlugin

Под новым ключом добавляют новое строковое значение:Имя: Значение classid: {C476FF24-5E5C-419d-9110-05EC2EED8511}

Таким образом, мы создали плагин, который соответствует интерфейсам, зарегистрировал его в GAC и сказал медиаплееру, что это там.

Если Вы откроете медиаплеер и щелкнете правой кнопкой по пространству визуализации, то меню появится. В том меню будет наш новый плагин. Когда Вы спустите свою мышь к новому объекту, WMP назовет GetPresetCount на плагине (это зарегистрируется в файл). Затем WMP, как предполагается, называет GetPresetTitle, но он НИКОГДА не делает для меня.

Если Вы открываете tools\options от строки меню и выбираете вкладку Plugins, можно выбрать новый плагин. Если Вы нажмете Properties WMP, то назовет GetCapabilities затем, DisplayPropertyPage и окно сообщения от плагина появятся. WMP затем отказывает. В версии C++ FinalConstruct () называют в интерфейсе CComCoClass - я не имею этого и не знаю, каково это. Я думаю, что это может ниже находиться на одном уровне, чем я использую??

Я попробовал много много вещей заставить это работать включая изменение объявлений метода. Может кто-то смотреть на него и справка. Я посмотрел на всем протяжении сети для решения и ничего не нашел.

Спасибо за чтение, Nanook

5
задан Nanook 19 January 2010 в 09:56
поделиться

1 ответ

После рассмотрения некоторого кода взаимодействия Windows Shell, я обнаружил, что я должен был использовать [Comimport], а не [невидимые] для интерфейсов. Чтобы подписи были равными этому уже зарегистрированному, я использовал [SESERVESIG]. Наследование интерфейса также нуждалась в некоторой помощи, восстановив базовый интерфейс в родительском языке.

Я надеюсь, что это поможет кому-то.

Вот рабочий класс

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace WmpTestPlugin
{
    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("695386EC-AA3C-4618-A5E1-DD9A8B987632")]
    public interface IWmpEffects2 : IWmpEffects
    {
        [PreserveSig]
        new int Render(ref TimedLevel pLevels, IntPtr Hdc, ref RECT pRC);
        [PreserveSig]
        new int MediaInfo(int lChannelCount, int lSampleRate, string bstrTitle);
        [PreserveSig]
        new int GetCapabilities(ref int pdwCapabilities);
        [PreserveSig]
        new int GetTitle(ref string bstrTitle);
        [PreserveSig]
        new int GetPresetTitle(int nPreset, ref string bstrPresetTitle);
        [PreserveSig]
        new int GetPresetCount(ref int count);
        [PreserveSig]
        new int SetCurrentPreset(int currentpreset);
        [PreserveSig]
        new int GetCurrentPreset(ref int currentpreset);
        [PreserveSig]
        new int DisplayPropertyPage(IntPtr hwndOwner);
        [PreserveSig]
        new int GoFullScreen(bool fFullscreen);
        [PreserveSig]
        new int RenderFullScreen(ref TimedLevel pLevels);

        [PreserveSig]
        int SetCore(IntPtr pPlayer);
        [PreserveSig]
        int Create(IntPtr hwndParent);
        [PreserveSig]
        int Destroy();
        [PreserveSig]
        int NotifyNewMedia(IntPtr pMedia);
        [PreserveSig]
        int OnWindowMessage(int Msg, int WParam, int LParam, ref int plResultParam);
        [PreserveSig]
        int RenderWindowed(ref TimedLevel pData, bool fRequiredRender);
    }

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("D3984C13-C3CB-48e2-8BE5-5168340B4F35")]
    public interface IWmpEffects
    {
        [PreserveSig]
        int Render(ref TimedLevel pLevels, IntPtr Hdc, ref RECT pRC);
        [PreserveSig]
        int MediaInfo(int lChannelCount, int lSampleRate, string bstrTitle);
        [PreserveSig]
        int GetCapabilities(ref int pdwCapabilities);
        [PreserveSig]
        int GetTitle(ref string bstrTitle);
        [PreserveSig]
        int GetPresetTitle(int nPreset, ref string bstrPresetTitle);
        [PreserveSig]
        int GetPresetCount(ref int count);
        [PreserveSig]
        int SetCurrentPreset(int currentpreset);
        [PreserveSig]
        int GetCurrentPreset(ref int currentpreset);
        [PreserveSig]
        int DisplayPropertyPage(IntPtr hwndOwner);
        [PreserveSig]
        int GoFullScreen(bool fFullscreen);
        [PreserveSig]
        int RenderFullScreen(ref TimedLevel pLevels);
    }


    //[ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct Data
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
        public byte[] Data0;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)]
        public byte[] Data1;
    }

    //[ComVisible(true)]
    public enum PlayerState
    {
        Stop_State,
        Pause_State,
        Play_State
    }

    //[ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct TimedLevel
    {
        public Data Frequency;
        public Data Waveform;
        public PlayerState State;
        public long TimeStamp;
    }

    //[ComVisible(true)]
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        public int Width { get { return this.Right - this.Left; } }
        public int Height { get { return this.Bottom - this.Top; } }
    }
}
6
ответ дан 14 December 2019 в 13:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: