Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

IDL For VB Tutorial : Page 5

The Interface Definition Language, or IDL, is a language for describing collections of data types, constants, methods (functions, procedures, etc.), and groups of methods known as interfaces. VB creates type information for you automatically when you create ActiveX components, but you can also create independent type information using IDL and the Microsoft IDL compiler. Using IDL in this way can enhance your programming skills and allow you to solve some pretty complex problems. The best way to get an understanding of how to write IDL, compile it to a type library, and use the type library in VB is see some real examples in a tutorial format, as this article demonstrates.


advertisement

Example 4: IDL for a Reason

The previous examples may help you understand how to write IDL and create, register, and reference the resulting compiled type libraries, but they dont give you a reason to care. This example is designed to provide a problem and an IDL/type library solution that will show you the power of this technique.



The Problem

Our team has been tasked with creating an application where large portions of functional requirements remain undefined. The team has sufficient business information to complete portions of the final system, and we have determined that many parts of the final system can be delivered independent of the others. The team has come to the consensus that we must build a shell application that will allow pieces of the system to plug in as they are completed and installed. This is similar to the concept of VB add-ins. The shell application will consist of a simple toolbar that will provide access to the individual plug-in components. The components (the subsystem DLLs) will hold all the forms and user interface functionality of a single subsystem. Here is a diagram depicting the idea:

This means our development will differ from normal VB application development. Under normal circumstances, we create DLLs to help with common functionality across all subsystems and the final application project contains all the user interface functionality. In addition, the DLLs are referenced by the application project and are included as a part of the final installation of the application. If we forgot to include a DLL in the installation, the system would crash. In our toolbar scenario, we want the DLLs to take on entire subsystem functionality and we want the finished application executable to work even if the DLLs are not there or are installed in the future. In other words, we need to create forward compatibility. This is the real programming problem we must overcome and we will solve this problem using a design pattern called Facade. In addition, we will create a type library to enforce our desired solution across the entire development effort and to ensure forward compatibility.

The Solution

Our solution rests on a simple capability VB possesses. Using the CreateObject() function we can dynamically create an instance of an object from another ActiveX component without actually referencing this ActiveX component in the client project. The code looks like this:

Dim p_objSubsystem As Object

Set p_objSubsystem = CreateObject(progid)

p_objSubsystem.DoSomething

The progid, short for programmatic identifier, is the human readable name for the object class were trying to create (like Word.Document). We want our toolbar application to get programmatic identifiers from a file or a database when it starts up. This will give us the flexibility to change which subsystems the toolbar loads. The drawback to the code above is its use of late binding (i.e., the Object data type). Late binding does not allow us to enforce a specific design. What we really want to do is something similar to this:

Public Function GetAddIn(ByVal strProgID As String) As PIL.PlugIn
   Dim p_objTest As Object

   On Error GoTo CreateError
   Set p_objTest = CreateObject(strProgID) 'attempt to create object

   If TypeOf p_objTest Is PIL.PlugIn Then 'test for correct interface
   Set GetAddIn = p_objTest
   End If

ExitFunc:
   Set p_objTest = Nothing 'clean up object variable
CreateError:
   Resume ExitFunc 'exit gracefully if the progid is bad
End Function 'GetAddIn

The name PIL in this example is be the name of a type library we would create for all the independent developers to reference. Each subsystem DLL would contain a class that implements the PlugIn interface from the PIL library. Here is the IDL for the PlugIn interface:

[
  uuid(9771F9AA-CB8E-11D3-A88C-EFEDC0026670),
  helpstring("Each Plug-In component must contain one class that implements the PlugIn interface."),
  nonextensible,
  oleautomation,
]
interface PlugIn : IUnknown {
  [helpstring("The PlugIn host will call Connect passing an App object. The PlugIn may keep references to the PlugIn provided the PlugIn destroys all App references when Disconnect is called.")]
  HRESULT Connect([in] App* HostApp );
  [helpstring("The PlugIn host will call Disconnect when the application must shut down. The PlugIn must destroy all references to the App object passed in the Connect method.")]
  HRESULT Disconnect();
  [helpstring("The PlugIn host will call Execute when the user has requested the services of the PlugIn object.")]
  HRESULT Execute ();
  [helpstring("Returns either Nothing or the Icon which represents the services of the PlugIn. Icon is displayed, if available, as a menu option to the user along with the Name.")]
  [propget] HRESULT Icon ([out, retval] IDispatch** PropData );
  [helpstring("Returns the display Name of the PlugIn. The PlugIn host creates a menu selection for the PlugIn using Name and Icon.")]
  [propget] HRESULT Name ([out, retval] BSTR* PropData );
};

The PlugIn interface is really a contract that details how future components can plug in to a preexisting toolbar application. In the Connect method youll notice an App object is passed in (App*). Here is the IDL for the App interface:

[
  uuid(9771F9AB-CB8E-11D3-A88C-EFEDC0026670),
  helpstring("The hosting application provides services to any PlugIn through the App interface."),
  nonextensible,
  oleautomation,
]
interface App : IUnknown {
  [helpstring("Returns the file path of the application.")]
  [propget] HRESULT Path([out, retval] BSTR* PropData );
  [helpstring("Displays application errors in an application standard error dialog.")]
  HRESULT ShowError([in] long ErrNumber,
                    [in] BSTR ErrMessage,
                    [in, optional, defaultvalue("")] BSTR ErrSource);
};

The App interface represents a contract detailing the services provided by the application toolbar to the individual plug in subsystems. This means the App interface will be implemented by a class in the toolbar application and an instance of this class will be passed to each subsystem component that connects to the application executable.

Design Patterns

Both the App and PlugIn interfaces are examples of the Facade design pattern. Design patterns are abstract models of objects and interfaces proven to solve specific problems. The Facade design pattern is used to simplify access to subsystems. Some information on design patterns can be found online or in magazine articles, but to get the definitive source on design patterns, I strongly suggest purchasing the following book:

  • Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides; Addison-Wesley

I consider this book the most important object-oriented programming reference Ive ever read. The book details twenty-three primary design patterns and includes information on the types problems solved by each. The most difficult aspect of learning about design patterns is that the patterns and the problems they solve are both very abstract. This abstract nature, however, means that each pattern is really a solution to a broad category of problems. The Facade pattern is fairly simple and the problem it addresses is not overly complex. Our solution is made more elegant through the use of an independent type library and the VB CreateObject function. Because our various VB projects do not reference each other specifically, we have the flexibility to change any subsystem component, or the toolbar executable, at any time so long as each new part adheres to the appropriate type library interface.

The Final System

The final example system cannot adequately be provided by code samples. So, in order for you to finish, I have packaged the complete example plug in system code for you. Just download in the following file: PIExample.Zip . Youll find these files in PIExample.Zip:

PIL.idl Plug In Library IDL
...
PIToolbar.vbp Plug In Toolbar VB Project
frmPIToolbar.frm Plug In Toolbar Form
frmPIToolbarErr.frm Plug In Toolbar Error Display Form
CToolbarApp.cls Plug In App Class (implements PIL.App)
modPIToolbar.bas Plug In Toolbar Module
PlugIns.lst Plug In List Text File
...
PIOne.vbp Sample Plug In One VB Project
frmPIOne.frm Sample Plug In One Form
PlugInOne.cls Sample Plug In One Class (implements PIL.PlugIn)
modPIOne.bas Sample Plug In One Module
...
PITwo.vbp Sample Plug In Two VB Project
frmPITwo.frm Sample Plug In Two Form
PlugInTwo.cls Sample Plug In Two Class (implements PIL.PlugIn)
modPITwo.bas Sample Plug In Two Module

Once youve downloaded an unpacked the example files, perform the following steps:

  1. Compile the PIL.idl file to a type library using the steps outlined in Example 2.
  2. Register the PIL.TLB file with Windows using the steps outlined in Example 2.
  3. Compile the PIToolbar.vbp project using VB and run the resulting PIToolbar.exe.
  4. The toolbar application will tell you it was unable to load the PIOne and PITwo plug in components. The toolbar does this because the PlugIns.lst file contains programmatic identifiers for both sample plug in components.
  5. Compile the PIOne.vbp project using VB and run the PIToolbar.exe. The toolbar application will tell you it was unable to load the PITwo plug in component only.
  6. Compile the remaining PITwo.vbp project and run the PIToolbar.exe.
  7. To see the code in action, load all the VB projects in separate instances of VB and set a break point on the Sub Main procedure of the modPIToolbar.bas module.
  8. Start the two plug in components and finally run the toolbar project. Step through the code an watch the toolbar connect to each plug in.

There you have it. This example is really only a minor display of how design patterns can provide elegant and flexible solutions to complex problems.

Conclusions

Here are some advantages to using IDL created type libraries:

  1. It forces you to separate your design from its implementation. This is a subtle but powerful technique.  The enterprise edition allows you to create separate type libraries files for your components, but these type libraries are very limited in what they can express and their purpose is focused on remote access of components.
  2. You can precisely control the specification of your components classes and make major design changes without the headaches associated with VB binary and project compatibility settings. In some cases you can get away with just setting a DLL project to No Compatibility and still have it work with existing EXEs.
  3. Because VB always includes a type library in your DLL, your components are vulnerable to abuse and misuse by VBA hacks.  If you're creating DLL's for in-process use only, you don't need to distribute your type libraries with your application.  This virtually assures the security of your DLL's.
  4. You can use VB keywords as names for properties or methods.  You can also include helpstrings for members of the type library where VB does not provide support (i.e., helpstrings for enumerations).
  5. You can make applications that are more flexible and independent from the various DLLs you use to put them together.
  6. You can include string and numeric constants in a type library and if you dont use them in a particular project that references the type library, they do not consume resources in the final compiled program.

Here are some disadvantages to using IDL created type libraries:

  1. You have to learn a new language and have a deeper understanding of COM.
  2. You can easily create type information in IDL that is unusable in Visual Basic.
  3. You must be more careful when you make changes to your IDL and generate a new library. You can easily break compatibility and cause existing applications to crash if youre not careful.

This technique alone does not solve any earth shattering problems. However, when you couple the use of IDL created type libraries with design patterns, you will probably find this kind of programming power irresistible. In my experience, I have found the advantages far outweigh the disadvantages.




Philip Fucich is an independent contractor for Comforce IT in Dallas, TX.  He is working as the lead Visual Basic developer on a large scale, multi-tier, real-time personnel management system for Southwest Airlines.  He has been programming professionally for ten years.  He first started programming as a hobby in 1984 on the Apple II in Basic and Pascal.  Philip can be reached at yellowbit@mindspring.com.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date