= Automation with Juno = In this first tutorial, we'll learn how to generate a source module from a COM type library. Then we'll use it to automate the Windows shell. === Step 1: Convert the type library === Open up a console window and use Tlbimpd to create an import module from the Microsoft Shell Controls and Automation library - otherwise known as shell32.dll. [[Image(browser:docs/tlbimpd.shell32.png, nolink)]] The above command generates a module named {{{shell32}}} in a file named {{{shell32.d}}}. The {{{/comments}}} argument just includes extra information found in the type library that can't be represented in code, for example member identifiers and certain IDL attributes. It also adds documentation to the module if the type library provides it. === Step 2: Write a program to automate the shell === In your code editor, create a new source file called {{{shellauto.d}}} and add the following import declaration: {{{ #!d import juno.com.core, shell32, bstr = juno.com.bstr; }}} {{{juno.com.core}}} contains the bare essentials to work with COM objects; {{{shell32}}} is the module we generated in Step 1. Our demonstration program will list the files on your Desktop. First, we need to create an instance of the {{{Shell}}} coclass. If {{{Shell}}} wasn't a COM class, but an ordinary class that implemented an interface named {{{IShellDispatch}}}, you'd write: {{{ #!d IShellDispatch shell = new Shell(); }}} But to create an instance of a coclass, you use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/7295a55b-12c7-4ed0-a7a4-9ecee16afdec.asp CoCreateInstance]. Juno provides a more convenient way that's similar to {{{new}}}ing a class: {{{ #!d IShellDispatch shell = Shell.coCreate!(IShellDispatch); }}} Note that there's no need to call [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/0f171cf4-87b9-43a6-97f2-80ed344fe376.asp CoInitialize] or {{{CoUninitialize}}} - Juno takes care of starting and shutting down COM for you. There's one other thing to note with the above code: it leaks memory. Unlike typical D objects, COM objects aren't garbage collected. Instead, COM uses reference counting to manage the lifetime of an object. In D, as in C++, we have to explicity decrement the reference count. Usually, this is done by calling {{{Release()}}} once you've finished using an object. But it's easy to forget to do this. Juno defines a function that you can use nearer the declaration instead of at the end of a scope: {{{releaseAfter}}} {{{ #!d IShellDispatch shell = Shell.coCreate!(IShellDispatch); releaseAfter (shell, { // Use the shell variable. }); // shell gets released here. }}} We'll employ the above technique throughout the code to ensure our objects are released. The remainder of the code is fairly straightforward. Here's the complete listing: {{{ #!d void main() { IShellDispatch shell = Shell.coCreate!(IShellDispatch); releaseAfter (shell, { // Get a reference to the Desktop folder. Folder folder; shell.NameSpace(toVariant(ShellSpecialFolderConstants.ssfDESKTOP), folder); if (folder !is null) { releaseAfter (folder, { // Get the collection of FolderItem objects. FolderItems items; folder.Items(items); if (items !is null) { releaseAfter (items, { // Iterate through all the FolderItem objects. int count; items.get_Count(count); for (int i = 0; i < count; i++) { FolderItem item; items.Item(toVariant(i), item); if (item !is null) { releaseAfter (item, { // Get the name of the item. wchar* bstrName; item.get_Name(bstrName); // Convert the BSTR to a UTF-8 string. bstr.toString frees the BSTR. char[] name = bstr.toString(bstrName); // Display the result. writefln(name); }); } } }); } }); } }); } }}} === Step 3: Compile and run === Add {{{shell32.d}}} and {{{shellauto.d}}} to DMD's command line, and compile. When you run {{{shellauto.exe}}}, it will output the names of each item on your Desktop. === Considerations === So how do you know which applications have programmable interfaces? Perhaps the simplest way is to look the application up in [http://www.microsoft.com/downloads/details.aspx?FamilyID=5233b70d-d9b2-4cb5-aeb6-45664be858b6&DisplayLang=en OleView], which displays a list of registered type libraries along with any classes and interfaces it contains. You can also find out the GUID and path of a type library, either of which can be passed to [TypeLibraryImporter Tlbimpd] to generate a D module. If you run into a Stack Overflow when running the example, make sure that you compile with optimizations turned on (-O switch).