I can't seem to find an answer to this.
Does anyone know?
Thanks
Here is the code of a utility class that allows you to deactivate WPF's WebBrowser context menu. It also allows you to suppress script errors (WPF WebBrowser control - how to supress script errors?) and change IE's DOCHOSTUIFLAG.
Usage sample:
public partial class Player : Window
{
private WebBrowserHostUIHandler _wbHandler;
public Player()
{
InitializeComponent();
...
_wbHandler = new WebBrowserHostUIHandler(MyWebBrower);
_wbHandler.IsWebBrowserContextMenuEnabled = false;
}
}
Utility code:
public class WebBrowserHostUIHandler : Native.IDocHostUIHandler
{
private const uint E_NOTIMPL = 0x80004001;
private const uint S_OK = 0;
private const uint S_FALSE = 1;
public WebBrowserHostUIHandler(WebBrowser browser)
{
if (browser == null)
throw new ArgumentNullException("browser");
Browser = browser;
browser.LoadCompleted += OnLoadCompleted;
browser.Navigated += OnNavigated;
IsWebBrowserContextMenuEnabled = true;
Flags |= HostUIFlags.ENABLE_REDIRECT_NOTIFICATION;
}
public WebBrowser Browser { get; private set; }
public HostUIFlags Flags { get; set; }
public bool IsWebBrowserContextMenuEnabled { get; set; }
public bool ScriptErrorsSuppressed { get; set; }
private void OnNavigated(object sender, NavigationEventArgs e)
{
SetSilent(Browser, ScriptErrorsSuppressed);
}
private void OnLoadCompleted(object sender, NavigationEventArgs e)
{
Native.ICustomDoc doc = Browser.Document as Native.ICustomDoc;
if (doc != null)
{
doc.SetUIHandler(this);
}
}
uint Native.IDocHostUIHandler.ShowContextMenu(int dwID, Native.POINT pt, object pcmdtReserved, object pdispReserved)
{
return IsWebBrowserContextMenuEnabled ? S_FALSE : S_OK;
}
uint Native.IDocHostUIHandler.GetHostInfo(ref Native.DOCHOSTUIINFO info)
{
info.dwFlags = (int)Flags;
info.dwDoubleClick = 0;
return S_OK;
}
uint Native.IDocHostUIHandler.ShowUI(int dwID, object activeObject, object commandTarget, object frame, object doc)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.HideUI()
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.UpdateUI()
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.EnableModeless(bool fEnable)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.ResizeBorder(Native.COMRECT rect, object doc, bool fFrameWindow)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID)
{
return S_FALSE;
}
uint Native.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.GetDropTarget(object pDropTarget, out object ppDropTarget)
{
ppDropTarget = null;
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.GetExternal(out object ppDispatch)
{
ppDispatch = Browser.ObjectForScripting;
return S_OK;
}
uint Native.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
{
pstrURLOut = null;
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.FilterDataObject(IDataObject pDO, out IDataObject ppDORet)
{
ppDORet = null;
return E_NOTIMPL;
}
public static void SetSilent(WebBrowser browser, bool silent)
{
Native.IOleServiceProvider sp = browser.Document as Native.IOleServiceProvider;
if (sp != null)
{
Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
object webBrowser;
sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser);
if (webBrowser != null)
{
webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent });
}
}
}
}
internal static class Native
{
[ComImport, Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDocHostUIHandler
{
[PreserveSig]
uint ShowContextMenu(int dwID, POINT pt, [MarshalAs(UnmanagedType.Interface)] object pcmdtReserved, [MarshalAs(UnmanagedType.Interface)] object pdispReserved);
[PreserveSig]
uint GetHostInfo(ref DOCHOSTUIINFO info);
[PreserveSig]
uint ShowUI(int dwID, [MarshalAs(UnmanagedType.Interface)] object activeObject, [MarshalAs(UnmanagedType.Interface)] object commandTarget, [MarshalAs(UnmanagedType.Interface)] object frame, [MarshalAs(UnmanagedType.Interface)] object doc);
[PreserveSig]
uint HideUI();
[PreserveSig]
uint UpdateUI();
[PreserveSig]
uint EnableModeless(bool fEnable);
[PreserveSig]
uint OnDocWindowActivate(bool fActivate);
[PreserveSig]
uint OnFrameWindowActivate(bool fActivate);
[PreserveSig]
uint ResizeBorder(COMRECT rect, [MarshalAs(UnmanagedType.Interface)] object doc, bool fFrameWindow);
[PreserveSig]
uint TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID);
[PreserveSig]
uint GetOptionKeyPath([Out, MarshalAs(UnmanagedType.LPArray)] string[] pbstrKey, int dw);
[PreserveSig]
uint GetDropTarget([In, MarshalAs(UnmanagedType.Interface)] object pDropTarget, [MarshalAs(UnmanagedType.Interface)] out object ppDropTarget);
[PreserveSig]
uint GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);
[PreserveSig]
uint TranslateUrl(int dwTranslate, [MarshalAs(UnmanagedType.LPWStr)] string strURLIn, [MarshalAs(UnmanagedType.LPWStr)] out string pstrURLOut);
[PreserveSig]
uint FilterDataObject(IDataObject pDO, out IDataObject ppDORet);
}
[StructLayout(LayoutKind.Sequential)]
internal struct DOCHOSTUIINFO
{
public int cbSize;
public int dwFlags;
public int dwDoubleClick;
public IntPtr dwReserved1;
public IntPtr dwReserved2;
}
[StructLayout(LayoutKind.Sequential)]
internal struct COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
internal class POINT
{
public int x;
public int y;
}
[ComImport, Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ICustomDoc
{
[PreserveSig]
int SetUIHandler(IDocHostUIHandler pUIHandler);
}
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOleServiceProvider
{
[PreserveSig]
uint QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
}
}
[Flags]
public enum HostUIFlags
{
DIALOG = 0x00000001,
DISABLE_HELP_MENU = 0x00000002,
NO3DBORDER = 0x00000004,
SCROLL_NO = 0x00000008,
DISABLE_SCRIPT_INACTIVE = 0x00000010,
OPENNEWWIN = 0x00000020,
DISABLE_OFFSCREEN = 0x00000040,
FLAT_SCROLLBAR = 0x00000080,
DIV_BLOCKDEFAULT = 0x00000100,
ACTIVATE_CLIENTHIT_ONLY = 0x00000200,
OVERRIDEBEHAVIORFACTORY = 0x00000400,
CODEPAGELINKEDFONTS = 0x00000800,
URL_ENCODING_DISABLE_UTF8 = 0x00001000,
URL_ENCODING_ENABLE_UTF8 = 0x00002000,
ENABLE_FORMS_AUTOCOMPLETE = 0x00004000,
ENABLE_INPLACE_NAVIGATION = 0x00010000,
IME_ENABLE_RECONVERSION = 0x00020000,
THEME = 0x00040000,
NOTHEME = 0x00080000,
NOPICS = 0x00100000,
NO3DOUTERBORDER = 0x00200000,
DISABLE_EDIT_NS_FIXUP = 0x00400000,
LOCAL_MACHINE_ACCESS_CHECK = 0x00800000,
DISABLE_UNTRUSTEDPROTOCOL = 0x01000000,
HOST_NAVIGATES = 0x02000000,
ENABLE_REDIRECT_NOTIFICATION = 0x04000000,
USE_WINDOWLESS_SELECTCONTROL = 0x08000000,
USE_WINDOWED_SELECTCONTROL = 0x10000000,
ENABLE_ACTIVEX_INACTIVATE_MODE = 0x20000000,
DPI_AWARE = 0x40000000
}
Reference mshtml
using mshtml;
Then you can do this by accessing the document:
MainBrowser.Navigate("https://www.domain.com");
MainBrowser.LoadCompleted += (o, args) =>
{
var doc = (HTMLDocumentEvents2_Event) MainBrowser.Document;
doc.oncontextmenu += obj => false;
};
From the internal WebBrowserSite class:
int UnsafeNativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, NativeMethods.POINT pt, object pcmdtReserved, object pdispReserved)
{
// Returning S_FALSE will allow the native control to do default processing,
// i.e., execute the shortcut key. Returning S_OK will cancel the context menu
return NativeMethods.S_FALSE;
}
Hard-baked, you cannot override this. Putting the Winforms WebBrower inside a WindowsFormsHost would be a workaround, it's got a smarter implementation of this method that uses the IsWebBrowserContextMenuEnabled property and allows you to create your own context menu.
This is an old question, but I had a unique problem in that I wanted/needed to use the newer CSProj file format with the .Net Core SDK but it has a known issue with not being able to reference extension libraries, such as mshtml. So I finally found a solution that's possible without a reference to mshtml.
Some inspiration came from https://stackoverflow.com/a/28464764/2646868 where the user cast the WebBrowser.Document property to dynamic.
Assuming webView is the name of your WebBrowser control, hook up the LoadCompleted event like such:
private void webView_LoadCompleted(object sender, NavigationEventArgs e)
{
dynamic document = webView.Document;
document.oncontextmenu += new Func<bool>(() => false);
}
Digging graves it seems, but...
Based on e.g. answer here https://stackoverflow.com/a/6979961/4537127 get IWebBrowser2 interface, then get DWebBrowserEvents2_Event interface, and attach handler to DocumentComplete event
SHDocVw.DWebBrowserEvents2_Event wbEvents = (SHDocVw.DWebBrowserEvents2_Event)myWebBrowser2;
wbEvents.DocumentComplete += new SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler(OnWebBrowserDocumentComplete);
(Above must to be done only once on WebBrowser LoadCompleted event, so add some logic to run it only on first one)
And then in this handler, run sone JavaScript to attach event listener to document, and as bonus, to the iframe in this document
private void OnWebBrowserDocumentComplete(object pDisp, ref object URL)
{
/* try-catch wrap this if you get e.g. DISP_E_EXCEPTION */
MyBrowser.InvokeScript("execScript", new Object[]
{
"document.addEventListener('contextmenu', function(e){ e.preventDefault(); }, false);" +
"var frm = document.getElementById('frm');" +
"frm.onload = function() { frm.contentDocument.body.addEventListener('contextmenu', function(e){ e.preventDefault(); }, false); };",
"JavaScript"
});
}
This will work even if the iframe content is changed. I use this with DITA WebHelp classic running in WPF WebBrowser.
And no mshtml reference needed.
Related
I am developing a BHO for IE in C# that controls the navigation flow of IE by checking the URL in onBeforeNavigateEvent.
Everything works fine except that when a link is opened in a new tab then some of the tabs opened as About:Blank.
I have checked the logs and also debugged the BHO no exception is thrown. However in the case of About:Blank BHO is initiated SetSite and GetSite methods are called but navigation event is not fired.
Also it happens when links are opened in new tab rapidly.
For testing purpose I disabled the addon and IE worked fine i.e. no About:Blank page.
Loading time of BHO is 0.1s and Navigation time is 0.5s
So, what could be the issue ?
The source I followed to build this BHO is here
Current Environment: IE 11, Windows 10
Interop.cs
using System;
using System.Runtime.InteropServices;
namespace IE_BHO
{
[
ComImport(),
ComVisible(true),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")
]
interface IObjectWithSite
{
[PreserveSig]
int SetSite([In, MarshalAs(UnmanagedType.IUnknown)] object site);
[PreserveSig]
int GetSite(ref Guid guid, out IntPtr ppvSite);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OLECMDTEXT
{
public uint cmdtextf;
public uint cwActual;
public uint cwBuf;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public char rgwz;
}
[StructLayout(LayoutKind.Sequential)]
public struct OLECMD
{
public uint cmdID;
public uint cmdf;
}
[ComImport(), ComVisible(true),
Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleCommandTarget
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryStatus(
[In] IntPtr pguidCmdGroup,
[In, MarshalAs(UnmanagedType.U4)] uint cCmds,
[In, Out, MarshalAs(UnmanagedType.Struct)] ref OLECMD prgCmds,
//This parameter must be IntPtr, as it can be null
[In, Out] IntPtr pCmdText);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int Exec(
//[In] ref Guid pguidCmdGroup,
//have to be IntPtr, since null values are unacceptable
//and null is used as default group!
[In] IntPtr pguidCmdGroup,
[In, MarshalAs(UnmanagedType.U4)] uint nCmdID,
[In, MarshalAs(UnmanagedType.U4)] uint nCmdexecopt,
[In] IntPtr pvaIn,
[In, Out] IntPtr pvaOut);
}
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(1)]
public interface IServiceProvider
{
int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
}
[
ComVisible(true),
Guid("4C1D2E51-018B-4A7C-8A07-618452573E42"),
InterfaceType(ComInterfaceType.InterfaceIsDual)
]
public interface IExtension
{
[DispId(1)]
string ActivateEndPoint(string licenseKey, string userAgent);
}
}
BHO.cs
using Microsoft.Win32;
using SHDocVw;
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Expando;
using System.Security.Permissions;
using System.Threading.Tasks;
using mshtml;
using System.Reflection;
using System.Diagnostics;
namespace IE_BHO
{
[
SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode),
ComVisible(true),
Guid("BDCB9FDA-8370-40D9-96C9-9D4B4C25C0D8"),
ClassInterface(ClassInterfaceType.None),
ProgId("myExtension"),
ComDefaultInterface(typeof(IExtension))
]
public class BHO : IObjectWithSite, IOleCommandTarget, IExtension
{
object _site;
IWebBrowser2 _webBrowser2;
#region Implementation of IObjectWithSite
int IObjectWithSite.SetSite(object site)
{
_site = site;
if (site != null)
{
var serviceProv = (IServiceProvider)_site;
var guidIWebBrowserApp = Marshal.GenerateGuidForType(typeof(IWebBrowserApp));
var guidIWebBrowser2 = Marshal.GenerateGuidForType(typeof(IWebBrowser2));
IntPtr intPtr;
serviceProv.QueryService(ref guidIWebBrowserApp, ref guidIWebBrowser2, out intPtr);
_webBrowser2 = (IWebBrowser2)Marshal.GetObjectForIUnknown(intPtr);
((DWebBrowserEvents2_Event)_webBrowser2).BeforeNavigate2 += OnBeforeNavigate2;
((DWebBrowserEvents2_Event)_webBrowser2).BeforeScriptExecute += S2_BeforeScriptExecute;
((DWebBrowserEvents2_Event)_webBrowser2).DownloadComplete += BHO_DownloadComplete;
}
else
{
((DWebBrowserEvents2_Event)_webBrowser2).BeforeNavigate2 -= OnBeforeNavigate2;
((DWebBrowserEvents2_Event)_webBrowser2).BeforeScriptExecute -= S2_BeforeScriptExecute;
((DWebBrowserEvents2_Event)_webBrowser2).DownloadComplete -= BHO_DownloadComplete;
_webBrowser2 = null;
}
return 0;
}
int IObjectWithSite.GetSite(ref Guid guid, out IntPtr ppvSite)
{
IntPtr punk = Marshal.GetIUnknownForObject(_webBrowser2);
int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
Marshal.Release(punk);
return hr;
}
public void OnBeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
{
try
{
UrlObj dto = UrlManager.CheckUrl(URL.ToString());
if (dto.IsInList)
{
Cancel = true;
_webBrowser2.Navigate2("www.google.com");
}
}
catch (Exception ex)
{
}
}
private void S2_BeforeScriptExecute(object pDispWindow)
{
ExposeMethodstoJS();
}
private void BHO_DownloadComplete()
{
ExposeMethodstoJS();
}
private void ExposeMethodstoJS(string calledBy)
{
try
{
HTMLDocument doc = _webBrowser.Document as HTMLDocument;
if (doc != null)
{
IHTMLWindow2 tmpWindow = doc.parentWindow;
dynamic window = tmpWindow;
IExpando windowEx = (IExpando)window;
PropertyInfo p = windowEx.AddProperty("myExtension");
p.SetValue(windowEx, this);
}
}
catch (Exception ex)
{
}
}
#endregion
#region Implementation of IOleCommandTarget
int IOleCommandTarget.QueryStatus(IntPtr pguidCmdGroup, uint cCmds, ref OLECMD prgCmds, IntPtr pCmdText)
{
return 0;
}
int IOleCommandTarget.Exec(IntPtr pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
return 0;
}
#endregion
#region Implementation of IExtension
string IExtension.GetDetails()
{
return "Methods Exposed";
}
#endregion
#region COMRegistration
[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.CreateSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects");
RegistryKey guidKey = registryKey.CreateSubKey(type.GUID.ToString("B"));
registryKey.Close();
guidKey.Close();
}
[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects", true);
string guid = type.GUID.ToString("B");
if (registryKey != null)
{
registryKey.DeleteSubKey(guid, false);
}
}
#endregion COMRegistration
}
}
Which version of IE and OS are you using?
I think the issue occurs because the new tab is opened and navigated at almost the same time. For the detailed information, you could refer to this link.
You could also try the resolution in the article: Install the most recent cumulative security update for Internet Explorer.
How can I make the System.Windows.Forms.WebBrowser control use de DPI_AWARE flag to display documents with DPI support? If anyone can post a code example would be appreciated.
Edit 1: added more details
The problem is that px unit is treated as normal pixel unit (not web-like), so sizes at 150% dpi configuration differs a lot and even more on higher dpi settings. WebBrowser.Version is 11.x
class myWebBrowser: System.Windows.Forms.WebBrowser, IDocHostUIHandler
{
private const uint E_NOTIMPL = 0x80004001;
private const uint S_OK = 0;
private const uint S_FALSE = 1;
private HostUIFlags flags = HostUIFlags.DPI_AWARE;
public myWebBrowser()
{
this.DocumentCompleted += OnLoadCompleted;
}
private void OnLoadCompleted(object sender, WebBrowserDocumentCompletedEventArgs args)
{
ICustomDoc doc = this.Document.DomDocument as ICustomDoc;
if (doc != null)
{
\\code reaches this point
doc.SetUIHandler(this);
}
}
uint GetHostInfo(ref DOCHOSTUIINFO info)
{
info.dwFlags = (int)flags;
info.dwDoubleClick = 0;
return S_OK;
}
\\other IDocHostUIHandler methods...
}
The interfaces are defined here:
[ComImport, Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ICustomDoc
{
[PreserveSig]
int SetUIHandler(IDocHostUIHandler pUIHandler);
}
[ComImport, Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDocHostUIHandler
{
[PreserveSig]
uint ShowContextMenu(int dwID, POINT pt, [MarshalAs(UnmanagedType.Interface)] object pcmdtReserved, [MarshalAs(UnmanagedType.Interface)] object pdispReserved);
[PreserveSig]
uint GetHostInfo(ref DOCHOSTUIINFO info);
[PreserveSig]
uint ShowUI(int dwID, [MarshalAs(UnmanagedType.Interface)] object activeObject, [MarshalAs(UnmanagedType.Interface)] object commandTarget, [MarshalAs(UnmanagedType.Interface)] object frame, [MarshalAs(UnmanagedType.Interface)] object doc);
\\etc.
}
[StructLayout(LayoutKind.Sequential)]
internal struct DOCHOSTUIINFO
{
public int cbSize;
public int dwFlags;
public int dwDoubleClick;
public IntPtr dwReserved1;
public IntPtr dwReserved2;
}
I want to intercept drag&drop action on WebBrowser control. To be more precise, I want to be able to cancel or approve drag&drop event based on file extension (and process that file outside WebBrowser) and I want to make drag&drop icon look like regular icon: http://snag.gy/DUjMc.jpg, not like this one: http://snag.gy/ExX19.jpg.
I believe for this I need to implement custom IDocHostUIHandler and intercept GetDropTarget. Unfortunately, I have no success in doing that. I am using this code as my 'base' code: https://stackoverflow.com/a/19739699/2758677 and this part, that I made: http://pastebin.com/Ux947Eck. GetDropTarget is never called.
Here's a complete WinForms-based example that works, IDocHostUIHandler.GetDropTarget does get called.
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CustomWebBrowser
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
var wb = new ImprovedWebBrowser();
wb.Dock = DockStyle.Fill;
this.Controls.Add(wb);
wb.Visible = true;
wb.DocumentText = "<body contentEditable='true'><b>Hello from ImprovedWebBrowser!</b></body>";
}
}
public class ImprovedWebBrowser : WebBrowser
{
/// <summary>
/// provide custom WebBrowserSite,
/// where we override IDocHostUIHandler and call the base implementation
/// More info: http://stackoverflow.com/a/19739699/1768303
/// </summary>
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new ImprovedWebBrowserSite(this);
}
#region ImprovedWebBrowserSite
[ClassInterface(ClassInterfaceType.None)]
protected class ImprovedWebBrowserSite :
WebBrowserSite,
NativeMethods.IDocHostUIHandler,
IDisposable,
ICustomQueryInterface
{
ImprovedWebBrowser _host;
NativeMethods.IDocHostUIHandler _baseIDocHostUIHandler;
IntPtr _unkInnerAggregated;
IntPtr _unkOuter;
Inner _inner;
// constructor
public ImprovedWebBrowserSite(WebBrowser host) :
base(host)
{
_host = (ImprovedWebBrowser)host;
// get the CCW object for this
_unkOuter = Marshal.GetIUnknownForObject(this);
Marshal.AddRef(_unkOuter);
try
{
// aggregate the CCW object with the helper Inner object
_inner = new Inner(this);
_unkInnerAggregated = Marshal.CreateAggregatedObject(_unkOuter, _inner);
// obtain private WebBrowserSite COM interfaces
_baseIDocHostUIHandler = (NativeMethods.IDocHostUIHandler)Marshal.GetTypedObjectForIUnknown(_unkInnerAggregated, typeof(NativeMethods.IDocHostUIHandler));
}
finally
{
Marshal.Release(_unkOuter);
}
}
~ImprovedWebBrowserSite()
{
// need to work out the reference counting for GC to work correctly
Debug.Print("ImprovedWebBrowserSite object finalized.");
}
void IDisposable.Dispose()
{
base.Dispose();
_baseIDocHostUIHandler = null;
if (_unkInnerAggregated != IntPtr.Zero)
{
Marshal.Release(_unkInnerAggregated);
_unkInnerAggregated = IntPtr.Zero;
}
if (_unkOuter != IntPtr.Zero)
{
Marshal.Release(_unkOuter);
_unkOuter = IntPtr.Zero;
}
}
#region Inner
// Inner as aggregated object
class Inner :
ICustomQueryInterface,
IDisposable
{
object _outer;
Type[] _interfaces;
public Inner(object outer)
{
_outer = outer;
_interfaces = _outer.GetType().BaseType.GetInterfaces();
}
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
{
if (_outer != null)
{
var guid = iid;
var iface = _interfaces.FirstOrDefault((t) => t.GUID == guid);
if (iface != null)
{
var unk = Marshal.GetComInterfaceForObject(_outer, iface, CustomQueryInterfaceMode.Ignore);
if (unk != IntPtr.Zero)
{
ppv = unk;
return CustomQueryInterfaceResult.Handled;
}
}
}
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.Failed;
}
~Inner()
{
// need to work out the reference counting for GC to work correctly
Debug.Print("Inner object finalized.");
}
public void Dispose()
{
_outer = null;
_interfaces = null;
}
}
#endregion
#region ICustomQueryInterface
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
{
// CustomQueryInterfaceMode.Ignore is to avoid infinite loop during QI.
if (iid == typeof(NativeMethods.IDocHostUIHandler).GUID)
{
ppv = Marshal.GetComInterfaceForObject(this, typeof(NativeMethods.IDocHostUIHandler), CustomQueryInterfaceMode.Ignore);
}
else
{
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.NotHandled;
}
return CustomQueryInterfaceResult.Handled;
}
#endregion
#region IDocHostUIHandler
int NativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, ref NativeMethods.POINT pt, IntPtr pcmdtReserved, IntPtr pdispReserved)
{
return _baseIDocHostUIHandler.ShowContextMenu(dwID, ref pt, pcmdtReserved, pdispReserved);
}
int NativeMethods.IDocHostUIHandler.GetHostInfo(ref NativeMethods.DOCHOSTUIINFO info)
{
Debug.Print("IDocHostUIHandler.GetHostInfo");
return _baseIDocHostUIHandler.GetHostInfo(ref info);
}
int NativeMethods.IDocHostUIHandler.ShowUI(int dwID, IntPtr activeObject, IntPtr commandTarget, IntPtr frame, IntPtr doc)
{
return _baseIDocHostUIHandler.ShowUI(dwID, activeObject, commandTarget, frame, doc);
}
int NativeMethods.IDocHostUIHandler.HideUI()
{
return _baseIDocHostUIHandler.HideUI();
}
int NativeMethods.IDocHostUIHandler.UpdateUI()
{
return _baseIDocHostUIHandler.UpdateUI();
}
int NativeMethods.IDocHostUIHandler.EnableModeless(bool fEnable)
{
return _baseIDocHostUIHandler.EnableModeless(fEnable);
}
int NativeMethods.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
{
return _baseIDocHostUIHandler.OnDocWindowActivate(fActivate);
}
int NativeMethods.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
{
return _baseIDocHostUIHandler.OnFrameWindowActivate(fActivate);
}
int NativeMethods.IDocHostUIHandler.ResizeBorder(ref NativeMethods.COMRECT rect, IntPtr doc, bool fFrameWindow)
{
return _baseIDocHostUIHandler.ResizeBorder(ref rect, doc, fFrameWindow);
}
int NativeMethods.IDocHostUIHandler.TranslateAccelerator(ref NativeMethods.MSG msg, ref Guid group, int nCmdID)
{
return _baseIDocHostUIHandler.TranslateAccelerator(ref msg, ref group, nCmdID);
}
int NativeMethods.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
{
return _baseIDocHostUIHandler.GetOptionKeyPath(pbstrKey, dw);
}
int NativeMethods.IDocHostUIHandler.GetDropTarget(IntPtr pDropTarget, out IntPtr ppDropTarget)
{
Debug.Print("IDocHostUIHandler.GetDropTarget");
return _baseIDocHostUIHandler.GetDropTarget(pDropTarget, out ppDropTarget);
}
int NativeMethods.IDocHostUIHandler.GetExternal(out object ppDispatch)
{
return _baseIDocHostUIHandler.GetExternal(out ppDispatch);
}
int NativeMethods.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
{
return _baseIDocHostUIHandler.TranslateUrl(dwTranslate, strURLIn, out pstrURLOut);
}
int NativeMethods.IDocHostUIHandler.FilterDataObject(IntPtr pDO, out IntPtr ppDORet)
{
return _baseIDocHostUIHandler.FilterDataObject(pDO, out ppDORet);
}
#endregion
}
#endregion
}
public static class NativeMethods
{
#region IDocHostUIHandler
public enum DOCHOSTUIDBLCLICK
{
DEFAULT = 0x0,
SHOWPROPERTIES = 0x1,
SHOWCODE = 0x2
}
public enum DOCHOSTUIFLAG
{
DIALOG = 0x1,
DISABLE_HELP_MENU = 0x2,
NO3DBORDER = 0x4,
SCROLL_NO = 0x8,
DISABLE_SCRIPT_INACTIVE = 0x10,
OPENNEWWIN = 0x20,
DISABLE_OFFSCREEN = 0x40,
FLAT_SCROLLBAR = 0x80,
DIV_BLOCKDEFAULT = 0x100,
ACTIVATE_CLIENTHIT_ONLY = 0x200,
NO3DOUTERBORDER = 0x00200000,
THEME = 0x00040000,
NOTHEME = 0x80000,
DISABLE_COOKIE = 0x400
}
[StructLayout(LayoutKind.Sequential)]
public struct DOCHOSTUIINFO
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.I4)]
public int dwFlags;
[MarshalAs(UnmanagedType.I4)]
public int dwDoubleClick;
[MarshalAs(UnmanagedType.I4)]
public int dwReserved1;
[MarshalAs(UnmanagedType.I4)]
public int dwReserved2;
}
[StructLayout(LayoutKind.Sequential)]
public struct COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
POINT pt;
}
[ComImport(), Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDocHostUIHandler
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowContextMenu(
[In, MarshalAs(UnmanagedType.U4)]
int dwID,
[In]
ref POINT pt,
[In]
IntPtr pcmdtReserved,
[In]
IntPtr pdispReserved);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetHostInfo(
[In, Out]
ref DOCHOSTUIINFO info);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowUI(
[In, MarshalAs(UnmanagedType.I4)]
int dwID,
[In]
IntPtr activeObject,
[In]
IntPtr commandTarget,
[In]
IntPtr frame,
[In]
IntPtr doc);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int HideUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int UpdateUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int EnableModeless(
[In, MarshalAs(UnmanagedType.Bool)]
bool fEnable);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnDocWindowActivate(
[In, MarshalAs(UnmanagedType.Bool)]
bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnFrameWindowActivate(
[In, MarshalAs(UnmanagedType.Bool)]
bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ResizeBorder(
[In]
ref COMRECT rect,
[In]
IntPtr doc,
bool fFrameWindow);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateAccelerator(
[In]
ref MSG msg,
[In]
ref Guid group,
[In, MarshalAs(UnmanagedType.I4)]
int nCmdID);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetOptionKeyPath(
[Out, MarshalAs(UnmanagedType.LPArray)]
String[] pbstrKey,
[In, MarshalAs(UnmanagedType.U4)]
int dw);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetDropTarget(
[In]
IntPtr pDropTarget,
[Out]
out IntPtr ppDropTarget);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetExternal(
[Out, MarshalAs(UnmanagedType.IDispatch)]
out object ppDispatch);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateUrl(
[In, MarshalAs(UnmanagedType.U4)]
int dwTranslate,
[In, MarshalAs(UnmanagedType.LPWStr)]
string strURLIn,
[Out, MarshalAs(UnmanagedType.LPWStr)]
out string pstrURLOut);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int FilterDataObject(
[In]
IntPtr pDO,
[Out]
out IntPtr ppDORet);
}
#endregion
}
}
I'm creating a console application that is able to remotely capture screenshots of websites. Everything is working except the fact that I can't avoid Certificate errors. Every time I get popup message that I'm unable to pass.
I tried using:
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
But it doesn't work.
Also tried the solution found here:
http://www.codeproject.com/Articles/31163/Suppressing-Hosted-WebBrowser-Control-Dialogs
but but it doesnt seem to work for webbrowser invoked from console application.
Any ideas?
The webbrowser control uses WinInet as its network stack. Setting the ServerCertificateValidationCallback would have no effect on WinInet.
To handle the certificate error, you need to implement an IHttpSecurity service and pass to the webbrowser on request. The webbrowser queries host services via IServiceProvider implemented on the ActiveX host. Assuming you are using Windows Forms, you need to do the following:
derive a class from WebBrowser
create a nested class derived from WebBrowser.WebBrowserSite (the only way you can derive from the nested class)
overwrite CreateWebBrowserSiteBase and return a new instance of your webbrowser site.
implement IServiceProvider on the webbrowser site
implement IServiceProvider.QueryService so it returns an IHttpSecurity imepleemntation when the IHttpSecurity service is requested
handle IHttpSecurity.OnSecurityProblem and return S_OK
use the new webbrowser in the form
Example code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void webBrowser1_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.ToString() == "about:blank")
{
//create a certificate mismatch
webBrowser1.Navigate("https://74.125.225.229");
}
}
}
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface UCOMIServiceProvider
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out] out IntPtr ppvObject);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IWindowForBindingUI
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHttpSecurity
{
//derived from IWindowForBindingUI
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
[PreserveSig]
int OnSecurityProblem(
[In, MarshalAs(UnmanagedType.U4)] uint dwProblem);
}
public class MyWebBrowser : WebBrowser
{
public static Guid IID_IHttpSecurity
= new Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b");
public static Guid IID_IWindowForBindingUI
= new Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b");
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int E_NOINTERFACE = unchecked((int)0x80004002);
public const int RPC_E_RETRY = unchecked((int)0x80010109);
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new MyWebBrowserSite(this);
}
class MyWebBrowserSite : WebBrowserSite,
UCOMIServiceProvider,
IHttpSecurity,
IWindowForBindingUI
{
private MyWebBrowser myWebBrowser;
public MyWebBrowserSite(MyWebBrowser myWebBrowser)
:base(myWebBrowser)
{
this.myWebBrowser = myWebBrowser;
}
public int QueryService(ref Guid guidService
, ref Guid riid
, out IntPtr ppvObject)
{
if (riid ==IID_IHttpSecurity)
{
ppvObject= Marshal.GetComInterfaceForObject(this
, typeof(IHttpSecurity));
return S_OK;
}
if (riid == IID_IWindowForBindingUI)
{
ppvObject = Marshal.GetComInterfaceForObject(this
, typeof(IWindowForBindingUI));
return S_OK;
}
ppvObject = IntPtr.Zero;
return E_NOINTERFACE;
}
public int GetWindow(ref Guid rguidReason
, ref IntPtr phwnd)
{
if (rguidReason == IID_IHttpSecurity
|| rguidReason == IID_IWindowForBindingUI)
{
phwnd = myWebBrowser.Handle;
return S_OK;
}
else
{
phwnd = IntPtr.Zero;
return S_FALSE;
}
}
public int OnSecurityProblem(uint dwProblem)
{
//ignore errors
//undocumented return code, does not work on IE6
return S_OK;
}
}
}
Figured it out finally.
I've been trying to bypass the SSL cert error for a Headless IE browser running as a console app (http://triflejs.org)
ShengJiang provided the bulk of the answer but I still couldn't use Application.Run() as it locks up execution on the main thread and I needed to execute other events in the loop, also, instantiating an ApplicationContext with message pumps seemed way too complicated.
The answer once I got it was exceedingly simple. Just create a loop and run Application.DoEvents()
Here is some working code. I simplified it to suit the question posted here.
Please make sure that:
You are running as a Console Application project.
You add a project reference to System.Windows.Forms.
Hope it helps!
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace IgnoreSSLErrorBrowserConsoleApp
{
public class Program
{
[STAThread]
public static void Main(string[] args)
{
MyWebBrowser browser = new MyWebBrowser();
browser.Navigate("about:blank");
browser.DocumentCompleted += delegate (object obj, WebBrowserDocumentCompletedEventArgs e) {
if (e.Url.ToString() == "about:blank") {
// This is the SSL path where certificate error occurs
browser.Navigate("https://localhost");
}
};
while (browser.ReadyState != WebBrowserReadyState.Complete) {
Application.DoEvents();
// RunOtherEvents();
}
}
}
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface UCOMIServiceProvider
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int QueryService(
[In] ref Guid guidService,
[In] ref Guid riid,
[Out] out IntPtr ppvObject);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IWindowForBindingUI
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
}
[ComImport()]
[ComVisible(true)]
[Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHttpSecurity
{
//derived from IWindowForBindingUI
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetWindow(
[In] ref Guid rguidReason,
[In, Out] ref IntPtr phwnd);
[PreserveSig]
int OnSecurityProblem(
[In, MarshalAs(UnmanagedType.U4)] uint dwProblem);
}
public class MyWebBrowser : WebBrowser
{
public static Guid IID_IHttpSecurity
= new Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b");
public static Guid IID_IWindowForBindingUI
= new Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b");
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int E_NOINTERFACE = unchecked((int)0x80004002);
public const int RPC_E_RETRY = unchecked((int)0x80010109);
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new MyWebBrowserSite(this);
}
class MyWebBrowserSite : WebBrowserSite,
UCOMIServiceProvider,
IHttpSecurity,
IWindowForBindingUI
{
private MyWebBrowser myWebBrowser;
public MyWebBrowserSite(MyWebBrowser myWebBrowser)
: base(myWebBrowser)
{
this.myWebBrowser = myWebBrowser;
}
public int QueryService(ref Guid guidService
, ref Guid riid
, out IntPtr ppvObject)
{
if (riid == IID_IHttpSecurity)
{
ppvObject = Marshal.GetComInterfaceForObject(this
, typeof(IHttpSecurity));
return S_OK;
}
if (riid == IID_IWindowForBindingUI)
{
ppvObject = Marshal.GetComInterfaceForObject(this
, typeof(IWindowForBindingUI));
return S_OK;
}
ppvObject = IntPtr.Zero;
return E_NOINTERFACE;
}
public int GetWindow(ref Guid rguidReason
, ref IntPtr phwnd)
{
if (rguidReason == IID_IHttpSecurity
|| rguidReason == IID_IWindowForBindingUI)
{
phwnd = myWebBrowser.Handle;
return S_OK;
}
else
{
phwnd = IntPtr.Zero;
return S_FALSE;
}
}
public int OnSecurityProblem(uint dwProblem)
{
//ignore errors
//undocumented return code, does not work on IE6
return S_OK;
}
}
}
}
I'm writing a program that needs to be able to extract the thumbnail image from a file. I've gotten a hold of a class, ThumbnailCreator, which is able to do this. The source of this Class is below.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Drawing.Imaging;
internal class ThumbnailCreator : IDisposable
{
// Fields
private IMalloc alloc;
private Size desiredSize;
private bool disposed;
private static readonly string fileExtention = ".jpg";
private Bitmap thumbnail;
// Methods
public ThumbnailCreator()
{
this.desiredSize = new Size(100, 100);
}
public ThumbnailCreator(Size desiredSize)
{
this.desiredSize = new Size(100, 100);
this.DesiredSize = desiredSize;
}
public void Dispose()
{
if (!this.disposed)
{
if (this.alloc != null)
{
Marshal.ReleaseComObject(this.alloc);
}
this.alloc = null;
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
}
this.disposed = true;
}
}
~ThumbnailCreator()
{
this.Dispose();
}
private bool getThumbNail(string file, IntPtr pidl, IShellFolder item)
{
bool CS;
IntPtr hBmp = IntPtr.Zero;
IExtractImage extractImage = null;
try
{
if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
{
int prgf;
IUnknown iunk = null;
Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk);
extractImage = (IExtractImage) iunk;
if (extractImage != null)
{
SIZE sz = new SIZE {
cx = this.desiredSize.Width,
cy = this.desiredSize.Height
};
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT;
int uFlags = (int) flags;
extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
extractImage.Extract(out hBmp);
if (hBmp != IntPtr.Zero)
{
this.thumbnail = Image.FromHbitmap(hBmp);
}
Marshal.ReleaseComObject(extractImage);
extractImage = null;
}
return true;
}
CS = false;
}
catch (Exception)
{
if (hBmp != IntPtr.Zero)
{
UnManagedMethods.DeleteObject(hBmp);
}
if (extractImage != null)
{
Marshal.ReleaseComObject(extractImage);
}
throw;
}
return CS;
}
public Bitmap GetThumbNail(string file)
{
if (!File.Exists(file) && !Directory.Exists(file))
{
throw new FileNotFoundException(string.Format("The file '{0}' does not exist", file), file);
}
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
this.thumbnail = null;
}
IShellFolder folder = getDesktopFolder;
if (folder != null)
{
IntPtr pidlMain;
try
{
int cParsed;
int pdwAttrib;
string filePath = Path.GetDirectoryName(file);
folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
throw;
}
if (pidlMain != IntPtr.Zero)
{
Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
IShellFolder item = null;
try
{
folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (item != null)
{
IEnumIDList idEnum = null;
try
{
item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (idEnum != null)
{
IntPtr pidl = IntPtr.Zero;
bool complete = false;
while (!complete)
{
int fetched;
if (idEnum.Next(1, ref pidl, out fetched) != 0)
{
pidl = IntPtr.Zero;
complete = true;
}
else if (this.getThumbNail(file, pidl, item))
{
complete = true;
}
if (pidl != IntPtr.Zero)
{
this.Allocator.Free(pidl);
}
}
Marshal.ReleaseComObject(idEnum);
}
Marshal.ReleaseComObject(item);
}
this.Allocator.Free(pidlMain);
}
Marshal.ReleaseComObject(folder);
}
return this.thumbnail;
}
private static string PathFromPidl(IntPtr pidl)
{
StringBuilder path = new StringBuilder(260, 260);
if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0)
{
return path.ToString();
}
return string.Empty;
}
// Properties
private IMalloc Allocator
{
get
{
if (!this.disposed && (this.alloc == null))
{
UnManagedMethods.SHGetMalloc(out this.alloc);
}
return this.alloc;
}
}
public Size DesiredSize
{
get
{
return this.desiredSize;
}
set
{
this.desiredSize = value;
}
}
private static IShellFolder getDesktopFolder
{
get
{
IShellFolder ppshf;
UnManagedMethods.SHGetDesktopFolder(out ppshf);
return ppshf;
}
}
public Bitmap ThumbNail
{
get
{
return this.thumbnail;
}
}
// Nested Types
private enum EIEIFLAG
{
IEIFLAG_ASPECT = 4,
IEIFLAG_ASYNC = 1,
IEIFLAG_CACHE = 2,
IEIFLAG_GLEAM = 0x10,
IEIFLAG_NOBORDER = 0x100,
IEIFLAG_NOSTAMP = 0x80,
IEIFLAG_OFFLINE = 8,
IEIFLAG_ORIGSIZE = 0x40,
IEIFLAG_QUALITY = 0x200,
IEIFLAG_SCREEN = 0x20
}
[Flags]
private enum ESFGAO
{
SFGAO_CANCOPY = 1,
SFGAO_CANDELETE = 0x20,
SFGAO_CANLINK = 4,
SFGAO_CANMOVE = 2,
SFGAO_CANRENAME = 0x10,
SFGAO_CAPABILITYMASK = 0x177,
SFGAO_COMPRESSED = 0x4000000,
SFGAO_CONTENTSMASK = -2147483648,
SFGAO_DISPLAYATTRMASK = 0xf0000,
SFGAO_DROPTARGET = 0x100,
SFGAO_FILESYSANCESTOR = 0x10000000,
SFGAO_FILESYSTEM = 0x40000000,
SFGAO_FOLDER = 0x20000000,
SFGAO_GHOSTED = 0x80000,
SFGAO_HASPROPSHEET = 0x40,
SFGAO_HASSUBFOLDER = -2147483648,
SFGAO_LINK = 0x10000,
SFGAO_READONLY = 0x40000,
SFGAO_REMOVABLE = 0x2000000,
SFGAO_SHARE = 0x20000,
SFGAO_VALIDATE = 0x1000000
}
[Flags]
private enum ESHCONTF
{
SHCONTF_FOLDERS = 0x20,
SHCONTF_INCLUDEHIDDEN = 0x80,
SHCONTF_NONFOLDERS = 0x40
}
[Flags]
private enum ESHGDN
{
SHGDN_FORADDRESSBAR = 0x4000,
SHGDN_FORPARSING = 0x8000,
SHGDN_INFOLDER = 1,
SHGDN_NORMAL = 0
}
[Flags]
private enum ESTRRET
{
STRRET_WSTR,
STRRET_OFFSET,
STRRET_CSTR
}
[ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IEnumIDList
{
[PreserveSig]
int Next(int celt, ref IntPtr rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(ref ThumbnailCreator.IEnumIDList ppenum);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
private interface IExtractImage
{
void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);
void Extract(out IntPtr phBmpThumbnail);
}
[ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMalloc
{
[PreserveSig]
IntPtr Alloc(int cb);
[PreserveSig]
IntPtr Realloc(IntPtr pv, int cb);
[PreserveSig]
void Free(IntPtr pv);
[PreserveSig]
int GetSize(IntPtr pv);
[PreserveSig]
int DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")]
private interface IShellFolder
{
void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes);
void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList);
void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.IShellFolder ppvOut);
void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);
[PreserveSig]
int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut);
void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)] ref ThumbnailCreator.ESFGAO rgfInOut);
void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut);
void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName);
void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")]
private interface IUnknown
{
[PreserveSig]
IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);
[PreserveSig]
IntPtr AddRef();
[PreserveSig]
IntPtr Release();
}
[StructLayout(LayoutKind.Sequential)]
private struct SIZE
{
public int cx;
public int cy;
}
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto)]
private struct STRRET_ANY
{
// Fields
[FieldOffset(4)]
public IntPtr pOLEString;
[FieldOffset(0)]
public ThumbnailCreator.ESTRRET uType;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=4)]
private struct STRRET_CSTR
{
public ThumbnailCreator.ESTRRET uType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=520)]
public byte[] cStr;
}
private class UnManagedMethods
{
// Methods
[DllImport("gdi32", CharSet=CharSet.Auto)]
internal static extern int DeleteObject(IntPtr hObject);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
}
}
As a test I created a quick console application, the code of which is below. This test worked fine and was able to extract a thumbnail, and saved it to a PNG file.
static void Main(string[] args)
{
try
{
Console.WriteLine("press a key to extract");
System.Console.ReadKey();
string path = #"C:\somefile.xyz";
ThumbnailCreator creator = new ThumbnailCreator();
creator.DesiredSize = new Size(600, 600);
Bitmap bm = creator.GetThumbNail(path);
bm.Save(#"C:\blah.png", System.Drawing.Imaging.ImageFormat.Png);
Console.WriteLine("press a key to exit");
System.Console.ReadKey();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
My problem is the real application I want to use this in runs as a plug-in for another application. When I try to run the same test code in the plug-in creator.GetThumbNail(path); returns null.
I debugged a little further and found that in the method ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item) the line extractImage.Extract(out hBmp); returns IntPtr.Zero. Whereas in the console application that works this method actually returns a number. Maybe this is the problem? Or maybe this problem happens before this. Honestly, I'm completely lost when it comes to Interop and Windows API stuff.
Does anyone know of any possible reasons why this class would work in a standalone console application, but not as part of a plug-in for another application?
Update
Here's a bit more detail of how this plug-in is created. To add a custom command to this program you create a class that implements an ICommand interface from is API. The interface has a single Execute() method where code is place that should run when the user executes the custom command from the program. The native application, however, is what actualy instantiates my command class and calls the execute method.
What could be different about the way that the native app instantiates my command class and or calls the Execute() method that would prevent the thumbnail extraction from working?
Just ran across this issue myself and I do say, it took some time to figure out that it was a bitness issue. In your scenario your console app was probably compiled as x64 and your Windows app as x86.
The ExtractImage interface (BB2E617C-0920-11d1-9A0B-00C04FC2D6C1) on a 64bit OS is only accessible from 64bit applications (because explorer is 64bit), thus resulting in a "Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))" exception in applications compiled as x86.
Code that calls the Shell API must be in a COM Single Threaded Apartment, try putting [STAThread] attribute on the thread that calls this.