Tuesday, August 01, 2006

Calling Dot Net Components from PowerBuilder via COM wrappers

I used to implement SMTP for PowerBuilder using a PBNI class that used P.J. Naughter's CPJNSMTPConnection MFC class to do the heavy lifting. Well, .Net 2.0 changes all that. One of the new features of .Net 2.0 is the introduction of an SMTPClient class. One of the features of .Net since it was introduced was the ability for non-.Net applications to call .Net components through the use of a COM Callable Wrapper (CCW).

I'm going to demonstrate how to use a CCW to utilize the new SMTPClient class in .Net 2.0. The first thing we'll need to do is create our own .Net component that uses the new class. We'll start by creating a Class Library using Visual Studio.Net 2005. Note that although I haven't tried it personally, I believe everything that I'm doing is possible with Visual C# Express.





Once we've created the new solution, we should rename the C# file that was automatically created from "Class1" to something a bit more meaningful. We can do that in the Solution Explorer, and we will be prompted to allow Visual Studio to apply the change throughout the code.





We also need to make a couple of adjustments to the project properties. Right click on the project and select Properties. On the Application tab, we need to select the "Assembly Information..." command button to access the Assembly Information editor.



Once there, we need to check the "Make assembly COM-visible" option. You can also provide values for some of the other assembly properties at this time.



We're going to be adding our assembly to the Global Assembly Cache (GAC), and we need to have a strong name for it to do that. So we go to the Signing tab of the Project properties and check the "Sign the assembly" option.

Then click the drop down list box under "Choose a strong name key file" and select the "" option. This displays the Create Strong Key Name dialog. I've chosen not to assign a password for it.

Using this dialog automatically creates the strong name file and adds it as a reference to the project.

At some point after creating our assembly we're going to have to:

(a) add the assembly to the GAC using the GACUTIL utility
(b) create a REG file with the COM entries for the registry using the REGASM utility

We can have the compiler do that for us automatically by adding calls to those utilities in a post-build build event.

Here I'm dynamically creating environmental variables to point to the location of the .Net Framework 2.0 directory (where the REGASM utility is found) and the .Net 2.0 SDK bin directory (where the GACUTIL utility is found). It's not necessary, but it is helpful if you're calling more than one utility in those directories. It also helps makes the later command lines more legible. Note that the call to GACUTIL has quotes around the command line and the TargetPath argument. That's because the path to the .Net 2.0 SDK directory and the TargetPath have embedded spaces, and the command will fail if the quotes are not used.

The output of the REGASM utility is a .REG file which we will need to run (double click) in order to add the entries into the registry.

So far we've set up a class that can be used as a CCW, but it doesn't have any code it in yet. Go back to the class file and add "using System.Net.Mail" to the using section so we can reference the new SMTPClient classes without having to fully qualify them. Then add the following code to implement the class:
private String senderName;

public String SenderName
{
get { return senderName; }
set { senderName = value; }
}
private String senderEmail;
public String SenderEmail
{
get { return senderEmail; }
set { senderEmail = value; }
}
private String smtpServer;
public String SmtpServer
{
get { return smtpServer; }
set { smtpServer = value; }
}
private String recipientEmail;
public String RecipientEmail
{
get { return recipientEmail; }
set { recipientEmail = value; }
}
private String subject;
public String Subject
{
get { return subject; }
set { subject = value; }
}
private String ccEmail = "";
public String CcEmail
{
get { return ccEmail; }
set { ccEmail = value; }
}
private String bccEmail = "";
public String BccEmail
{
get { return bccEmail; }
set { bccEmail = value; }
}
private bool isHTML = false;
public bool IsHTML
{
get { return isHTML; }
set { isHTML = value; }
}
private String messageText;
public String MessageText
{
get { return messageText; }
set { messageText = value; }
}
public void sendMail()
{
SmtpClient smtpClient = new SmtpClient();
MailMessage message = new MailMessage();

MailAddress fromAddress = new MailAddress(senderEmail, senderName);
smtpClient.Host = smtpServer;
smtpClient.Port = 25;
message.From = fromAddress;
message.To.Add(recipientEmail);
message.Subject = subject;
if (!ccEmail.Equals(""))
{
message.CC.Add(ccEmail);
}
if (!bccEmail.Equals(""))
{
message.Bcc.Add(ccEmail);
}
message.IsBodyHtml = isHTML;
message.Body = messageText;
smtpClient.Send(message);

}

Compile the new class, and then (assuming there were no errors) run the REG file to add the COM information to the registry.

We probably want to test out the class to make sure it does what we're expecting. We can do that within Visual Studio.Net 2005 (or Visual C# Express) by creating another project to test the assembly. Create another solution, this time a standard Windows Application. We'll need to add a reference to the assembly, so select Project->Add Reference from the menu, choose the Browse tab in the Add Reference dialog and then navigate to the assembly in the build directory from the previous project.



Now drop a CommandButton on the default form, and then double click on it to bring up the editor for its Click event. Add the following code, inserting your own name, email address and email server:


DotNetSMTP.DotNetSMTP smtpclient = new DotNetSMTP.DotNetSMTP();
smtpclient.SenderName = "your name";
smtpclient.SenderEmail = "your email address";
smtpclient.SmtpServer = "your email server";
smtpclient.RecipientEmail = "your email address";
smtpclient.MessageText = "This is a test, this is only a test.";
smtpclient.Subject = "Dot Net via COM is cool";
smtpclient.sendMail();


Run the project, and when you click on the button in the form you should send yourself email using the assembly.

Because of the CCW, it's as easier (perhaps even easier) to call the assembly from PowerBuilder. Just add a CommandButton to a window in PowerBuilder, and add the following code to its clicked event:


integer li_rc
oleobject loo_smtp
loo_smtp = CREATE oleobject
li_rc = loo_smtp.ConnectToNewObject ( "DotNetSMTP.DotNetSMTP" )
loo_smtp.SenderName = "your name"
loo_smtp.SenderEmail = "your email address"
loo_smtp.RecipientEmail = "your email address"
loo_smtp.SmtpServer = "your email server"
loo_smtp.MessageText = "This is a test, this is only a test."
loo_smtp.Subject = "Dot Net via COM is cool"
loo_smtp.sendMail()
Destroy loo_smtp


That's it! You're done. You can use the same technique to call other .Net components. And you'll probably want to expand on this example to add support for authentication to the email server, attachments, multiple recipients, etc. However, the supplied sample should get you well started.

1 comment:

Calling Dot Net Components from PowerBuilder via COM wrappers – Redux « Bruce Armstrong’s Blog said...

[...] a comment » Back in August of 2006, I wrote an entry about calling .Net components from PowerBuilder using COM wrappers (i.e., CCW). Since I was basing it on a registry entry approach still, the technique demonstrated [...]