What is wwDotnetBridge?
wwDotNetBridge provides an easy way to open up most of .NET's functionality to Visual FoxPro applications. While it relies on COM Interop to handle the interactions between FoxPro and .NET it uses its own runtime hosting and .NET assembly to manage the .NET runtime in FoxPro.
This provides a number of functionalty improvements over using COM Interop (ie. CreateObject("myApp.MyDotNetObject")) by greatly extending the range of what .NET features you can access with Visual FoxPro. Native COM Interop is severely limited by components that are exposed to COM explicitly in .NET [ComVisible(true)] and to components that can be instantiated from Visual FoxPro via CreateObject() (explicitly exported to COM). wwDotnetBridge - because it operates inside of .NET - can circumvent most of these limitations and provide access to a much wider range of features in .NET. For example, using wwDotNetBridge you can access static classes and their members, enum types, value types, generic types, Collections, interface based values as well as providing easier access and manipulation of arrays and collections from within FoxPro.
Another big feature is that .NET components do not need to be registered for COM - wwDotnetBridge can load components directly from within .NET so no COM registration is required for components loaded. This means if you create your own .NET components for use in your applications, these components don't need to be registered. It also means that you can access just about any .NET type directly from FoxPro, whether the type is registered for COM, marked as [ComVisible] or not. This opens up most of the .NET runtime to you for use in your own applications.
How it works
wwDotnetBridge consists of three pieces:
wwDotnetBridge.prg
A FoxPro class that your application uses to access extended .NET features.wwIPStuff.dll
Contains a small shim that loads the .NET Runtime into Visual FoxPro. Supports .NET 2.0 or 4.0 runtime loading explicitly. This feature is optional - you can also choose to load wwDotnetBridge using standard COM Interop Registry loading by using o = CREATE("wwDotNetBridge","V2",.T) which bypasses the custom loaded .NET runtime and use of wwIPstuff.dll. Note when using the latter approach wwDotnetBridge must be .NET COM registered with the RegAsm tool.wwDotnetBridge.dll
A .NET component that acts as a proxy inside of .NET to perform extended tasks on the behalf of your FoxPro code. The FoxPro wwDotnetBridge class interacts with this .NET component to provide interaction with .NET.
The FoxPro class is your interface to wwDotnetBridge and that's all you interact with from FoxPro code. You create an instance and use its methods to load .NET assemblies, create instances and frequently invoke methods, set/get property values indirectly.
wwIPStuff.dll is a small Win32 DLL that includes a helper function that bootstraps the .NET runtime into Visual FoxPro the first time wwDotnetBridge is loaded in a process. The runtime is loaded exactly once and you can specify (as part of the CreateObject() call) which version of the runtime you want to load. Once the runtime's been loaded it cannot be unloaded, nor can another version be loaded. Once loaded it's just there.
The wwDotnetBridge.dll then is used to interact with the loaded .NET runtime. It is used to load assemblies, create instances of .NET types and provides indirect execution access to .NET classes and members that execute within the .NET Runtime rather than through Visual FoxPro. As such wwDotnetBridge.dll provides a .NET proxy for your FoxPro code to execute operations inside of the context of .NET on behalf of your FoxPro code and returns and passes only values that FoxPro can understand and work with (ie. mostly simple types and basic objects). This provides access to a host of features not natively accessible via COM interop including access to static objects/members, value types, generic types, collections and overloaded methods to name a few.
A short Example
To see how wwDotnetBridge works it's easiest to show a simple example. This example uses the non-COM accessible JavaScriptSerializer that provides JSON serialization to ASP.NET applications.
*** Load libraries
do wwDotNetBridge
* **Create instance and use .NET 4.0
LOCAL loBridge as wwDotNetBridge
loBridge = CreateObject("wwDotNetBridge","V4")
* **Load known assembly
? loBridge.LoadAssembly("System.Web")
* **Load a GAC assembly (fully qualified .NET type name required)
? loBridge.LoadAssembly("System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
* **Load a private assembly from disk
* ? loBridge.LoadAssembly("c:\apps\myapp\myappassembly.dll")
* **Create an instance of the JavaScript Serializer .NET object - full class name required
loSer = loBridge.CreateInstance("System.Web.Script.Serialization.JavaScriptSerializer")
lcValue = "Hello World\r\nHere's what I've got!"
* **Serialize string to JSON
lcJson = loSer.serialize(lcValue)
? lcJson
* **Create a .NET Type object for string
loType = loBridge.GetType("x")
* **Deserialize with the .NET type provided
? loSer.Deserialize(lcJson,loType)
First you create an instance of wwDotnetBridge, followed by loading up any assemblies you might need using LoadAssembly. Next you call CreateInstance() to create an instance of a class. At this point you get back an object which you can use to call methods on using standard COM Interop. Here I'm simply accessing the Serialize and Deserialize methods directly because it just works.
The key is that JavaScriptSerializer is not directly accessible by COM Interop because System.Web.Extensions it's not registered for COM Interop. Using wwDotnetBridge you can access this component easily though (although it's not terribly useful).
Direct and Indirect Object access
In the example above I mentioned I used direct object access on the serializer because the methods I'm using happen to work with straight calls. But often times there are values passed or returned that can't be directly accessed. The following example uses a few more features of wwDotNetBridge like accessing an enumerated value, indirect property access and retrieving collection enty.
This example retrieves a list of private certificates from the Windows Certificate store a task that's notoriously difficult with Win32 code:
do wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = CreateObject("wwDotNetBridge","V4")
* **Create an instance of 509Store
loStore = loBridge.CreateInstance("System.Security.Cryptography.X509Certificates.X509Store")
* **Grab a static Enum value
leReadOnly = loBridge.GetEnumvalue("System.Security.Cryptography.X509Certificates.OpenFlags","ReadOnly")
* **Open the certificate store - results into loStore.Certificates
loStore.Open(leReadOnly)
* **Collection of Certificates
laCertificates = loStore.Certificates
* **Collections don't work over regular COM Interop
* **so use indirect access
lnCount = loBridge.GetProperty(laCertificates,"Count")
* **Loop through Certificates
FOR lnX = 1 TO lnCount -1
* **Access collection item indirectly
LOCAL loCertificate as System.Security.Cryptography.X509Certificates.X509Certificate2
loCertificate = loBridge.GetPropertyEx(loStore,"Certificates[" + TRANSFORM(lnX) + "]")
IF !ISNULL(loCertificate)
? loCertificate.FriendlyName
? loCertificate.SerialNumber
? loBridge.GetPropertyEx(loCertificate,"IssuerName.Name")
ENDIF
ENDFOR
Notice the GetEnumValue() function which retrieves an enum value. There are also calls to GetProperty() to retrieve the collection count (inaccessible through straight COM because it has a complex accessor function), and GetPropertyEx() to retrieve a collection item and a value on a child object. All of these would fail with straight COM interop calls.
There's much more to wwDotnetBridge but this should give you a basic idea of the functionality that this tool provides.
How indirect Invokers Work
Internally many of wwDotnetBridges methods use .NET Reflection to perform their operations. They dynamically invoke complex objects and reference them by string names that you pass in. For example, GetProperty(loObject,"PropName") takes an instance type (the parent type) and a property name. So loItem, and "Sku" for example to return the loItem.Sku value.
While Reflection is slower than direct operation, the advantage of it is that it allows your FoxPro code to tell wwDotnetBridge to perform tasks on its behalf - from inside of the .NET runtime. What this means is that even though you might set a value on a type not supported by FoxPro via COM interop because it can't be marshalled, wwDotnetBridge can assign or read the value because it's running inside of .NET and the value is never marshalled to FoxPro.
This opens a whole set of functionality that otherwise would not be available to COM interop.
In addition wwDotnetBridge provides a host of helper methods and classes, as well as type converters that simplify access to some complex and incompatible.NET objects in FoxPro.
For more info check out the wwDotnetBridge Feature List.
General Use Cases for wwDotnetBridge
Accessing .NET components with wwDotnetBridge from FoxPro can be broken down into two general groups:
- Accessing components directly (either in the framework itself or third party)
- Creating your own .NET components and calling them from FoxPro
Accessing standard .NET functionality directly with wwDotnetBridge
The .NET framework includes an enormous amount of functionality from system services to data service to Web functionality, to parsing of data and so much more. Microsoft also keeps publishing new APIs built in .NET constantly so there's even more functionality added on a regular basis. .NET has become Microsoft's main platform for pushing new technology APIs as well as old ones and using .NET tends to be much easier than the equivalent functionality using native Windows Win32 or COM APIs in most cases.
The examples I've shown above demonstrate this scenario: I'm accessing native .NET system components directly from my FoxPro code. For many tasks this works perfectly fine and is a great way to experiement with .NET functionality from your FoxPro code.
Building .NET Proxy classes and accessing those with wwDotnetBridge
While wwDotnetBridge will let you access just about any .NET functionality directly from within FoxPro code, the code you have to write often can be pretty tedious and much more verbose than what you would write inside of .NET. The reason is that a lot of code needs to be expressed using indirect referencing and all type references need full qualification - and there's no Intellisense to make it all easier as there is in .NET and Visual Studio.
Another option is to create key components inside of .NET, using C# or VB.NET code and compiling it into .NET assemblies. You can then call those components from Visual FoxPro. I've done this extensively with various West Wind components in our tools. For example, the wwSmtp class is implemented as a .NET component with a front FoxPro front end interface that calls the .NET component using wwDotnetBridge. The West Wind Web Service Proxy Generator tool creates .NET assemblies from WSDL Web Services and automatically generates FoxPro proxy classes that interact with the generated .NET classes using wwDotnetBridge. West Wind Help Builder uses a wwReflection library I created to import .NET types for documentation using wwDotnetBridge to interface with the .NET code.
If you need to call complex Web Services (WS* for example) using Windows Communication Foundation, it's much easier to create that code in .NET with the help of Intellisense and all the support tools available there and then expose only a limited API from a wrapper class that is callable from FoxPro. Not only is it often easier to create the code in .NET, but it also allows you to create a wrapper type that provides the simple types that are easy to consume in FoxPro (ie. basic hierarchical objects, simple arrays rather than complex collections etc.).
While this isn't for everyone since you DO have to write .NET code to make this happen, it's a good way to get started in .NET and to make it easier to consume .NET code in FoxPro.
See also
wwDotnetBridge Features© West Wind Technologies, 2004-2020 • Updated: 04/05/18
Comment or report problem with topic