This article highlights some of the new toolkits and components coming out of Redmond for COM Interop.
The Interop Forms Toolkit, the Interop UserControl Prototype, and the techniques used in Sedna’s NET4COM allow Visual FoxPro developers to incorporate .NET components into their applications.
The .NET and COM platforms are not designed to communicate directly with one another and Microsoft knew it needed to provide a way for developers to bridge the gap. Microsoft came up with a way to wrap the respective components in two special wrappers known as the Runtime Callable Wrapper (RCW) and the COM Callable Wrapper (CCW). The RCW and CCW act as proxies to facilitate communication between COM and .NET.
RCW
In .NET, developers use RCW, also known as an interop assembly, to communicate with COM components. A developer can create an RCW for a COM component by either using the Add Reference feature in Visual Studio 2005 or by using tlbimp.exe. To create the RCW for a COM component through the Add Reference feature, a developer performs the following steps:
Visual Studio will then create an RCW for the selected COM component and save it into the project’s bin directory. As noted above, there is another way to accomplish this. To use the tlbimp.exe to create the RCW for a COM component, a developer performs the following steps (Figure 2):
Using the above steps will cause the tlbimp command-line tool to generate the RCW (MyInteropAssembly.dll), which you can then add to a .NET project by using the Add Reference dialog box.
Using a COM-visible assembly from Visual FoxPro is no different than using any other COM component. This is the magic of the CCW.
An instance of the System.Runtime.InteropServices.TypeLibConverter class generates the interop assembly (RCW) regardless of which of the above methods you use. After the instance of the System.Runtime.InteropServices.TypeLibConverter creates the interop assembly and you’ve added the interop assembly to the project, you can then add a reference to the interop assembly’s namespace in code, utilizing the Imports keyword in Visual Basic or the using keyword in C#, and then reference the classes in the RCW directly. You’re not required to reference the namespace. You could choose to fully qualify the class instead. Just use the RCW in a .NET project just like any other assembly and access the COM component’s public OLE classes in a straightforward and natural way.
CCW
The CCW provides for the reverse scenario than that presented for the RCW. The CCW provides access to .NET components from a COM client, such as a Visual FoxPro application. The way that developers create a .NET component to provide the CCW is a little more complex than creating an RCW, but most developers will find the following steps straightforward and doable.
NET4COM provides a framework that Visual FoxPro developers can use as a guide when creating COM-visible classes in Visual Basic .NET and wrapping them in Visual FoxPro classes.
When creating a CCW from Visual Studio 2005, developers create and build the .NET classes and the resultant assembly in the usual way for the most part. Basically, the only additional things a developer needs to do to expose public .NET classes in an assembly is to perform the following steps before building the project:
Developers can add a reference to the namespace by including either “Imports System.Runtime.InteropServices” in Visual Basic .NET or “using System.Runtime.InteropServices;” in C#.
To make the assembly COM-visible in Visual Studio, select the “Make assembly COM-Visible” check box in the Assembly Information dialog box (Project Properties screen | Application page | Assembly Information button | Make assembly COM-Visible check box) as shown in Figure 3.
Register the assembly in Visual Studio by selecting the “Register for COM interop” check box (Project Properties screen | Compile page) (Figure 4).
Finally, you’ll sign the assembly with a strong name key file. This is done so that the developer can install the assembly into the Global Assembly Cache (GAC), which requires that assemblies be signed. Also, developers can avoid a number of ominous warning messages when they register the assembly into a location other than the GAC using the /codebase switch. More on registering the assembly in a moment; for now, let’s take a look at the two ways to produce the strong name key file that a developer needs to sign the assembly.
Creating a Strong Name Key File
You can create a strong name key file using the Strong Name tool (sn.exe). Follow these steps (Figure 5):
The Strong Name tool will generate a new random key pair and write it out to the specified strong name key file (myproject.snk). You can use this key file to sign the assembly by selecting <Browse…> from the drop-down combo box provided in Visual Studio (Project Properties | Signing page).
You could also create a strong name key file using one of the new features in Visual Studio 2005 that allows developers to generate a new strong name key file from the Signing page of the Project Properties screen.
Note that you can create two types of key files using the Create Strong Name Key dialog box: a non password-protected key file (.snk) or a password-protected key file (.pfx). A password-protected key file is more secure as a developer must know the password in order to use it. Given that a key file is used to uniquely identify the vendor of the assembly, it is best to protect the key file with a password.
After developers complete the above steps, they will specify the strong name key file in the drop-down combo box on the Signing page (Figure 7). Visual Studio 2005 will also add the strong name key file to the project as evidenced by the .pfx or .snk now present in the Solution Explorer’s list of application files.
Building a COM-Visible Assembly
After completing the required steps outlined above, developers can build the assembly in the usual manner using Visual Studio.
The Interop UserControl allows you to add .NET control elements to a UserControl class, and then build an ActiveX control from it.
From the Build menu select Build MyProject. Alternately right-click on the project and select Build from the context menu.
When the project is built, Visual Studio basically compiles the .NET assembly, exports a typelib for the assembly, and then registers the assembly by creating the proper registry entries. Visual Studio generates IDispatch type interfaces for all of the assembly’s public classes. Visual Studio uses these interfaces to expose the classes and their public properties and methods to COM. (The developer can keep the interfaces from being exported by using the ComVisible attribute: <ComVisible(False)> in Visual Basic and [ComVisible(false)] in C#.) In any event, you can now reference the assembly and use it in a COM client, such as Visual FoxPro. Using a COM visible assembly from Visual FoxPro is no different than using any other COM component. This is the magic of the CCW.
Registering a .NET Assembly
While the steps above will certainly make the .NET assembly available to a Visual FoxPro application on the developer’s machine, they don’t address the problem of getting the assembly registered on a user’s machine when the application is installed. There are a number of install builders that support registering .NET assemblies, but for the sake of this article I’ll look at the tool that comes with the .NET Framework that facilitates registering assemblies, Regasm.exe.
The easiest way for Visual FoxPro developers to understand Regasm.exe is to think of it as .NET’s equivalent to COM’s regsvr32. Regasm.exe is quite simply a command-line tool that developers can use to register .NET assemblies. The definition goes a step further than this though in the sense that developers can also use RegAsm to generate and export a typelib for an assembly containing COM-visible types.
To generate a typelib and register the assembly, follow these steps (Figure 8):
Regasm.exe will generate the typelib and register the assembly by creating the necessary registry entries. Note that in the command above I included the full path to RegAsm.exe and used two command-line switches: /codebase and /tlb.
The /tlb switch simply instructs Regasm.exe to generate a type library for the assembly. This switch does the same thing that Visual Studio does when building a project that has been marked as register for interop as described earlier in this article.
Developers should use the /codebase switch whenever they install the assembly into a location other than the GAC. Basically, /codebase tells Regasm.exe to include the path to the assembly in the registry so that the COM client using it can find it. A quick look in the registry will reveal that, in the CLSID key of the registered .NET assembly classes, Regasm added a string value named CodeBase to the InprocServer32\ProductVersion subkeys. This string value is the fully qualified path to the assembly that was registered with the /codebase switch.
Early Binding vs. Late Binding
The reason that creating a CCW is more complex than creating a RCW is because there is a problem with the approach as outlined above. By allowing Visual Studio to autogenerate the interfaces that expose the public classes to COM, the GUIDs that make up the identity of the COM component will change from version to version. This change is fine if the Visual FoxPro application is only accessing the CCW through late binding using the progid, such as:
oObject =
CreateObject("MyAssembly.
MyClass")
Late binding still works because it relies on the PROGID (“MyAssembly.MyClass”), which is used to look up the current CLSID for the class in the registry. Visual FoxPro never compiles the CLSID into the application so the CLSID can change without a problem. As long as the assembly name and class name stay the same (used to generate the PROGID for the assembly), and developers don’t remove relied on properties, methods, or events, everything will continue to work. But, in the case of early binding, Visual FoxPro compiles information regarding the CLSID, Interface IDs, and Event IDs directly into the application. Since these IDs (GUIDs) change with each new version built in Visual Studio, the Visual FoxPro application will no longer work when the new version of the .NET assembly is installed on the user’s system.
Fortunately, there are more advanced techniques that developers can use when creating .NET assemblies for use in COM clients. NET4COM, a component of Sedna, uses many of these techniques well, so let’s take a look at it.
NET4COM
NET4COM is available in the latest Sedna CTP, which you can download from the Microsoft site at the following location:
Microsoft designed the Interop Forms Toolkit to allow you to leverage existing COM client applications while providing new functionality through .NET forms.
http://www.microsoft.com/downloads/details.aspx?FamilyId=808E96E1-3D87-421F-9BA5-4AAFE70C7B21&displaylang=en
Once you’ve installed the Sedna CTP, you can find NET4COM in the similarly named subdirectory. Essentially NET4COM is a COM-visible .NET assembly and a set of Visual FoxPro wrapper classes for using the assembly. NET4COM contains a number of classes that allow Visual FoxPro developers to work with:
- Audio Files
- Windows Clipboard
- File Compression
- Windows Eventlog
- Windows File System
- Mouse
- Network
- Power Status
- Printers
- Processes
- Regular Expressions
- Windows Registry
- Windows Security
- Windows Services
- Special Directories
Developers can already do some of the things NET4COM provides natively in Visual FoxPro. However, NET4COM provides a framework that Visual FoxPro developers can use as a guide when creating COM-visible classes in Visual Basic and wrapping them in Visual FoxPro classes. This framework makes accessing and using the .NET classes in Visual FoxPro extremely easy. NET4COM uses the same techniques to expose all of the .NET classes it contains. Next, I’ll demonstrate this NET4COM technique step by step using one of the simplest NET4COM classes, Mouse.
Note that Microsoft created NET4COM using Visual Basic so C# developers will use slightly different steps and syntax. This being said, using Visual Basic rather than C# offers you a number of reasons that allow you to make COM-visible assemblies more easily and with fewer restrictions, not the least of which is the availability of optional parameters in Visual Basic.
Creating the Mouse Class in Visual Basic 2005
Let me walk you through an example. I’ll add the Mouse class (Mouse.vb in the Solution Explorer) to the project by right-clicking the project and choosing Add -> Class from the context menu. Alternately, from the Project menu I can choose Add Class. Either method will cause Visual Studio to display the Add New Item dialog box (Figure 8). I’ll choose the Class item from the list provided and change the name from Class1.vb to Mouse.vb. I’ll click Add, which will produce a nearly empty Mouse.vb file and add it to the project.
Listing 1 shows the code contained in the Mouse.vb class file. I’ll then add additional code to the Mouse class until it matches the finished NET4COM Mouse class shown in Listing 2. I’ll explore the code in detail to help you fully understand the different parts of the code.
Imports System.Runtime.InteropServices
This reference to the .NET namespace contains the types that Visual Studio requires to create a COM-visible assembly and register it.
<ComVisible(True)> _
<Guid(Mouse.InterfaceId)> _
Public Interface IMouse
Function ButtonsSwapped() As
Boolean
Function WheelExists() As
Boolean
Function WheelScrollLines()
As Integer
End Interface
This code creates the default interface for the Mouse class. This interface, which is of type IDispatch, will be exported by Regasm to the typelib and used by COM clients to access the Mouse class from inside the COM-visible NET4COM assembly. Note in the attributes section (starting with the < sign) that I use the ComVisible attribute to ensure that a COM client can access the interface. I’ll set the GUID attribute next using the Mouse.InterfaceId, which is one of three public constants defined inside the Mouse class in the COM GUIDs Region of the code. (Developers can access static members directly without creating an instance of the class.)
Public Const ClassId As String =
"9ad99cd3-e715-4388-be12-
c36fb68b7e23"
Public Const InterfaceId As
String = "cfa8fe7b-1a63-4118-
887e-8f6625def8b4"
Public Const EventsId As
String = "94d20fa6-bd8a-4f78-
a7f8-734166be1f37"
NET4COM uses the GUIDs above to ensure that the Class IDs, Interface IDs, and Event IDs don’t change when you rebuild and register the assembly. The ability to define these IDs (GUIDs) shores up the issues that the previous approach had when you accessed the classes via Early Binding. This is a very important part of what NET4COM is showing you.
Three function signatures and an interface definition make up the IMouse interface itself. .NET uses a naming convention for interfaces that places a prefix before the name of either an “I” or an “_”. I chose to use the “I” prefix, though the underscore would have been equally correct. The function signatures must match the names and types returned from the same methods in the Mouse class. Additionally, if the methods would have had any parameters, these also would have to match in name and type. When creating a new class it is usually easier to code the class first, and then developers can create the interface definition by cutting and pasting the first lines of the methods that they wrote for the class.
After the interface definition comes the actual Mouse Class definition.
<ComVisible(True)> _
<Guid(Mouse.ClassId)> _
<ComDefaultInterface(GetType
(IMouse))> _
Public Class Mouse
Implements IMouse
The ComVisible attribute ensures that the class is visible to COM clients accessing the assembly. The Guid attribute uses the Mouse.ClassId property as opposed to the Mouse.InterfaceId. A new attribute, ComDefaultInterface, tells Visual Studio to use the IMouse type for the IDispatch interface that COM clients get when accessing this class. IMouse just happens to be the interface that is near the top of the Mouse.vb class file that I explained a moment ago. When a COM client creates an instance of this Mouse class it is going to get back an OLE object that has three functions: ButtonsSwapped, WheelExists, and WheelScrollLines. How all of this ties together should be beginning to make sense.
After the attributes, which modify the way in which the class is built, Visual Studio defines the class as Public Class Mouse, which gives the class the meaningful name Mouse and the required scope (only public classes can be visible to a COM client). The next line, Implements, is a contract between the Mouse class definition and the Interface definition. The contract says that the Mouse class will, at the very least, implement the three functions contained in the IMouse interface. If the Mouse class doesn’t match the IMouse interface, Visual Studio will throw an error.
I’ll skip over the static members that I have already talked about. Look at the next portion of the code, the constructor for the Mouse class:
Public Sub New()
MyBase.New()
End Sub
A .NET class constructor is similar to a Init method of a class created in Visual FoxPro. Visual Studio requires the above constructor for .NET COM-visible classes. While developers can use the constructor to set up default property values and do other stuff, all that’s required is to use the constructor exactly as shown above.
The last part of the Mouse class definition is the class methods that match the function signatures that are in the IMouse interface. All three of the class methods are relatively similar, so I’ll just look at the first one.
Protected Function
ButtonsSwapped() As Boolean
Implements IMouse.
ButtonsSwapped
Return My.Computer.Mouse.
ButtonsSwapped()
End Function
From the first line in the above Function definition, I see that the method is named ButtonSwapped, has a scope of Protected, and will return a Boolean value. The next line sets up a contract between this method and the IMouse interface’s ButtonSwapped signature. The second-to-last line is the Return, which will return the value returned from the call to My.Computer.Mouse.ButtonSwapped(). (Note that the MY namespace in Visual Basic provides a host of these useful functions and Microsoft is including a similar MY class with Sedna.) That’s all there is to the code. When a COM client instantiates an instance of the Mouse class and invokes its ButtonSwapped method, the Mouse class will return a .T. if the user has swapped the left and right mouse buttons and a .F. if they haven’t. Remember that NET4COM’s value is not in what has been implemented, but rather in the examples it provides of how to create .NET classes so that they can be used from inside a Visual FoxPro application. After all, how often does an application need to know if the mouse buttons have been swapped? Give a developer a class and you provide them a solution to a need, teach them how to create the classes and you provide them a solution to all of their needs.
I have already set the COM-visible and Register for Interop properties of the project for NET4COM as outlined earlier in this article, so I’ll save the new Mouse class and rebuild the project.
Creating a Wrapper Class in Visual FoxPro
Open Visual FoxPro and create a new class in the _net4com.vcx (provided in the FFC). You should base this class on the _net4comfactory class (Listing 3) and name the new class _net4com_mouse.
Add a cClass property to the _net4com_mouse class and set its value to “NET4COM.Mouse”. This property is always set to the PROGID of the NET4COM class it is wrapping. How Visual Studio uses this property is something I will explore in more depth in a moment. Additionally you’ll need to add three methods to the class: ButtonsSwapped, WheelExists, and WheelScrollLines. These methods wrap the calls to the NET4COM Mouse class methods of the same name. Note how the ButtonSwapped method delegates the call to the instance of the OLE class Mouse with the following code:
RETURN
this.oNET4COM.ButtonsSwapped()
So where did the oNET4COM object come from? Well, to understand this I’ll need to look at the _net4comfactory’s oNET4COM_Access method. You invoke the oNET4COM_Access method whenever you refer to the oNET4COM object in code (such as in the return statement shown above), and the method will first check to see if the oNET4COM property of the class has been set by checking the type of the property. If the property is of type Object, the access method will just return the object as seen in the following snippet from the oNET4COM_Access method:
IF VARTYPE(this.oNET4COM)=="O"
RETURN this.oNET4COM
ENDIF
However, if the oNET4COM property is not an object, it will create the object with a call to CREATEOBJECT using the value of the cClass property. Remember that I’ve already set the cClass property for this example to “NET4COM.Mouse”. Once the oNET4COM_Access method has created the object and stored it in the oNET4COM property the method will return the property as before. See the following code from the oNET4COM_Access method to see where this is happening:
this.oNET4COM=CREATEOBJECT(this.
cClass)
RETURN this.oNET4COM
In using this class-factory base class and simple design, you can quickly create new class wrappers in Visual FoxPro. Obviously, you can access the .NET COM visible classes in Visual FoxPro directly, which would seem to negate the need for a wrapper class. (To access a visible class directly, simply issue a call to CREATEOBJECT to create an instance of the .NET class, and then use the instance variable to invoke the methods or set the properties of the object.) However, it is sometimes useful to have a wrapper class to further protect the component, make it easier to extend the component, or hide the complexity of the component’s internal workings. By providing a central class factory, NET4COM makes maintenance and enhancements really easy.
Overall NET4COM provides an excellent example of using Visual Basic to create COM-visible classes that are then wrapped by Visual FoxPro classes for use in Visual FoxPro applications.
Interop Forms Toolkit
Microsoft designed the Interop Forms Toolkit to allow developers to leverage their existing COM client applications while providing new functionality through .NET forms. The Interop Forms Toolkit also provides a way for developers to incrementally migrate their applications if they so choose. So let’s take a look at creating a .NET form and using it in Visual FoxPro courtesy of the Interop Forms Toolkit.
To follow the examples provided below, you will need to download and install the Interop Forms Toolkit from the following location:
https://www.microsoft.com/downloads/details.aspx?FamilyId=98C38C1D-C630-4D9A-8BB5-7F1FC088A7C4&displaylang=en
Creating an InteropForm Library in Visual Studio 2005
Once you have installed the Interop Forms Toolkit, open Visual Studio 2005 and create a New Project. In the New Project dialog box, select the VB6 InteropForm Library in the My Template area and give the project a name (see Figure 9). This action will create a new project containing, among other things, a form named InteropForm1. You can modify InteropForm1 in whatever way you want, such as adding additional controls and functionality.
For this article, I modified InteropForm1 to include a Toolstrip Container, a Binding Navigator, a datasource (hooked to the Customers table from the Northwind.dbc included in Visual FoxPro 9.0), and a bunch of labels and bound text boxes to show the customer field values on the form. Figure 10 shows the result.
After you have set up the form sufficiently, you’ll have Visual Studio produce the wrapper classes for the form by selecting the “Generate InteropForms Wrapper Classes” from the Tools menu (Figure 11). Visual Studio will generate a new InteropForm1.wrapper.vb file and add it to the project. Visual Studio generated the code that a developer would have had to write by hand to make this form callable by a COM client.
Finally you will need to rebuild the project. By rebuilding the project, the system will build and register the assembly so that you can call the form from Visual FoxPro.
Using the InteropForm from Visual FoxPro
Once you have built and registered the .NET assembly that contains the form class, you can call it from Visual FoxPro using the familiar CREATEOBJECT command. Though the InteropForm is a UI component, it is still just a .NET class that is accessible in a COM client via the CCW. To call the InteropForm from Visual FoxPro, use code similar to the following:
oForm =
CREATEOBJECT("InteropFormForVFP.
Interop.InteropForm1")
oForm.Show()
oForm.Caption = ".NET Interop
Form"
The .NET form would then display from the Visual FoxPro application with a caption of “.NET Interop Form” and a user could navigate the customer records via the Binding Navigator control at the top of the form (Figure 12). Bear in mind that when using the InteropForms Toolkit, the wrapper classes it generates only help to expose the form itself to COM. This means that you cannot perform drill-downs into the form from Visual FoxPro; access to the contained controls on the form is simply not provided.
You can deal with this limitation in three ways and you can use them in conjunction with one another:
This discussion regarding contained controls brings up the next section of this article, which deals with how to create .NET controls for use directly on Visual FoxPro forms. The technique basically requires that you compile and register a .NET control as an ActiveX control. Microsoft supported this basic idea in an early beta of .NET but pulled because of problems when they released .NET Beta 2. The subject, and presumably some of the support for it, has re-arisen in the form of a very early prototype of something known as the Interop UserControl.
Interop UserControl Prototype
The Interop UserControl will allow you to add .NET control elements to a UserControl class and then build an ActiveX control from it. Visual FoxPro developers who are not familiar with the .NET UserControl can think of it as akin to the Visual FoxPro container class, and like a container class, it can contain other controls.
In order to follow the example presented in this section you will need to download and install the Interop UserControl Prototype (InteropUserControl.zip) from the following URL:
http://blogs.msdn.com/vbteam/archive/2006/11/02/interop-roadmap-usercontrols-mdi-and-data.aspx
Then extract the zip into a subdirectory of the Templates for Visual Studio 2005. To give you some clue as to the location that it will need to be extracted into, here is the location where I extracted it on my machine:
C:\Documents and Settings\Craig Boyd\My Documents\Visual Studio 2005\Templates\ProjectTemplates\Visual Basic\Windows
After you have completed the preceding steps, fire up Visual Studio 2005 and create a new project. In the New Project dialog box, select the InteropUserControl in the My Template area and give the project a name (Figure 13). This will create a new project containing an InteropUserControl class, which you can modify by placing additional .NET controls into it and adding functionality through code.
For this example just add a ToolStrip class to the InteropUserControl provided. Then right-click the Toolstrip you added and select Insert Standard Items from the context menu. This action will result in all of the standard toolstrip items being added, such as buttons for New, Open, Save, Print, etc. With the standard items added to the toolstrip, resize the InteropUserControl to the size of the contained toolstrip. When finished, the InteropUserControl should look like it does in Figure 14.
That’s essentially all there is to using the Interop UserControl Prototype to create a .NET ActiveX control, in this case a toolstrip. You can now build the project and Visual Studio will automatically register it as an ActiveX control on the system. Visual Studio will register the control with a PROGID that is AssemblyName.ClassName. So, in the case of the example used in this article, Visual Studio will simply register the control as InteropUserControlForVFP.InteropUserControlForVFP.
Under the Hood
There is a lot going on under the hood of this thing, but the magic truly happens by way of the code that the ActiveXControlHelpers.vb file contains, which you will find in the Solution Explorer. The two class methods responsible for actually taking this .NET class from a simple COM-visible class to the level of an ActiveX control are the ComRegistration.RegisterControl and ComRegistration.UnregisterControl.
The InteropUserControl class uses these controls to affect the way in which Visual Studio/Regasm.exe registers the assembly (Listing 5). By marking the calls to these functions with the <ComRegisterFunction> and <ComUnregisterFunction> attributes respectively, the InteropUserControl ensures that Visual Studio will call the controls whenever the assembly is registered or unregistered. Among the additional things that these functions do when the assembly is registered is to create a Control key beneath the CLSID key for the class. The Control key basically tells Windows that this is not just a COM class, but a COM control (ActiveX) that you can place on a Visual FoxPro form for instance.
Using the InteropUserControl in Visual FoxPro
You add the newly created toolstrip to a Visual FoxPro form just as you’d add any other ActiveX control.
Well, to be truthful it sort of works.
This control has the same problem I encountered with the Interop Form presented earlier in this article, I cannot access the contained controls from within Visual FoxPro. While I can access the toolstrip, I cannot access or change any of the button icons or captions at run time, and worse yet, there’s really no way to know when a user has clicked a toolstrip button. Unfortunately Microsoft hasn’t managed to have their Interop tools write all of the code, so you need to add additional code and logic to the .NET class in order to provide for the elements that the Interop tools aren’t creating automatically.
The good news is that there are ways to do this and the information is out there for developers who want to explore this topic further.
The Wrap-up on Wrappers
I hope this article has piqued your interest by highlighting the power of COM Interop and its ability to extend Visual FoxPro with .NET components. NET4COM and the Interoperability tools coming from Microsoft provide good starting points if you are interested in learning how to integrate .NET classes into your Visual FoxPro applications. While either platform is capable of a great many things on its own, they are nearly unbeatable when used in conjunction. I have provided the examples shown in this article in the source code and I encourage you to play around with them. Until next time, Visual FoxPro Rocks!
Listing 1: The class definition provided by Visual Basic when a new class file is created
Public Class Class1
End Class
Listing 2: NET4COM’s Mouse class shows how to properly create a simple COM-visible class. The GUIDs are hardcoded to ensure that Visual Studio does not change the CLSID, Interface IDs, and Event IDs every time the project is rebuilt.
Imports System.Runtime.InteropServices
<ComVisible(True)> _
<Guid(Mouse.InterfaceId)> _
Public Interface IMouse
Function ButtonsSwapped() As Boolean
Function WheelExists() As Boolean
Function WheelScrollLines() As Integer
End Interface
<ComVisible(True)> _
<Guid(Mouse.ClassId)> _
<ComDefaultInterface(GetType(IMouse))> _
Public Class Mouse
Implements IMouse
#Region "COM GUIDs"
' These GUIDs provide the COM identity
' for this class and its COM interfaces.
' If you change them, existing clients
' will no longer be able to access the
' class.
Public Const ClassId As String =
"9ad99cd3-e715-4388-be12-c36fb68b7e23"
Public Const InterfaceId As String =
"cfa8fe7b-1a63-4118-887e-8f6625def8b4"
Public Const EventsId As String =
"94d20fa6-bd8a-4f78-a7f8-734166be1f37"
#End Region
' A creatable COM class must have a
' Public Sub New() with no parameters,
' otherwise, the class will not be
' registered in the COM registry and
' cannot be created via CreateObject.
Public Sub New()
MyBase.New()
End Sub
Protected Function ButtonsSwapped() As
Boolean Implements IMouse.ButtonsSwapped
Return My.Computer.Mouse.ButtonsSwapped()
End Function
Protected Function WheelExists() As
Boolean Implements IMouse.WheelExists
Return My.Computer.Mouse.WheelExists()
End Function
Protected Function WheelScrollLines() As
Integer Implements IMouse.WheelScrollLines
Return My.Computer.Mouse.WheelScrollLines()
End Function
End Class
Listing 3: The class definition for _net4comfactory. You should subclass this class whenever you create a new NET4COM wrapper class in Visual FoxPro.
DEFINE CLASS _net4comfactory AS custom
onet4com = .NULL.
cclass = ""
Name = "_net4comfactory"
PROCEDURE onet4com_access
IF VARTYPE(this.oNET4COM)=="O"
RETURN this.oNET4COM
ENDIF
this.oNET4COM=CREATEOBJECT(this.cClass)
RETURN this.oNET4COM
ENDPROC
ENDDEFINE
Listing 4: The _net4com_mouse class definition. Note the cclass property, which is used by the _net4comfactory base class to generate the correct object type.
DEFINE CLASS _net4com_mouse AS _net4comfactory
cclass = "NET4COM.Mouse"
Name = "_net4com_mouse"
PROCEDURE buttonsswapped
RETURN
this.oNET4COM.ButtonsSwapped()
ENDPROC
PROCEDURE wheelexists
RETURN this.oNET4COM.WheelExists()
ENDPROC
PROCEDURE wheelscrolllines
RETURN
this.oNET4COM.WheelScrollLines()
ENDPROC
ENDDEFINE
Listing 5: The trick to creating an ActiveX control in .NET as opposed to a simple COM-visible class is to create additional registry entries when you register the assembly. Use the ComRegisterFunction attribute to cause an additional method to be called when the assembly is registered.
'These routines perform additional COM
'registration needed by ActiveX controls
<EditorBrowsable(EditorBrowsableState.
Never)> _
<ComRegisterFunction()> _
Public Shared Sub Register(ByVal t As
Type)
ComRegistration.RegisterControl(t)
End Sub
<EditorBrowsable(EditorBrowsableState.
Never)> _
<ComUnregisterFunction()> _
Public Shared Sub Unregister(ByVal t As
Type)
ComRegistration.UnregisterControl(t)
End Sub