本文将描述如何在.NET之外利用Windows Forms控件。
编写控件
从Visual Studio中创建一个新的控件项目-我的示例都是用C#编写的,但VB.NET也可以使用。
在这篇文章中,Garry建议:“首先,创建一个托管的用户控件项目 - 可以是Windows窗体类库或控件库项目。使用用户控件设计器按照您的意愿设计自定义用户控件(使用任何您喜欢的标准控件)。"
Add controls etc to the form, put in the code etc.
Add in the following using clauses...
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using Microsoft.Win32;
- Attribute your class so that it gets a ProgID. This isn't strictly necessary as one will be generated, but it's almost always best to be
explicit.
[ProgId("Prisoner.PrisonerControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
This assigns the ProgID, and also defines that the interface
exposed should be 'AutoDual' - this crufts up a default interface for
you from all public, non-static members of the class. If this isn't
what you want, use one of the other options.
- Update the project properties so that your assembly is registered for COM interop.
If you're using VB.NET, you also need a strong named assembly.
Curiously in C# you don't - and it seems to be a feature of the
environment rather than a feature of the compiler or CLR.
- Add the following two methods into your class.
[ComRegisterFunction()]
public static void RegisterClass ( string key )
{
// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open the CLSID\{guid} key for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),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 ( ) ;
}
The RegisterClass function is attributed with ComRegisterFunction -
this static method will be called when the assembly is registered for
COM Interop. All I do here is add the 'Control' keyword to the
registry, plus add in the CodeBase entry.
CodeBase is interesting - not only for .NET controls. It defines a URL
path to where the code can be found, which could be an assembly on
disk as in this instance, or a remote assembly on a web server
somewhere. When the runtime attempts to create the control, it will
probe this URL and download the control as necessary. This is very
useful when testing .NET components, as the usual caveat of residing
in the same directory (etc) as the .EXE does not apply.
[ComUnregisterFunction()]
public static void UnregisterClass ( string key )
{
StringBuilder sb = new StringBuilder ( key ) ;
sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
// Open HKCR\CLSID\{guid} for write access
RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(),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 ( ) ;
}
The second function will remove the registry entries added when (if)
the class is unregistered - it's always a good suggestion to tidy up
as you go.
Now you are ready to compile & test your control.
注意:这似乎对于Excel(经过非常有限的测试)可以正常工作,部分适用于PowerPoint,但在Word中失败了。可能,一些更多的OLEMISC值可以改善这种情况;可能有一些消息需要挂钩;可能还需要实现更多接口…我只是勉强让它以非常有限的方式工作,这说明这可能不是你想要在任何严肃的场合使用的技术。