Why my implementation of IDocHostUIHandler is ignored - c#

I have created derived control from WebBrowser control that has own IDocHostUIHandler implementation following idea from CreateWebBrowserSiteBase method:
The WebBrowser.WebBrowserSite class provides default implementations
of the OLE IDocHostUIHandler interface. You can provide your own
implementation of this interface or implement any other WebBrowser
ActiveX control interface in order to customize the behavior of the
control.
The problem that is not working. My code looks next way:
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public MyBrowser(){}
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
var manager = new NewWebBrowserSite(this);
return manager;
}
protected class NewWebBrowserSite : WebBrowserSite,
UnsafeNativeMethods.IDocHostUIHandler
{
private MyBrowser host;
public NewWebBrowserSite(MyBrowser h)
: base(h)
{
this.host = h;
}
int UnsafeNativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, NativeMethods.POINT pt, object pcmdtReserved, object pdispReserved)
{
MyBrowser wb = (MyBrowser)this.host;
// other code
}
// rest of IDocHostUIHandler methods
}
My questions are:
Do I have to implement other interfaces to make it working;
Is that by design, I have read some post that it is related to a bug in .net framework implementation of WebBrowser
I know that is possible to go ICustomDoc.SetUIHandler way, but it is not what I am looking for.
I was having idea at some point to give up with c# and do that with unmanaged code. Is that the way?

I've just dealt with exactly the same problem: how to provide a custom implementation of IDocHostUIHandler to WinForms WebBrowser control. The problem is that the base class WebBrowserSite has already implemented its own version of IDocHostUIHandler (which is an internal interface, so it's not possible to explicitly re-implement it in the derived class NewWebBrowserSite). However, in theory it should not be a problem to implement another C# interface with the same GIID and methods layout (because that's all the COM client - the underlying WebBrowser ActiveX Control - cares about in this particular case).
Unfortunately, it was not possible until .NET 4.0. Luckily, now it is, by means of the new ICustomQueryInterface feature:
protected class NewWebBrowserSite : WebBrowserSite,
UnsafeNativeMethods.IDocHostUIHandler
ICustomQueryInterface
{
private MyBrowser host;
public NewWebBrowserSite(MyBrowser h): base(h)
{
this.host = h;
}
int UnsafeNativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, NativeMethods.POINT pt, object pcmdtReserved, object pdispReserved)
{
MyBrowser wb = (MyBrowser)this.host;
// other code
}
// rest of IDocHostUIHandler methods
// ICustomQueryInterface
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
{
if (iid == typeof(UnsafeNativeMethods.IDocHostUIHandler).GUID)
{
ppv = Marshal.GetComInterfaceForObject(this, typeof(UnsafeNativeMethods.IDocHostUIHandler), CustomQueryInterfaceMode.Ignore);
}
else
{
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.NotHandled;
}
return CustomQueryInterfaceResult.Handled;
}
}

You can't simply override the interfaces implemented by a class. If the methods for IDocHostUIHandler are not marked as virtual, you can't replace them.
The fact that the interface is defined in UnsafeNativeMethods is also a clue that you probably shouldn't be messing with it unless you have a very good idea of what you're doing.

Related

VSTO Addin Bidirectional communication

I'm looking into the possibility of having a instance of an VSTO Word Add-in communicate with an instance of a VSTO Excel Add-in.
So far i've found an article from Microsoft on what I thought was going to be the solution:
https://learn.microsoft.com/en-us/visualstudio/vsto/calling-code-in-vsto-add-ins-from-other-office-solutions?view=vs-2022&tabs=csharp
However, when one iterates over the Globals.ThisAddIn.Application.COMAddIns collection, I can only see the current Office product (Word/Excel/PowerPoint/Outlook) installed Addin's.
I have a central project that all of the add-in's share, this includes the shared interface. Each add-in implements that interface and are decorated with attributes as per the above guide.
public interface IInterAddinCommsService
{
void Notify();
}
[ComVisible(true)]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public class WordAddinNotificationService : StandardOleMarshalObject, IInterAddinCommsService
{
public void Notify()
{
}
}
[ComVisible(true)]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
class PowerPointAddinNotificationService : StandardOleMarshalObject, IInterAddinCommsService
{
public void Notify()
{
}
}
And here is my method trying to retrieve the com objects and calling the implemenetd method
public void OnAction(Office.IRibbonControl control, bool IsPressed)
{
//// Notify other addin's
var addins = Globals.ThisAddIn.Application.COMAddIns;
foreach (Office.COMAddIn addin in addins)
{
var service = addin.Object as IInterAddinCommsService;
if (service == null) continue; // skip if object doesn't implement IInterAddinCommsService
service.Notify();
}
}
Any help would be greatly appreciated.
In addition to implementing the interface you need to override the RequestComAddInAutomationService method which returns an object in your add-in that can be used by other solutions. For a code example that demonstrates how to override the RequestComAddInAutomationService method, see Walkthrough: Calling Code in a VSTO Add-in from VBA. For example, that is how it should like:
private WordAddinNotificationService utilities;
protected override object RequestComAddInAutomationService()
{
if (utilities == null)
utilities = new WordAddinNotificationService();
return utilities;
}
Note, the object that you return must be public, it must be visible to COM, and it must expose the IDispatch interface. If the object you return does not meet these requirements, the Visual Studio Tools for Office runtime will throw an InvalidCastException after it calls your implementation.

C# How do you get an instance of a COM interface

I've been doing quite a bit of googling trying to find the standard way to get an instance of a COM interface.
Microsoft provides an example of this in their article COM Interop Part 1: Client Tutorial:
// Create an instance of a COM coclass:
FilgraphManager graphManager = new FilgraphManager();
// See if it supports the IMediaControl COM interface.
// Note that this will throw a System.InvalidCastException if
// the cast fails. This is equivalent to QueryInterface for
// COM objects:
IMediaControl mc = (IMediaControl) graphManager;
// Now you call a method on a COM interface:
mc.Run();
However, it appears as though they are instantiating a COM object and casting it to a COM interface.
For the interface I am interested in, IDesktopWallpaper, there does not seem to be an implementing COM object to instantiate.
An example I found here defines some class that gets instantiated and then casts it to the interface the same way that the msdn example does:
[ComImport, Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
internal class IDesktopWallpaper
{
}
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B"), //B92B56A9-8B55-4E14-9A89-0199BBB6F93B
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface DesktopWallpaperInterface
{
// declared members
}
I'm not understanding what the instantiated object is. It seems like an arbitrary object, it has a GuidAttribute which seems to indicate that it is an actual COM object.
Another example i found here System.Type and System.Runtime.InteropServices.Marshal to instantiate an object and then casts it to the interface:
IntPtr ptrRet;
SHGetMalloc(out ptrRet);
System.Type mallocType = System.Type.GetType("IMalloc");
Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,mallocType);
IMalloc pMalloc = (IMalloc)obj;
This method seems to be requesting a pointer to an existing instance of the interface. I can't find any methods like SHGetMalloc for IDesktopWallpaper in the Windows Shell documentation.
Question
So, long story short, what's the standard way to get an instance of a COM interface?
In the event that there is no one-size-fits-all solution, what are the standard ways that one can use to get an instance of a COM interface and in what circumstance is each of these ways most useful?
Edit
After downloading the Windows 10 SDK and referencing that against the Requirements section of the IDesktopWallpaper interface documentation, I have discovered that you can look up the MIDL from Shobjidl.h and use that in the GuidAttribute for your interface declaration and then look up the CLSID from Shobjidl.idl and use that in conjunction with Type.GetTypeFromCLSID(Guid) and Activator.CreateInstance(Type) to get an instance of an object that implements IDesktopWallpaper.
I also see now that the CLSID is what is used in the second method listed above for the GuidAttribute of the seemingly arbitrary object. It seems like this method allows you to mimic managed instantiation of the object by instantiating the class and then casting the instance to the COM interface.
However
I am still interested to know if this is the best way to do this and what pros and cons may be associated with this method vs others.
You can get a pointer to a COM object reference by a variety of methods:
P/Invoke CoCreateInstance
P/Invoke CLSIDFromProgID → CoCreateInstance
P/Invoke IRunningObjectTable.GetObject
Type.GetTypeFromCLSID → Activator.CreateInstance
Type.GetTypeFromProgID → Activator.CreateInstance
new SomeType() where SomeType is marked with ComImport
Activator.CreateInstance and new SomeType() eventually hit CoCreateInstance (if they do not get intercepted by various in-app-domain stuff). Calls to CoCreateInstance for an out-of-process server will eventually hit IRunningObjectTable with a class moniker (I think). The best option depends on what you are trying to do:
For an in-process server, just use ComImport
For an out-of-process server that is not implemented in .Net, ComImport will work, I would prefer to call CoCreateInstance to pass the right CLSCTX.
For a .net implemented out-of-process server implemented in .Net, you must call CoCreateInstance directly to avoid the "optimizations" added by ComImport that would result in the server being run in-process
If you are dealing with a moniker, use IRunningObjectTable
If you are starting out with a ProgID rather than a CLSID, use either CLSIDFromProgID or Type.GetTypeFromProgID
Regardless of how we get a reference to an object, we start out with IUnknown (object in .Net), and then have to call IUnknown->QueryInterface to get a pointer to a particular interface. Calling QueryInterface in .Net is effected by casting to an interface marked as ComVisible (and usually annotated with GuidAttribute).
In the example you named, you would end up with:
// based off of https://bitbucket.org/ciniml/desktopwallpaper
[ComImport]
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDesktopWallpaper
{
void SetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.LPWStr)] string wallpaper);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetMonitorDevicePathAt(uint monitorIndex);
[return: MarshalAs(UnmanagedType.U4)]
uint GetMonitorDevicePathCount();
[return: MarshalAs(UnmanagedType.Struct)]
Rect GetMonitorRECT([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
void SetBackgroundColor([MarshalAs(UnmanagedType.U4)] uint color);
[return: MarshalAs(UnmanagedType.U4)]
uint GetBackgroundColor();
void SetPosition([MarshalAs(UnmanagedType.I4)] DesktopWallpaperPosition position);
[return: MarshalAs(UnmanagedType.I4)]
DesktopWallpaperPosition GetPosition();
void SetSlideshow(IntPtr items);
IntPtr GetSlideshow();
void SetSlideshowOptions(DesktopSlideshowDirection options, uint slideshowTick);
void GetSlideshowOptions(out DesktopSlideshowDirection options, out uint slideshowTick);
void AdvanceSlideshow([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.I4)] DesktopSlideshowDirection direction);
DesktopSlideshowDirection GetStatus();
bool Enable();
}
[ComImport]
[Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
public class DesktopWallpaper
{
}
[Flags]
public enum DesktopSlideshowOptions
{
None = 0,
ShuffleImages = 0x01
}
[Flags]
public enum DesktopSlideshowState
{
None = 0,
Enabled = 0x01,
Slideshow = 0x02,
DisabledByRemoteSession = 0x04
}
public enum DesktopSlideshowDirection
{
Forward = 0,
Backward = 1
}
public enum DesktopWallpaperPosition
{
Center = 0,
Tile = 1,
Stretch = 2,
Fit = 3,
Fill = 4,
Span = 5,
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
An example use of which would be:
public partial class Form1 : Form
{
private IDesktopWallpaper Wallpaper;
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Wallpaper = (IDesktopWallpaper)new DesktopWallpaper();
uint monitorCount = Wallpaper.GetMonitorDevicePathCount();
for (uint i = 0; i < monitorCount; i++)
{
lbMonitors.Items.Add(Wallpaper.GetMonitorDevicePathAt(i));
}
}
private void lbMonitors_SelectedValueChanged(object sender, EventArgs e)
{
var path = (string)lbMonitors.SelectedItem;
tbWallpaper.Text = Wallpaper.GetWallpaper(path);
}
}
Which produces the form:

Requirements for COM aggregate my custom IRibbonExtensibility implementation?

We have a stripped down Connect class which instantiates the addin's core from an other assembly. Our architectural design is to keep the UI and business logic separated from the loading module (= Connect class), but the restrictions of a shared Addin make us troubles.
what we did in Connect.cs:
[GuidAttribute("XYZ"), ProgId("XYZ")]
public class Connect : Object, IDTExtensibility2, ICustomQueryInterface
{
...
CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid,
out IntPtr ppv)
{
if (iid.Equals(new Guid("000C0396-0000-0000-C000-000000000046")))
{
ppv = Marshal.GetComInterfaceForObject(
UIObject,
typeof(IRibbonExtensibility),
CustomQueryInterfaceMode.Ignore);
return CustomQueryInterfaceResult.Handled;
}
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.NotHandled;
}
}
how the RibbonUI looks like:
public interface IOfficeFluentUI : IRibbonExtensibility
{
...
}
public class OfficeFluentUI : OfficeUIBase, IOfficeFluentUI
{
...
public string GetCustomUI(string RibbonID)
{
...
}
}
The GetInterface function works, it gets the com interface, but unfortunately the GetCustomUI function gets NEVER called.
What did we wrong? Thank You very much for any help.
[edit]
We already know the "Managed COM Aggregation" and "ICustomQueryInterface" articles. Unfortunately, they did not really help.

Create COM object from .NET COM method

Here is a code sample
[Guid("159463FB-A87A-4BBB-BCA1-064CD84495FB")]
public interface ISettingsFactory
{
ISettings GetSettings(string userName);
}
[Guid("FD11B979-0ED1-41fb-8EB0-1234512345D0")]
public interface ISettings
{
string PrivateKey {get;}
}
[Guid("782937826-705F-4be2-1234-A748332D6D1")]
[ClassInterface(ClassInterfaceType.None)]
public class SettingsFactory : ISettingsFactory
{
public ISettings GetSettings(string userName)
{
return new Settings(userName);
}
}
[Guid("8BDC1F18-48FD-4a49-8DF3-D81C6321657B")]
[ClassInterface(ClassInterfaceType.None)]
public class Settings : ISettings
{
private readonly PrivateData privateData;
public Settings(string userName)
{
privateData= PrivateData.Load(userName);
}
public string PrivateKey
{
get { return privateData.Key; }
}
}
The problem is when creating SettingsFactory COM class from VB6 code and calling method GetSettings(userName)
settings = factory.GetSettings(userName);
key = settings.PrivateKey //<--- Exception occurs saying "Read write from protected memory is not allowed" or something like this.
The problem disappears when in GetSettings method I save Settings instance in field of SettingsFactory, so that GC doesn't collect it. Does anyone know why is this happening? I mean why GC collects objects that are exposed to COM? Isn't RCW increases the ref number on the Settings object after GetSettings get called?
I think the problems is that privateData.Key is not being marshalled correctly. I can't see the definition for the type of Key but my guess is it is a ptr to some kind of data and tha this data isn't being marshalled correctly. If thats the case you probably need to use one of the Marhal.PtrToXXX fumctions.
A great place to start if you want to learn more about using COM objects from .net are the MSDN articles on Runtime Callable Wrappers

Interfacing common functionality between controls

I'm not really sure how to ask this question. Suppose I have a class that needs to access certain properties of a Control (for example, Visible and Location). Perhaps I want to use the same class to access properties of another item that have the same name, but the class might not derive from Control. So I tried making an interface:
public interface IThumbnail {
bool Visible { get; set; }
int Height { get; set; }
int Width { get; set; }
Image Image { get; set; }
Point Location { get; set; }
event EventHandler Click;
}
Note that, for example, PictureBox happens to implement this interface. However, because the class definition does not say that it implements IThumbnail, I can't cast PictureBoxes to IThumbnails--I get an InvalidCastException at runtime. But why can't the CLR 'figure out' that PictureBox really does implement IThumbnail (it just doesn't explicitly say it does).
Also, what should I do to handle this situation? I want a way to access some of the PictureBox's properties without having my class know it's modifying a PictureBox.
Thx, Sam
PS- I'm a newbie to interface programming, so I apologize if this is a stupid q.
It's not a stupid question, it's a good one. :)
What you're asking for on the interface is commonly referred to as "duck-typing." It isn't supported right now, but C#4.0 will support it via the new "dynamic" keyword.
You've really got three choices that I'm aware of at this point:
You can go up the tree until you find the common ancestor (probably Component) and then downcast to your supported types. If the downcast fails, you throw or handle appropriately.
Pro: Minimal code duplication.
Con: You're trading away compile-time type safety for runtime type safety. You have to add error checking/handling for invalid casts.
Code:
public void UseThumbnail(Component c)
{
PictureBox p = c as PictureBox;
if(p != null) // do whatever
// so forth
}
You can duplicate functionality as appropriate for everything that you need to implement this functionality for.
Pro: Maintain compile time type safety
Con: You're duplicating code for different types. This can grow into a significant maintenance burden, especially if you're handling more than two similar classes.
Code:
public void UsePictureBox(PictureBox p)
{
// Some code X
}
public void UseOtherControl(OtherControl p)
{
// Some code X
}
You can create your special interface and subclass the classes you want to support to expose that common functionality.
Pro: You get compile time safety and can program against your new interface.
Con: You have to add an empty subclass for everything you're dealing with, and you need to use them.
Code:
public class ThumbnailPictureBox : PictureBox, IThumbnail
{ }
I guess you would have to make you own class that derives from PictureBox. That new class would also implement IThumbnail.
public class MyPictureBox : PictureBox, IThumbnail {
}
The CLR can't figure out that PictureBox implements the same method signature as IThumbnail because it's just not supported yet. The feature you are talking about here is often reffered to as Duck Typing, and it's a feature of dynamic languages such as Ruby - it should be available in .NET and C# with the next release (C# 4) and the DLR (dynamic language runtime).
To get the functionality you require right now, you can write a class that implements the IThumbnail interface and encapsulates an instance of a PictureBox.
public class ThumbnailPictureBox
{
private PictureBox _pictureBox;
public ThumbnailPictureBox(PictureBox pictureBox)
{
_pictureBox = pictureBox;
}
public bool Visible
{
get { return _pictureBox.Visible; }
set { _pictureBox.Visible = value; }
}
// etc...
}
If the class you're interested in creating an interface for is not "sealed" then your could create a new class and inherit from the non-sealed class, implementing the interface.
If it were sealed (and I don't think PictureBox is) you'd probably want to build a wrapper class that implements the interface that you're interested in and contains a PictureBox control, exposing only the members that you're interested in.
You could create your own class that implements the interface and has a PictureBox behind the scenes
class MyPictureBox: IThumbnail
{
private PictureBox _pictureBox = new PictureBox();
bool Visible
{
get
{
return _pictureBox.Visible;
}
set
{
_pictureBox.Visible = value;
}
}
//implement the rest, you get the idea
}
This pattern would also be useful in the event that you want to extend a 3rd party's "PictureBox" that is sealed, in which case you would not be abel to extend from it.
Actually Derik Whittaker just did a nice podcast on this pattern at dimecasts.net here
There are hundreds of interfaces in the .net runtime. If interfaces were left to be implicitly implemented, and the compiler were to go through and match up interfaces automatically to classes, every class you program could be matched to interfaces that you didn't even want it to.
As the other posters have suggested, your situation is exactly what OO is meant to solve ;)
Try extending PictureBox to implement your interface.
The situation you are describing is exactly what Interfaces are for
public interface IThumbnail
{
bool Visible {get; set;}
string FilePath {get; set;}
}
public class Bar : IFoo
{
bool Visible {get; set;}
int SomeNumber {get; set;}
/*
rest of Bar functionality
*/
}
public class SomeClass
{
public void DisplayThumbnail(IThumbnail thumb)
{
//Do Stuff to things.
}
}
Once it is implemented that way, for any aspect of your program that shouldn't have access to any Bar functionality, but SHOULD have access to any IThumbnail functionality, just pass them an object of type IThumbnail
Bar bar = new Bar();
SomeClass someClass = new SomeClass();
someClass.DisplaySomething((IThumbnail) bar);
Now the DisplaySomething function cannot access any Bar exclusive functionality. It can only access the IThumbnail specific parts of it.
EDIT
This question deals with the same issue
Will C#4.0 allow dynamic casting, If not, should it?

Categories