[quoted text, click to view] "Dmitriy Lapshin [C# / .NET MVP]" <x-code@no-spam-please.hotpop.com> wrote
in message news:uft8OlX5DHA.2460@TK2MSFTNGP09.phx.gbl...
> Hello,
>
> The reason the shim control is necessary is that .NET user controls are
not
> full-fledged ActiveX controls. While it seems the framework implements the
> necessary OLE interfaces behind the scenes and provides necessary level of
> interoperability, it is still a not supported feature. I have seen several
> posts reporting problems with this way of implementing the Tools | Options
> pages.
Well, this is only half of the truth. Actually, it seems that .NET controls
ARE full fledged ActiveX controls, however,
the Windows.Forms (.NET 1.1) library contains code which explicitly checks
whether the control is hosted by Internet Explorer.
This code fails on a call to IOleClientSite.GetContainer (expecting an
IHTMLDocument2), which is situated in the internal class
Windows.Forms.Control.ActiveXImpl, Property get_IsIE. The fun thing is, the
outcome of this check is stored in two private static fields of this class,
isIE and checkedIE. By forcing these fields to false and true, respectively,
the bad call will never be executed and the site can happily create your
forms as ActiveX controls.
Further, one should set the 'Control' ImplementedCategory in the Registry.
This allows you to even instantiate the control in the ActiveX control test
container application which comes with VS.NET.
Here's how it goes:
call the following method BEFORE calling DTE.Windows.CreateToolWindow(...):
public static void SetForceNonIEContext()
{
Type tControl = typeof(Control);
Type [] nested =
tControl.GetNestedTypes(BindingFlags.Public|BindingFlags.NonPublic);
Type tActiveXImpl = null;
foreach (Type t in nested)
{
Console.WriteLine("{0}", t.FullName);
if (t.Name == "ActiveXImpl")
{
tActiveXImpl = t;
break;
}
}
FieldInfo checkedIE = tActiveXImpl.GetField("checkedIE",
BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);
FieldInfo isIE = tActiveXImpl.GetField("isIE",
BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);
Console.WriteLine("checkedIE={0}", checkedIE.GetValue(null));
Console.WriteLine("isIE={0}", isIE.GetValue(null));
checkedIE.SetValue(null, true);
isIE.SetValue(null, false);
}
To register the ActiveX object as control, add the following methods to your
control classes (either your ToolWindow ToolsOptionsPage class):
using System.Runtime.InteropServices;
[ComRegisterFunction]
public static void RegisterAxControl( Type cls )
{
try
{
string sKey = string.Format(@"CLSID\{0}", cls.GUID.ToString("B"));
Console.WriteLine("RegisterClass: {0}, sKey = {1}", cls.FullName,
sKey);
// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sKey,true);
// And create the 'Control' key - this allows it to show up in
// the ActiveX control container
RegistryKey ctrl = k.CreateSubKey("Control") ;
ctrl.Close();
// Next create the CodeBase entry - needed if not string named and
GACced.
RegistryKey inprocServer32 = k.OpenSubKey( "InprocServer32" , true )
;
inprocServer32.SetValue("CodeBase" ,
Assembly.GetExecutingAssembly().CodeBase ) ;
inprocServer32.Close() ;
// Finally close the main key
k.Close();
}
catch (Exception exc)
{
Console.WriteLine(exc.ToString());
throw;
}
}
[ComUnregisterFunction]
public static void UnregisterAxControl( Type cls )
{
try
{
string sKey = string.Format(@"CLSID\{0}", cls.GUID.ToString("B"));
Console.WriteLine("UnregisterClass: {0}, sKey = {1}", cls.FullName,
sKey);
// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sKey,true);
// Delete the 'Control' key, but don't throw an exception if it does
not exist
k.DeleteSubKey ( "Control" , false ) ;
// Next open up InprocServer32
RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" ,
true ) ;
// And delete the CodeBase key, again not throwing if missing
k.DeleteSubKey ( "CodeBase" , false ) ;
// Finally close the main key
k.Close ( );
}
catch (Exception exc)
{
Console.WriteLine(exc.ToString());
throw;
}
}
BTW. to test whether the
[quoted text, click to view] > So I'd suggest sticking to the shim control just to be safe. I would also
> like to hear from Microsoft on how managed user controls still can be
hosted
> as ActiveX ones. Is it really that the Control class has OLE support built
> in?
Yep. Complete support is there, but it's rather a bug that it fails for
non-IE applications.
[quoted text, click to view] > --
> Dmitriy Lapshin [C# / .NET MVP]
> X-Unity Test Studio
>
http://x-unity.miik.com.ua/teststudio.aspx
> Bring the power of unit testing to VS .NET IDE
>
> "Szymon Madejczyk" <smad@icslab.agh.edu.pl> wrote in message
> news:bv6teg$61a$1@news.onet.pl...
> > Hello,
> >
> > I've created UserControl that implements IDTToolsOptionsPage, all in
> > managed code, registered and use it in my add in. It works fine.
> > However I then found info in msdn:
> >
>
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vxtskcreatingcustomtoolsoptionspages.asp
> >
> > "...To create an Options page with a .NET Windows Form using Visual
> > Basic .NET or Visual C# .NET, in addition to implementing the
> > IDTToolsOptionsPage interface, you must implement a shim control in
> > Visual C++ .NET to enable the custom page to display..."
> >
> > Are there any consequences related with using managed code directly I
> > should be aware of?
> > Or rather managed code options pages are now supported and msdn
> > documantation is not up-to-date (because link looks like from vs 7.0).
> >
> > Thanks
> >
> > Szymon Madejczyk
>