0

I have created a sample template for a future project that implements IDeskBand2.

The registration with gacuti and regasm works, it is also displayed in the toolbar. If I want to display / execute it, the explorer crashes.

For the creation I used the documentation of Mircosoft and pinvoke.

I compared my project with media-control-deskband and tried some things (COMInterop file in my project, compare code etc.) But can't find the cause, guess it's because of a faulty implementation.

Here's my implementation.

IDeskBand2 Interface:

/// <summary>
/// Gets information about a band object.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EB0FE172-1A3A-11D0-89B3-00A0C90A90AC")]
public interface IDeskBand : IDockingWindow
{
    /// <summary>
    /// Gets state information for a band object.
    /// </summary>
    /// <param name="dwBandID">The identifier of the band, assigned by the container. The band object can retain this value if it is required.</param>
    /// <param name="dwViewMode">The view mode of the band object. One of the following values: DBIF_VIEWMODE_NORMAL, DBIF_VIEWMODE_VERTICAL, DBIF_VIEWMODE_FLOATING, DBIF_VIEWMODE_TRANSPARENT.</param>
    /// <param name="pdbi">Pointer to a DESKBANDINFO structure that receives the band information for the object. The dwMask member of this structure indicates the specific information that is being requested.</param>
    /// <returns></returns>
    [PreserveSig]
    int GetBandInfo(UInt32 dwBandID, DeskBandInfoViewMode dwViewMode, ref DESKBANDINFO pdbi);
}


/// <summary>
/// Exposes methods to enable and query translucency effects in a deskband object.
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("79D16DE4-ABEE-4021-8D9D-9169B261D657")]
public interface IDeskBand2 : IDeskBand
{
    /// <summary>
    /// Indicates the deskband's ability to be displayed as translucent.
    /// </summary>
    /// <param name="pfCanRenderComposited">When this method returns, contains a BOOL indicating ability.</param>
    /// <returns>If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.</returns>
    [PreserveSig]
    int CanRenderComposited(out bool pfCanRenderComposited);

    /// <summary>
    /// Gets the composition state.
    /// </summary>
    /// <param name="pfCompositionEnabled">When this method returns, contains a BOOL that indicates state.</param>
    /// <returns>If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.</returns>
    [PreserveSig]
    int GetCompositionState(out bool pfCompositionEnabled);

    /// <summary>
    /// Sets the composition state.
    /// </summary>
    /// <param name="fCompositionEnabled">TRUE to enable the composition state; otherwise, FALSE.</param>
    /// <returns>If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.</returns>
    [PreserveSig]
    int SetCompositionState(bool fCompositionEnabled);
}

IDeskBand2 in use: (GetBandInfo function is tested and works correctly.)

    /// <summary>
/// Basic class for a DeskBand object
/// </summary>
/// <example>
/// [Guid("00000000-0000-0000-0000-000000000000")]
/// [DeskBandInfo("Beispiel Erweiterung", "Diese ist eine Beispiel Erweiterung für die Taskleiste.")]
/// public class SampleExtension : DeskBand
/// { /*...*/ }
/// </example>
public class DeskBand : UserControl, IDeskBand2
{
    #region Constants

    private const int S_OK = 0;
    private const int E_NOTIMPL = unchecked((int)0x80004001);

    #endregion

    #region Properties

    /// <summary>
    /// Title of the band object, displayed by default on the left or top of the object.
    /// </summary>
    [Browsable(true)]
    [DefaultValue("")]
    public String Title { get; set; }

    /// <summary>
    /// Minimum size of the band object. Default value of -1 sets no minimum constraint.
    /// </summary>
    [Browsable(true)]
    [DefaultValue(typeof(Size), "-1,-1")]
    public Size MinSize { get; set; }

    /// <summary>
    /// Maximum size of the band object. Default value of -1 sets no maximum constraint.
    /// </summary>
    [Browsable(true)]
    [DefaultValue(typeof(Size), "-1,-1")]
    public Size MaxSize { get; set; }

    /// <summary>
    /// Minimum vertical size of the band object. Default value of -1 sets no maximum constraint. (Used when the taskbar is aligned horinzortal.)
    /// </summary>
    [Browsable(true)]
    [DefaultValue(typeof(Size), "-1,-1")]
    public Size MinSizeVertical { get; set; }

    /// <summary>
    /// Says that band object's size must be multiple of this size. Defauilt value of -1 does not set this constraint.
    /// </summary>
    [Browsable(true)]
    [DefaultValue(typeof(Size), "-1,-1")]
    public Size IntegralSize { get; set; }

    #endregion

    #region IDeskBand2

    public virtual int CanRenderComposited(out bool pfCanRenderComposited)
    {
        pfCanRenderComposited = true;
        return S_OK;
    }

    public int GetCompositionState(out bool pfCompositionEnabled)
    {
        pfCompositionEnabled = false;
        return S_OK;
    }

    public int SetCompositionState(bool fCompositionEnabled)
    {
        fCompositionEnabled = true;
        return S_OK;
    }

    public int GetBandInfo(uint dwBandID, DeskBandInfoViewMode dwViewMode, ref DESKBANDINFO pdbi)
    {
        if (pdbi.dwMask.HasFlag(DeskBandInfoMasks.DBIM_MINSIZE))
        {
            // Support for a vertical taskbar
            // Most examples have no support for a vertical taskbar. Who in hell uses their taskbar vertically? Me! Very practical on a 21:9 monitor.
            if (dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_FLOATING) || dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_VERTICAL))
            {
                pdbi.ptMinSize.Y = this.MinSizeVertical.Width;
                pdbi.ptMinSize.X = this.MinSizeVertical.Height;
            }
            else
            {
                pdbi.ptMinSize.X = this.MinSize.Width;
                pdbi.ptMinSize.Y = this.MinSize.Height;
            }
        }
        if (pdbi.dwMask.HasFlag(DeskBandInfoMasks.DBIM_MAXSIZE))
        {
            if (dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_FLOATING) || dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_VERTICAL))
            {
                pdbi.ptMaxSize.Y = this.MaxSize.Width;
                pdbi.ptMaxSize.X = this.MaxSize.Height;
            }
            else
            {
                pdbi.ptMaxSize.X = this.MaxSize.Width;
                pdbi.ptMaxSize.Y = this.MaxSize.Height;
            }
        }
        if (pdbi.dwMask.HasFlag(DeskBandInfoMasks.DBIM_INTEGRAL))
        {
            if (dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_FLOATING) || dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_VERTICAL))
            {
                pdbi.ptIntegral.Y = this.IntegralSize.Width;
                pdbi.ptIntegral.X = this.IntegralSize.Height;
            }
            else
            {
                pdbi.ptIntegral.X = this.IntegralSize.Width;
                pdbi.ptIntegral.Y = this.IntegralSize.Height;
            }
        }

        if (pdbi.dwMask.HasFlag(DeskBandInfoMasks.DBIM_ACTUAL))
        {
            if (dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_FLOATING) || dwViewMode.HasFlag(DeskBandInfoViewMode.DBIF_VIEWMODE_VERTICAL))
            {
                pdbi.ptActual.Y = this.Size.Width;
                pdbi.ptActual.X = this.Size.Height;
            }
            else
            {
                pdbi.ptActual.X = this.Size.Width;
                pdbi.ptActual.Y = this.Size.Height;
            }
        }

        if (pdbi.dwMask.HasFlag(DeskBandInfoMasks.DBIM_TITLE))
        {
            pdbi.wszTitle = this.Title;
        }

        pdbi.dwModeFlags = DeskBandInfoModeFlag.DBIMF_ALWAYSGRIPPER | DeskBandInfoModeFlag.DBIMF_NORMAL | DeskBandInfoModeFlag.DBIMF_VARIABLEHEIGHT;
        pdbi.dwMask = pdbi.dwMask | DeskBandInfoMasks.DBIM_BKCOLOR | DeskBandInfoMasks.DBIM_TITLE; // Testen

        return S_OK;
    }

    public int CloseDW([In] uint dwReserved)
    {
        Dispose(true);
        return S_OK;
    }

    public int ResizeBorderDW(IntPtr prcBorder, [In, MarshalAs(UnmanagedType.IUnknown)] object punkToolbarSite, bool fReserved)
    {
        return E_NOTIMPL;
    }

    public int ShowDW([In] bool fShow)
    {
        if (fShow)
            Show();
        else
            Hide();

        return S_OK;
    }

    public int GetWindow(out IntPtr phwnd)
    {
        phwnd = Handle;
        return S_OK;
    }

    public int ContextSensitiveHelp(bool fEnterMode)
    {
        return S_OK;
    }

    #endregion

    #region COM

    [ComRegisterFunctionAttribute]
    public static void Register(Type t)
    {
        string guid = t.GUID.ToString("B");

        RegistryKey rkClass = Registry.ClassesRoot.CreateSubKey(@"CLSID\" + guid);
        RegistryKey rkCat = rkClass.CreateSubKey("Implemented Categories");

        DeskBandInfoAttribute[] deskBandInfo = (DeskBandInfoAttribute[])
            t.GetCustomAttributes(typeof(DeskBandInfoAttribute), false);

        string _displayName = t.Name;
        string _helpText = t.Name;

        if (deskBandInfo.Length == 1)
        {
            if (deskBandInfo[0].DisplayName != null)
            {
                _displayName = deskBandInfo[0].DisplayName;
            }

            if (deskBandInfo[0].HelpText != null)
            {
                _helpText = deskBandInfo[0].HelpText;
            }
        }

        rkClass.SetValue(null, _displayName);
        rkClass.SetValue("MenuText", _displayName);
        rkClass.SetValue("HelpText", _helpText);

        // TaskBar
        rkCat.CreateSubKey("{00021492-0000-0000-C000-000000000046}");
    }

    [ComUnregisterFunctionAttribute]
    public static void Unregister(Type t)
    {
        string guid = t.GUID.ToString("B");

        DeskBandInfoAttribute[] deskBandInfo = (DeskBandInfoAttribute[])
            t.GetCustomAttributes(typeof(DeskBandInfoAttribute), false);

        Registry.ClassesRoot.CreateSubKey(@"CLSID").DeleteSubKeyTree(guid);
    }

    #endregion

    public DeskBand()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        this.Name = "DeskBand";
    }
}
}

The actual extension without Designer Code:

namespace TaskbarSampleExt
{
    [Guid("2D53D9CC-0288-4511-A387-B330044C63EA")]
    [DeskBandInfo("Beispiel Erweiterung", "Diese ist eine Beispiel Erweiterung für die Taskleiste.")]
    public partial class SampleExtension : DeskBand
    {

        public SampleExtension()
        {
            this.MinSize = new Size(90, 40);
            this.MinSizeVertical = new Size(90, 40);
            this.Title = "Beispiel Erweiterung";

            InitializeComponent();
        }

    }
}

Thank you for your time

Brocken files

patbec
  • 39
  • 6
  • The interface declarations are incorrect, the CLR is gimpy about COM interfaces that inherit an interface other than IUnknown or IDispatch. The way the CLR implements interfaces is incompatible with the way, say, a C++ compiler does it, it is much more flexible about binding with no demand that a class implements them in the order specified by the interface. Too flexible for a COM client that expects the methods in the correct order, you have to include the methods that are "inherited" from the base interface as a workaround. Just boring copy-and-paste. Or just flatten the interface. – Hans Passant Sep 03 '17 at 10:17
  • Thanks for the answer, I changed the interface as in the 2nd example of Simon Mourier. _(In IDeskBand2 the IOleWindow, IDockingWindow etc. interfaces added)_ Now I can start the extension now without crashing, but the form is not shown in the taskbar. Is it possible to debug the extension with Visual Studio (2017) or read the error message from Explorer? – patbec Sep 03 '17 at 13:41
  • Found the problem, in addition the interface IObjectWithSite has to be included in the base class. Thank you for your explanation :) I will continue to read myself into the topic COM. – patbec Sep 03 '17 at 16:14

1 Answers1

1

You didn't copy/paste the original code properly.

In .NET when you declare a derived interface say IDeskBand2 from IDeskBand, you must redeclare all the methods of the base interface (and recursively, you can just omit IUnknown of course).

Like this (if you want the derivation appear in .NET):

public interface IDeskBand2 : IDeskBand
{
    // IOleWindow
    new int GetWindow(out IntPtr phwnd);
    new int ContextSensitiveHelp(bool fEnterMode);

    // IDockingWindow
    new int ShowDW(bool bShow);
    new int CloseDW(UInt32 dwReserved);
    new int ResizeBorderDW(RECT rcBorder, IntPtr punkToolbarSite, bool fReserved);

    // IDeskBand
    new int GetBandInfo(UInt32 dwBandID, DESKBANDINFO.DBIF dwViewMode, ref DESKBANDINFO pdbi);

    // IDeskBand2
    int CanRenderComposited(out bool pfCanRenderComposited);
    ....
}

or simply like this:

public interface IDeskBand2
{
    // IOleWindow
    int GetWindow(out IntPtr phwnd);
    int ContextSensitiveHelp(bool fEnterMode);

    // IDockingWindow
    int ShowDW(bool bShow);
    int CloseDW(UInt32 dwReserved);
    int ResizeBorderDW(RECT rcBorder, IntPtr punkToolbarSite, bool fReserved);

    // IDeskBand
    int GetBandInfo(UInt32 dwBandID, DESKBANDINFO.DBIF dwViewMode, ref DESKBANDINFO pdbi);

    // IDeskBand2
    int CanRenderComposited(out bool pfCanRenderComposited);
    ....
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Thanks for the answer, I've changed the interface. I can start the extension now without crashing, but the form is not displayed in the taskbar. Is it enough to implement only IDeskBand2 in the base class? **class DeskBand : UserControl, IDeskBand2** Is it possible to debug the extension or read the error from Explorer? – patbec Sep 03 '17 at 13:24
  • This is another question. However, official doc says "bands [...] are not supported as of Windows 7" https://msdn.microsoft.com/en-us/library/windows/desktop/bb762064.aspx – Simon Mourier Sep 03 '17 at 13:31
  • I have here an example that works without problems under Windows 10. So far I haven't found a real alternative to DeskBands. – patbec Sep 03 '17 at 13:53
  • Found the problem, in addition the interface IObjectWithSite has to be included in the base class. Thanks for your example, now it works as it should. – patbec Sep 03 '17 at 16:08