We've worked with several AVR Classic customers lately helping them resolve challenges with their legacy Classic apps. During this work, we've come to the opinion that integrating AVR for .NET with AVR Classic is not only a great way to solve AVR Classic-imposed challenges and limitations, but it's also a way to start replacing parts of your AVR Classic applications with AVR for .NET. Many AVR Classic applications are quite large and complex and rewriting them from the ground up isn't doable--at least quickly. But over time, incrementally replacing parts of them with AVR for .NET may be the key to clawing your way out of COM prison.
Be sure to understand that this article isn't about launching .NET EXEs with AVR's OSExec operation. While you can achieve a modest level of interoperability with by launching an EXE with OSExec, you're still dealing with two distinct processes that don't really interoperate with each other. With this article's techniques, you'll have a single (started by AVR Classic), that uses the .NET binary just as it would an ActiveX control. You'll be able to call .NET methods, get results back from .NET functions, and read and write .NET properties.
This article provides the keys for integrating AVR for .NET with AVR Classic. While the solution provided here is an overly-simple, academic example, with just a little more effort you can create powerful DLLs with AVR for .NET to provide services such as these (and many more) for AVR Classic:
- Integrate Active Directory with the app
- Provide SQL Server access
- Send email
- Make HTTP requests
- Generate or consume Json data
- Add new Windows forms that use any of .NET's Windows controls
.NET provides native facilities to all of those things and many others.
To be able to follow the instructions in this article, you'll need both AVR Classic and AVR for .NET installed on your developer PC. If you don't yet have AVR for .NET, you can download a trial copy here.
Visual Studio setup
To consume a .NET binary (either a DLL or an EXE ) you need to register that binary with .NET's Regasm utility. Regasm is a command line utility and is installed along with Visual Studio .NET--but it's a little hard to find. Make it easy on yourself and use the instructions in this ASNA TechHub article to make Regasm easily available. Don't continue until you can launch the .NET Dev Prompt from Visual Studio and see that RegAsm works. To test it, just type
regasm with command prompt displayed and press enter. You should see its help listing.
An AVR for .NET Windows app with two forms
This article shows a very simple interoperability example. It features a .NET program that has two Windows forms.
Form2. This is a free-standing program, an EXE, and the intent is for it to either be able to run by itself or consumed directly AVR Classic. You could also create a DLL and consume it directly from AVR Classic. If there is enough demand, we'll cover that in more detail later (but it's pretty much the exact process explained here).
Don't let the simpleness of this example's forms be offputting. They were kept simple to not let their complexity obfuscate the concept of COM/.NET interoperability. These forms could very easily be performing very complex operations. They could have database connections, be passed arguments, and generally do anything that you can do it .NET.
When it's running on its own, the AVR for .NET program presents itself as shown below in Figure 1a.
Figure 1a. The .NET app with two Windows Forms.
This AVR for .NET program doesn't need much code to display these two forms. The two click event handlers are shown below in Figure 1b:
BegSr buttonShow`Form2`_Click Access(*Private) Event(*this.buttonShow`Form2`.Click) DclSrParm sender Type(*Object) DclSrParm e Type(System.EventArgs) DclFld f2 Type(`Form2`) New() f2.ShowDialog() EndSr
BegSr buttonClose_Click Access(*Private) Event(*this.buttonClose.Click) DclSrParm sender Type(*Object) DclSrParm e Type(System.EventArgs) *This.Close() EndSr
Figure 1b. The click event handlers in the AVR for .NET example shown in Figure 1a.
As written above, the AVR for .NET example runs on its own. When you need to share .NET forms with COM, this is a good approach. You can easily use all of .NET's debugging capabilities to ensure the forms are working exactly as expected and then surface what's necessary from the project to COM.
While the code above makes the app run, the app isn't quite ready to share with COM. We need just a little more code. To surface a method to COM to display
Form1, the .NET project needs the
ComBridge class, shown below in Figure 1c.
Using System.Runtime.InteropServices BegClass ComBridge Access(*Public) + Attributes(ComVisible(*True), + ClassInterface(ClassInterfaceType.AutoDual)) BegSr `Form1`_Show Access(*Public) Attributes(ComVisible(*True)) DclFld f Type(`Form1`) New() f.ShowDialog() EndSr EndClass
Figure 1c. The
ComBridge class that surfaces methods (and optionally, properties) from the .NET project to COM.
ComBridge class does a couple of things:
It marks the
ComBridgeclass itself with the ComVisible and ClassInterface attributes necessary to surface the class to COM.
It marks the
Form1_Show method with the ComVisible attribute to make the method visible to COM.
If you aren't familiar with .NET attributes, don't worry much about that now. Just know for now that these attributes provide the COM-compatible the .NET binary needs to be used by COM. These attributes reside in .NET's
System.Runtime.InteropServices namespace (and the
Using statement at the top of the
ComBridge class makes that namespace available).
ComBridge defines what components of the .NET project are made available to COM (there isn't anything magic about the
ComBridge name; the class could have any valid class name). As you'll soon see, this
ComBridge class is visible to COM, but the form classes themselves (classes
Form2 in this project) are not directly available to COM. The intent with the
ComBridge class is to harness those elements of the .NET project necessary for COM to use those elements.
You could also apply the ComVisible and ClassInterface attributes directly in the
Form2 classes, but the
ComBridge class helps provide a clear separation of concerns--clearly identifying what parts of the project are visible to COM. While this example's
ComBridge classes surfaces only one method, others may surface several methods and/or properties (which in turn reveal other parts of the .NET program).
After you compile the .NET project, you need to register it for COM use with RegAsm as discussed in the previous section and shown below in Figure .
Figure 1d. Registering the .NET EXE for COM use with REGASM.
The command syntax for RegAsm:
regasm /tlb /codebase <EXE or DLL name>
/codebase are very important. As mentioned in this article, Visual Studio needs to be run as an administrator to launch RegAsm.
In .NET, both EXEs and DLLs are simply assemblies--which is essentially one or more classes compiled as a .NET binary. You can use this technique with both .NET free-standing EXEs and DLLs.
With the EXE (or DLL) compiled and registered, you're ready to use it from AVR Classic.
An AVR Classic app to consume the .NET EXE
To use the RegAsm-registered .NET binary from AVR Classic, set a reference to the binary in a new (or an existing) AVR Classic project. In this case, the binary's name is DotNetWinForms. It is highlighted in AVR Classic's Reference shown below in Figure 2a:
Figure 2a. Setting a reference to the .NET EXE in AVR Classic.
After setting the reference, take a look at the object with AVR Classic's Object Browser. It shows what public members are available and it shows how to declare the .NET object. The DotNetWinForms object is shown in the Object Browser below in Figure 2b:
Figure 2b. Using AVR Classic's Object Browser to view the .NET object.
You can see the
Form1_Show() method declared in Figure 1c, but you can also see other methods Figure 1c doesn't declare. These are intrinsic methods that .NET provides and can generally be ignored when consuming the .NET object from COM.
The bottom portion of the Object Browser shows that the .NET object should be declared as:
DclFld x Type(DotNetWinForms.`ComBridge`)
You can name the object any valid field name. In this example its name is
dnwf. The meager code in the AVR Classic example is shown below in Figure 2c. It is only one button click event handler and it calls the object's
DclFld dnwf DotNetWinForms.`ComBridge` BEGSR buttonShowDotNETform Click DclFld dnwf Type(DotNetWinForms.`ComBridge`) dnwf.`Form1`_Show() ENDSR
Figure 2c. The AVR Classic code calling the .NET object's
When the AVR Classic runs (as shown below in Figure 2d), and when its button is clicked, the AVR for .NET
Form1 is displayed. ComBridge effectively provides a gateway into the .NET app. Even though nothing about
Form2 is surfaced to COM, because its use is encapsulated in the .NET app, it is displayed as it should be. In this case,
Form1_Show isn't a function (and therefore doesn't return a value), but if it did that value would be directly available to AVR Classic. If the
ComBridge had surfaced any properties from the .NET app those would also be directly available to AVR Classic.
Figure 2d. What you see when the AVR Classic code in Figure 2c runs.
There is a small downside to this interoperability: Any .NET component that needs database access must establish its own connection to the IBM i. It's not possible for AVR Classic and AVR for .NET to share an IBM i connection; thus you'll most likely have two IBM i connections per AVR Classic app. This isn't a best-case situation, but in the real world, it presents only a minor issue--especially for the value it delivers.
Remember too, that AVR Classic and AVR for .NET both work directly with modern versions of DataGate so you don't need two DataGate installs (eg, AVR Classic 5.0 and AVR for .NET 15 both work with DataGate 15).
Get to it!
The example proves that with just a little elbow grease, you can build great components, including full Windows forms, for AVR Classic with AVR for .NET. This effectively harnesses the full power of .NET for your AVR Classic apps. It also provides the opportunity for AVR Classic shops:
- Start learning .NET with a concrete purpose
- Start chipping away at obsolete third-party dependencies and replacing them AVR for .NET counterparts
- Start replacing their AVR Classic one chunk at a time
If you are an AVR Classic customer, the technique presented here doesn't just provide a way to extend the power of AVR Classic, it provides a way out of AVR Classic and a way to wean your business off COM dependencies. The COM world won't last forever. We don't know Microsoft's long-term plans for COM, and without trying to be too melodramatic, we're potentially one Windows update away from lots of broken AVR Classic code. We're nearly certain that day is very far away, but take to the bank that day is coming. Ask any third-party ActiveX vendor, all of whom have already experienced that day, what it's like.
With a little planning and care, you could start today moving AVR Classic maintenance and enhancement work to .NET and start easing away from the potential business disruption that is COM (the COM model is 25 years old!). With care, you could craft a .NET class library/DLL structure that offers both UI facilities and business services. The business services could be used by both AVR Classic and AVR for .NET Windows forms today, and later provide the basis for longer-term RESTful services for web or mobile development.