Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder, Microsoft Cloud

PowerBuilder: Article

Calling .NET Components from PowerBuilder

Via COM Wrappers – revisited

I've written a number of articles in the past on using .NET components, both visual and non-visual, from a PowerBuilder "Classic" (i.e., Win32) application. Until now, all of them involved using a .NET component that was either provided in the .NET Framework or created using Visual Studio. What changes with PowerBuilder 12 is that we can now write a non-visual component using PowerBuilder.NET, so the solution is entirely PowerBuilder based.

Technically, this really first became possible with the introduction of the .NET assembly target in PowerBuilder 11. However, there were a couple of issues with using this approach at that time:

  1. PowerBuilder 11 (and 11.5) didn't have CLR compliance and so referencing .NET classes from the non-visual object used to create the assembly required the use of conditional code blocks, with their inherent limitations (mainly lack of autoscripting, script validation and somewhat poor documentation of syntax).
  2. PowerBuilder 11 (and 11.5) generated assemblies that were not marked COM visible, a requirement for making them accessible via COM Callable Wrappers (CCW) to PowerBuilder Win32 applications. It is possible to change the setting, for example, by using ILDASM to decompile the assembly, modify the decompiled code and then using ILASM to recompile it. I covered that in my presentation on using .NET assemblies from PowerBuilder at TechWave 2008, so you might look for the materials for that session if you want to research it further. However, that is a less than an ideal approach.

With PowerBuilder.NET in PowerBuilder 12, we now have a fully CLR compliant PowerScript with a full autoscripting implementation, script validation and better documented syntax. What's more, the assemblies generated by PowerBuilder.NET are marked as COM visible, so it's much easier to expose them to Win32 applications via CCW.

Creating the Assembly
We're going to implement the call to the SMTPClient class in the .NET Framework like we did in the earlier examples, except now we're going to do it directly from PowerScript in PowerBuilder.NET (see Listing 1). This code is adapted from the code that Dave Fish used for one of his sessions at TechWave 2010. There are a number of instance variables, indicated by the lowercase "i" prefix, such as iSubject, iIsHTML and iBody. Because PowerBuilder.NET does not (as of this writing) support properties in generated .NET assemblies, we've implemented setter methods on the assembly as well to set those instance variables. The code listing for those are not included because of their trivial nature.

Now that we have a non-visual object that contains these methods, we create a .NET assembly project to tell PowerBuilder to generate that non-visual object as an assembly (see Figures 1 and 2). Deploy the project (creating the assembly) and we're halfway done.

Creating CCW Registry Entries for the Assembly
Now that we have our own .NET assembly, we have to create the registry entries that the PowerBuilder Classic application will use in order to use the assembly. We could also use a manifest file instead of registry entries, something I explained in one of the previous PBDJ articles on using CCWs. However, for this particular demo we're going to stick with registry entries.

The way you get those registry entries is by running the REGASM utility from the .NET SDK on the assembly. I've named this assembly PBSmtpClient.dll, so I would run REGASM on the assembly at the command line as follows:

REGASM  PBSmtpClient.dll /regfile:PBSmtpClient.reg

If you ran REGASM directly against the assembly with no options, it would automatically add the entries to the registry of the machine you ran it on. For a couple of reasons, you might want to generate the registry entries as a registry file and then incorporate it later. One reason is that you might want to use that registry file (or the information in it) to deploy the assembly on other machines.

A second reason is specific to 64-bit operating systems, such as the 64-bit version of Windows 7. When you run REGASM on an assembly on a 64-bit operating system, it only creates the registry entries that are required for 64-bit applications. You'll need to do a bit of copy and pasting to add the information needed for 32-bit applications, such as a PowerBuilder Classic application. What you will do is open the registry file and copy and paste the generated registry entries so that there are two sets in the file. Then for the second set, you will search for HKEY_CLASSES_ROOT and replace it with HKEY_CLASSES_ROOT\Wow6432Node. The Wow6432Node is the subkey of all the main registry keys that contain entries used by 32-bit applications.

For example, the first entry in the file would look something like this:

[HKEY_CLASSES_ROOT\PBSmtpClient.SmtpClient]
@="PBSmtpClient.SmtpClient"

And after your copy, paste and replace operation, there would be an additional entry in the file that would look like this:

[HKEY_CLASSES_ROOT\Wow6432Node\PBSmtpClient.SmtpClient]
@="PBSmtpClient.SmtpClient"

Once you've done that (assuming you're on a 64-bit system) double-click the registry file to have Windows add the information to the registry. Of course if you're on a 32-bit system, you could do that directly with what REGASM generated.

Calling the Assembly from PowerBuilder Classic
Once the registry entries have been added to the Windows registry, a PowerBuilder Classic application can now access the assembly as if it was a standard COM component using OLE Automation. The code is pretty much the same as we've used in previous samples where we were using a Visual Studio-generated assembly:

integer             li_rc
oleobject          smtp

smtp = CREATE oleobject
li_rc = smtp.ConnectToNewObject ( "PBSmtpClient.SmtpClient" )
smtp.SetFrom ( "[email protected]" )
smtp.SetTo ( "[email protected]" )
smtp.SetSubject ( "Test" )
smtp.SetBody ( "This is only a test" )
smtp.SetHost ( "mail.domain.com" )
smtp.SendMessage()

smtp.DisconnectObject()
Destroy smtp

A Few Final Thoughts
There are some limitations to this approach, which I also covered in my session at TechWave 2008, primarily related to capturing error information from exceptions raised by the assembly (and I indicated in that session how to address that problem). That aside, the technique should be quite useful for those applications where the amount of calls you need to make to .NET classes is limited enough that a full migration to a WinForm or WPF application is not really justified. Using this technique will allow you to maintain that application as a PowerBuilder Classic application, but still add functionality from .NET as needed.

More Stories By Bruce Armstrong

Bruce Armstrong is a development lead with Integrated Data Services (www.get-integrated.com). A charter member of TeamSybase, he has been using PowerBuilder since version 1.0.B. He was a contributing author to SYS-CON's PowerBuilder 4.0 Secrets of the Masters and the editor of SAMs' PowerBuilder 9: Advanced Client/Server Development.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.