devxlogo

COM Interop: Making .NET and VFP Talk to Each Other

COM Interop: Making .NET and VFP Talk to Each Other

any developers wrote VFP applications using COM components, usually for data access logic and business logic. As a VFP developer you’ll be relieved to know that you can reuse those components in .NET, allowing you to easily create a .NET User Interface (a Web application, for instance) that uses those VFP components, instead of throwing them away and rewriting everything from scratch. On the other hand, the .NET Framework comes with many classes that VFP developer might want to use in their VFP applications, and that is also possible.

Whether you use a COM component from .NET, or a .NET component from a COM-enabled environment (such as VFP), COM Interop is the mechanism that allows for such interoperability.

Why COM Interop?
COM-enabled languages can use COM components created in any language because those components conform to the standards defined by COM. Most languages have different types, or treat common types in a different way, and therefore, in order to make components created in different languages talk to each other, they have to be compatible, and it is COM that determines the common rules.

.NET goes a step further in trying to address issues with the COM standards (such as DLL hell), and it uses different approaches that lead to a very different set of standards. COM components and .NET components are not compatible by default; however, keeping in mind that many companies have put a lot of work into COM components, Microsoft added a mechanism to .NET so that .NET components can see COM components as if they were .NET components, and COM components can see .NET components as if they were COM components.

Fast Facts
.NET is growing more and more important, making interoperability between .NET and VFP a hot topic. There are a few different approaches to combining the two tools, such as COM Interop, Web services, and interoperability on the database level.

Calling VFP Components from .NET
For .NET to see COM components, you must create a proxy (or wrapper). This proxy, called Runtime Called Wrapper (or RCW), is an object that sits between COM and .NET, and translates calls from .NET to COM. To the .NET client, the proxy looks like any other .NET component, and the proxy takes care of interpreting the calls to COM. Creating the RCW is not a daunting task, as you will see in a minute.

You first create a COM component in VFP. The following code creates a sort of business object class that.NET will use. (We say sort of business object because the data access code is there too, but separating layers is not the point we’re trying to make here):

   Define Class CustomerBizObj As Session OlePublic      DataSession = 2  && Private Session      Procedure Init         Use Home(2) + "NorthwindCustomers.dbf"      EndProc       Procedure GetCustomerList() As String         Local lcOut As String         lcOut = ""         Cursortoxml("Customers","lcOut",1,0,0,"1")         Return lcOut      EndProc    EndDefine

The GetCustomerList method retrieves a list of customers, returning the results in an XML string. Note that you must declare the return type, otherwise VFP will define the return type to be of type variant in the type library (a file that defines the methods and other things that are in a COM component). You need to avoid variants because .NET doesn’t support a variant type. On the .NET side, the developer must know in advance what data type is actually getting returned to be able to use it.

You declare the class using the OlePublic keyword, marking it to be exposed as a COM component. For this demo we created a project called “COMInteropSample,” and added the file CustomerBizObj.prg to the project. We need to build the project as a multi-threaded COM server (.dll).

You can use the following code to test the COM component in VFP:

   *-- Instantiate the object.   oCustomerBizObj=;      CreateObject("COMInteropSample.CustomerBizObj")      *-- Call the method, saving the XML to a file.   StrToFile(oCustomerBizObj.GetCustomerList(),;      "c:CustomerList.xml")      *-- Release the object.   Release oCustomerBizObj      *-- Show XML.   Modify File c:CustomerList.xml

Next you can create any sort of .NET application. For this example we’ve created an ASP.NET Web Application, and we chose to use C#, but the language really doesn’t matter. After we created the project we added a reference to the COM component in the .NET project. You can do this by going to the Add Reference option on the Project menu, or by right-clicking the References node on the project through the Solution Explorer window (Figure 1 shows that). From the dialog box, click the Browse button, and navigate to the COMInteropSample.dll that was created when the VFP project was compiled.

?
Figure 1: Adding a reference to a .NET project.

Next we created a CustomerList.aspx Web Form, and added a DataGrid control (named dgCustomers) to it.

The CustomerBizObj class created in VFP will be contained within a namespace called cominteropsample, so we added the line using cominteropsample; at the top of the code-behind file for the Web Form. Inside that namespace you’ll find the class named CustomerBizObjClass. This Web Form displays the list of Customer objects returned by the GetCustomerList method on the business object. The following code snippet shows the Page_Load method on the Web Form, which runs every time the page loads:

   private void Page_Load(object sender,      System.EventArgs e) {      CustomerBizObjClass customer =          new CustomerBizObjClass();      DataSet dsCustomers = new DataSet();      dsCustomers.ReadXml(         new StringReader(         customer.GetCustomerList()));      this.dgCustomerList.DataSource = dsCustomers;      this.dgCustomerList.DataBind();   }

As you can see, the code just instantiates the CustomerBizObjClass and a DataSet, which it fills with data based on the XML returned from GetCustomerList. The DataSet’s ReadXml() method takes care of the transformation from XML to ADO.NET data. Finally, the code binds the DataSet to the DataGrid. Other than the specifics of using DataSets and StringReaders, using the VFP component is just a matter of instantiating objects and calling methods, as the VFP developer is very used doing in VFP. Figure 2 shows the results of running that page.

?
Figure 2: A .NET Web application that uses VFP components and data.

Remember what seemed to be a daunting task of creating the RCW (that proxy that intermediates .NET calls to COM components)? That’s been created automatically by the Visual Studio .NET IDE as soon as a reference to the COM component was added to the .NET project. If you select the cominteropsample reference on the Solution Explorer window and look at its Path property, you should see something like the following:

   C:YourProjectobjInterop.cominteropsample.dll

The YourProject portion of the path shown above should be whatever path you used for the .NET project you’ve created. The important detail to notice here is that the path doesn’t point directly to the cominteropsample.dll (created by VFP). Instead, it points to an Interop.cominteropsample.dll. This DLL is the RCW created by .NET. This proxy will help .NET communicate with the COM component. It has a class with the same name as the one exposed by the COM component, but with the class word added to it (thus, the CustomerBizObjClass that’s instantiated in the .NET sample). In other words, whenever your application instantiates that class in .NET, the proxy will know how to instantiate the COM component, and whenever a method is called in that class, the proxy will know how to translate the .NET call into a COM call.

The Type Library Importer Tool
When you add a reference to a COM component to a .NET project using the VS.NET IDE, VS uses a tool called the Type Library Importer, accepting default values for it. Some of those defaults determine that the proxy will be named after the COM DLL, but preceded by the word “interop” (such as in Interop.cominteropsample.dll), and the proxy class will be placed inside a namespace also named after the .dll (such as cominteropsample).

Many developers want to have more control over the process of creating the RCW, particularly the namespace where the proxy is going to be placed, and where the proxy DLL is going to be created. Developers can use the Type Library Importer tool (Tlbimp.exe) for that. This command-line tool installs with the .NET SDK in the folder C:Program FilesMicrosoft Visual Studio .NET 2003SDKv1.1Bin. You can run the tool at a DOS prompt like this: (We broke the lines for better readability, but this would all be typed on one line.)

   tlbimp.exe      "C:YourVFPProjectBizObjects.dll"   /out:   "C:YourDotNetProjectinProxy.BizObjects.dll"   /namespace:BizObjects

Notice that you specify the COM DLL, and then use the switch out to specify where you want to locate the proxy and what you want to name it. Use the namespace switch to specify the name of the namespace that will contain the proxy class.

At this point you can remove the reference created previously in the .NET project for the COM component. You can add a new reference pointing to the Proxy.BizObjects.dll you just created via the command line. (The RCW is already a .NET class so VS.NET won’t try to create another proxy). You can rewrite the using statement at the top of the Web Form as using BizObjects.

Calling .NET Components from VFP
Many .NET classes are useful to VFP developers via COM interop. For instance, you might want to use the classes that provide GDI+ features. Listing 1 shows a class created in .NET that wraps up some GDI+ functionality. We compiled the class into a class library project in .NET. The most important thing to note here is that the project has been marked as “Register for COM Interop.” To do that, right-click on the project, select Properties, select Configuration Properties?Build, and then set the “Register for COM Interop” option to True to expose a .NET class as a COM component (Figure 3).

?
Figure 3: A .NET project set up for COM Interop.

After you compile the project you can immediately use the class through COM from VFP. However, in order to provide a better experience for the user of such class, you might want to apply some attributes to the class. For instance, the class shown in Listing 1 has the following attributes applied:

   [ClassInterface(ClassInterfaceType.AutoDual)]   [ProgId("VFPAndDotNet.ImageHelper")]

The ClassInterface attribute, set to ClassInterfaceType.AutoDual, enables the IntelliSense support in VFP. The ProgId attribute specifies the ProgId that VFP will use when instantiating the .NET component as a COM component. For example, you can use the ImageHelper class in VFP like so:

   *-- Instantiate the .NET class as a COM component.   oHelper = CreateObject("VFPAndDotNet.ImageHelper")   *-- Set some properties.   oHelper.Copyright = "Claudio Lassala"   oHelper.ImageFile = "C:MyImage.BMP"   oHelper.SaveFileAs = "C:CopyOfMyImage.jpg"   *-- Call a method on the class.   oHelper.ProcessImage()

From the VFP side there is no indication that the object being used is a .NET object.

Calling .NET Web Services from VFP
The one.NET feature that was (and still is) mentioned more than any other is the ability to use .NET to create XML Web services. Web services are methods of functions that are exposed to the Web through a standardized protocol called SOAP. SOAP enables you to access to components in a platform and language neutral fashion. This means that any language and operating system can call any Web service no matter how the Web service was created. This, of course, means that Visual FoxPro can call Web services created in Visual Studio .NET.

We’ll show you how to create a .NET Web service before we call one You can easily do this using the Visual Studio .NET IDE.

Authors’ Note: If you do not have Visual Studio .NET installed, you can probably follow the example by calling an existing Web service such as one of the many Web services found listed with www.UDDI.org).

If you’re following along you have Visual Studio .NET loaded. First create a new ASP.NET Web service project. The language you choose to use does not matter. This example will use VB .NET but if you are more familiar with C#, you should have no difficulty following the examples. Figure 4 shows the New Project dialog box.

?
Figure 4: Web service Project: The figure shows the project template to select when creating a new Web service project in Visual Studio .NET.

When you create a new ASP.NET Web service project, the Visual Studio .NET IDE automatically includes all the required references and creates a Web service source code file (Service1.asmx), with a HelloWorld method as a template. For our purposes we’ll delete that method and instead change the code to what you see in Listing 2. You may have noticed that most of the code in Listing 2 is inside a “designer region,” which means that developers should never have to touch it. The important part of Listing 2 is the following method:

    _   Public Function GetCurrentTime() _      As DateTime      Return DateTime.Now   End Function

This method simply returns the current date and time as a DateTime data type. The only unusual aspect about this is the attribute. This attribute tells the ASP.NET runtime that this method is to be exposed through a Web service according to the SOAP standard.

You can start your Web service project (simply press F5) to see a test bench interface in Internet Explorer. In this example, the service is rather simple and has only one exposed method. Click the link to that method and then click the “Invoke” button to run the service. (Note: If your method accepted parameters this interface would provide textboxes to enter those parameters.) You can see the result in Figure 5. The return value of the method is wrapped in XML, which is the key mechanism that allows you to call this service from Visual FoxPro.

?
Figure 5. Testing a Web service: Here’s the way the simple Web service looks when using the test bench application provided by Visual Studio and running in Internet Explorer.

You can register a Web service in VFP through the Task Pane Manager under its special “Web services” tab. Click the first link provided in the window, titled “Register an XML Web service.” In the Web service registration dialog box (see Figure 6) you specify a URL that describes the Web service and tells VFP what methods as well as parameters and return values are supported by the service. ASP.NET-based Web services provide a WSDL (Web service Description Language) URL that provides exactly that information. You can find the URL by launching the test bench in Visual Studio .NET (click F5), to launch the service test bench start page. At the very top of the page there is a link to the “Service Description” of the Web service. In our example, the URL is similar to the following:

   http://localhost/ExampleService/   Service1.asmx?WSDL
?
Figure 6. Adding a Web service Reference to VFP: The figure shows the process of adding a Web service reference using the Visual FoxPro 9 Task Pane.

Note that in a real-life scenario, you need to replace the host name localhost with the name of the domain the Web service resides on (such as www.codefocus.com).

After you register a Web service with the VFP Task Pane you can test it immediately through the Task Pane Manager. Simply pick the service you would like to test (“Service 1” in our example) and the method you would like to call, and click on the icon next to the method drop-down list. You can see the result in Figure 7. You now know that the Web service works in VFP and you can start using it from within a VFP program. Doing this requires a little bit of code. The good news, however, is that the Task Pane also provides sample code (the bottom left of Figure 7 shows the start of the sample code) that you can use directly by dragging and dropping that code into a source code window. Listing 3 shows code created based on the sample code provided by the Task Pane. Note that Listing 3 contains a lot of code that is not strictly required, particularly the error handling. The important code is the following:

   loWSHandler =       NEWOBJECT("WSHandler",;      IIF(VERSION(2)=0,"",;      HOME()+"FFC")+"_ws3client.vcx")   loSvc =       loWSHandler.SetupClient(;      "http://.../Svc.asmx?WSDL",;      "Service1", "Service1Soap")   MessageBox(loSvc.GetCurrentTime())
Authors’ Note: We shortened the URL to make it more readable. Please replace the URL with the URL of the service you created.

?
Figure 7. Registered Web service: Here’s the sample Web service registered in the VFP Task Pane Manager and ready to be tested.

The sample code instantiates the WSHandler object, which is VFP’s connector to a Web service, and then configures it with the WSDL URL. Subsequently, the code calls the GetCurrentTime() method, which returns a .NET DateTime variable. VFP automatically assigns the return value to a VFP DataTime variable even though the two formats differ slightly internally. Because the code returns the value as a DateTime you can perform additional operations on it. For instance, you can retrieve the time portion using the following commands:

   LOCAL ldCurrentDateTime   ldCurrentDateTime = ;      loService1.GetCurrentTime()   ? TToC(ldCurrentDateTime,2)

Note that automatic type assignment does not happen all the time. It is possible?some would say likely?that the Web service will return a data type not natively supported in Visual FoxPro. This typically happens when the return value is a complex object, such as an ADO.NET DataSet. In that case the return value would be complex XML, which you must parse before VFP can use it. In the case of a DataSet VFP has an XMLAdapter class. (Note: For more information about the XMLAdapter class, see the “What’s New with Data in Visual FoxPro 9” article in this issue, or search for “XMLAdapter” on www.code-magazine.com.) For other complex objects, parsing the XML may be a little more complex, but using tools like XMLDOM, it is never overly hard.

Exposing VFP Objects as Web Services
Visual FoxPro does not support a native way to expose VFP objects as Web services, but there are several other Microsoft tools and technologies that you can use to accomplish this goal. In previous versions of VFP, Microsoft recommended the SOAP Toolkit (and in fact provided tools to automatically publish VFP Web services using this toolkit). This approach is now not recommended anymore, mainly because the SOAP Toolkit uses either ASP or ISAPI “listeners” to react to incoming requests. Neither technology is recommended at this point, and is only supported by Microsoft based on the standard Microsoft support policy. The better way to go at this point is to expose VFP objects through modern ASP.NET Web services.

The overall idea for this approach is simple: First, create a VFP object and expose it as a COM object. You can access this COM object from ASP.NET using a simple wrapper service to expose individual methods. For instance, consider the following VFP object:

   DEFINE CLASS TestClass AS Custom OLEPublic       FUNCTION GetName() AS String           RETURN "John Smith"       ENDFUNC   ENDDEFINE

Here’s the wrapper class used to expose this object through ASP.NET as a Web service:

   Imports System.Web.Services   < WebService(Namespace := _       "http://MySvc.org/Service1")> _   Public Class TestService      Inherits WebService      #Region " Designer Generated Code "          _      Public Function GetName() _         As String         Dim oVFPObject As New _            TestProject.TestClass()         Return oVFPObject.GetName()      End Function   End Class

For more details on how to use VFP COM objects in .NET, please refer to the earlier section on COM Interop.

Visual FoxPro and OLE DB
Another way to interact with Visual FoxPro data from .NET is via the Visual FoxPro OLE DB provider. Listing 4 demonstrates querying data from the sample NorthWind.DBC file and displaying it on an ASP.NET page.

You can simply call this method from an event in an ASP.NET Web Form (such as the Load event). The code first opens an OLE DB connection to a VFP database container. Next the code executes a SELECT statement fills the results into an ADO.NET DataSet using a DataAdapter. You can then use this DataSet like any other ADO.NET DataSet. In this example, we use it as the bound data source for a DataGrid.

Further details of accessing VFP data through OLE DB is beyond the scope of this article. The core concept however is relatively simple and pretty similar to accessing SQL Server data.

COM Interop makes it easier for the developer to use VFP components in a .NET application, preventing the developer from rewriting portions of logic such as data access and business rules when time constraints and budget don’t allow for that. The same mechanism also enables the developer to use .NET classes from VFP, adding even more power to existing VFP applications.

Web services are a more open process and allow your VFP application to work with environments that do not support COM or .NET. Web services work over the Internet, hence automatically adding remote invocation as a free benefit.

Interop on the database level is also a viable option. This works both ways: .NET can access VFP data through OLE DB. VFP, on the other hand, can access many of the data sources .NET uses, such as SQL Server. (We skipped this topic since SQL Server data access with VFP has been discussed many times).

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist