Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Late Binding

In Automation with Juno, we learnt how to automate the Windows shell. We used early binding in that example. Here, we'll see how late binding can accomplish similar results. Our program will automate the Collaboration Data Objects for Windows 2000 (CDOSYS) message component to send an email.

Step 1: Update your juno.com package

Ensure you have the latest Juno release. Click here for the latest version.

Step 2: The code

In your code editor, create a new source file named cdotest.d and add the following imports:

import juno.com.core, juno.com.client;

juno.com.client contains helper functions and classes to perform late binding, such as the DispatchObject class, which you use to create instances of late-bound COM objects. It wraps the IDispatch interface specifically for the purposes of late binding.

Next, we need to create an instance of CDOSYS's Message coclass, which we'll use to build our email message.

void main() {
  scope message = new DispatchObject("CDO.Message");
}

If you've seen automation done in Visual Basic, that might look familiar. For comparison, here's the equivalent VB code:

  Dim message
  Set message = CreateObject("CDO.Message")

The DispatchObject constructor essentially does the same thing as calling CreateObject in VB. Note that we've used the scope attribute in the D code: this is so that the underlying COM object's reference count is automatically decremented at the end of the scope. There's another D trick we can employ: local type inference, which will save few a key presses:

  scope message = new DispatchObject("CDO.Message");

Let's get on and build our email. We now need to set various properties of the Message class. If we were just using a pure IDispatch reference, we'd need to call IDispatch.Invoke and marshal the arguments into VARIANTs and a DISPPARAMS structure (among other arcane things) - tedious and error prone. Here's how we can set a property on a COM object using Juno:

  message.set("Subject", "Hello, World!");

This checks to see if the object has a property named "Subject", and sets its value to "Hello World". It's similar to the following VB code:

  message.Subject = "Hello World!"

The rest of the message can be built in the same way.

  message.set("TextBody", "Just saying Hello.");
  message.set("From", "me@home.com");
  message.set("To", "world@large.com");

Of course, you'd replace the fictional email addresses with real addresses, just as you would in an email client like Outlook. And how do we send an email? That's right - by clicking the "Send" button. In our code, we call the "Send" method on the message instance.

  message.call("Send");

If all went well, world@large.com would receive an email that looks something like this:

From: me@home.com
To: world@large.com
Subject: Hello, World!

Just saying Hello.

However, unless your computer is running an SMTP server, the message won't have been sent. So let's configure the message to be sent via a remote server. We've already looked at setting properties; now we'll see how to get some properties and use them.

  DispatchObject varConfig = message.get("Configuration");

The above code calls the Message's Configuration property, which returns an instance of the CDOSYS Configuration coclass. We're using late binding, so properties return VARIANTs, but Juno wraps them in a DispatchObject instance.

  scope config = message.get("Configuration");

  // Call config's Fields property and get a reference to its Fields instance
  scope fields = config.get("Fields");

  // Set the appropriate values
  scope sendUsing = fields.get("Item", 
    "http://schemas.microsoft.com/cdo/configuration/sendusing");
  sendUsing.set("Value", 2); // cdoSendUsingPort = 2

  scope port = fields.get("Item", 
    "http://schemas.microsoft.com/cdo/configuration/smtpserverport");
  port.set("Value", 25);

  scope server = fields.get("Item", 
    "http://schemas.microsoft.com/cdo/configuration/smtpserver");
  server.set("Value", "mail.remote.com"); // Replace 'mail.remote.com' with your remote server's address

If the remote mail server requires authentication, there's a few more properties we need to set.

  scope authentication = fields.get("Item",
    "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
  authentication.set("Value", 1); // cdoBasic = 1

  scope userName = fields.get("Item",
    "http://schemas.microsoft.com/cdo/configuration/sendusername");
  userName.set("Value", "username"); // Replace 'username' with your account's user name

  scope password = fields.get("Item",
    "http://schemas.microsoft.com/cdo/configuration/sendpassword");
  password.set("Value", "password"); // Replace 'password' with your account's password

Finally, call the Update method on the fields instance.

  fields.call("Update");

And we're done. Now we can send our email.

  message.call("Send");

If the call fails, a COMException will be thrown. If on the other hand it succeeds, you can just wait to see if you get a reply.

In the 0.3 version of Juno, you can set indexed properties, such as Fields.Item above, in one go rather than getting all those DispatchObjects. It works like this:

  config.set("Fields", "http://schemas.microsoft.com/cdo/configuration/smtpserverport", 25);
  config.set("Fields", "http://schemas.microsoft.com/cdo/configuration/smtpserver", "mail.remote.com");

Attachments

  • cdotest.d (1.5 kB) - added by John on 03/18/08 13:29:03.