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 4

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 3: IDL to VB

This example will begin to show you how to write IDL by showing the effects of an IDL generated type library in VB. You will see which IDL data types translate to VB data types and which IDL method declarations translate to VB method declarations. This is the meat and potatoes of examples. Therefore, I will give much more information here than in the previous examples. The best place to start is with the IDL itself. Here is the entire example IDL:

// TypeLib : EX3.IDL (EX3)
// TargetFile : EX3.TLB
//
// Version 1.0
// - Author : Philip G. Fucich
// - Completed: 01/01/2000
// - Notes : Types and interfaces for use in VB.

// UUID Usage:
// {3D6C1720-F527-11D3-A88C-ECBD3B289C70} : library EX3
// {3D6C1722-F527-11D3-A88C-ECBD3B289C70} : typedef SampleEnum
// {3D6C1724-F527-11D3-A88C-ECBD3B289C70} : typedef SampleUDT
// {3D6C1726-F527-11D3-A88C-ECBD3B289C70} : interface AllTypes
// {3D6C1728-F527-11D3-A88C-ECBD3B289C70} : interface AllMethods
// {3D6C172A-F527-11D3-A88C-ECBD3B289C70} : module Constants

[
uuid(3D6C1720-F527-11D3-A88C-ECBD3B289C70),
version(1.0),
helpstring("Example 3 Library")
]
library EX3
{
importlib("STDOLE2.TLB");

interface AllTypes;
interface AllMethods;

typedef
[
  uuid(3D6C1722-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Sample enum declaration."),
  v1_enum,
  version(1.0)
]
  enum SampleEnum {
    [helpstring("Helpstring for seDefault.")]
    seDefault = 0,
    seOne = 1,
    seTwo = 2,
    [helpstring("Enum constant using hex notation.")]
    seHex7F000000 = 0x7F000000,
    seHex7F001001 = 0x7F001001
} SampleEnum;

typedef
[
  uuid(3D6C1724-F527-11D3-A88C-ECBD3B289C70),
  version(1.0)
]
struct SampleUDT {
  VARIANT_BOOL   Boolean;
  unsigned char  Byte;
  CURRENCY       Currency;
  SampleEnum     CustEnum;
  AllMethods*    CustObj;
  DATE           Date;
  double         Double;
  short          Integer;
  long           Long;
  IDispatch*     Object;
  float          Single;
  BSTR           String;
  VARIANT        Variant;
  [helpstring("Array of Variants.")]
  SAFEARRAY(VARIANT) Array;
} SampleUDT;

[
  uuid(3D6C1726-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example interface showing VB compatible data types."),
  nonextensible,
  oleautomation,
]
interface AllTypes : IUnknown {
  HRESULT TheObject ([out, retval] IDispatch** FuncData );
  [helpstring("Helpstring for TheVariant function.")]
  HRESULT TheVariant ([out, retval] VARIANT* FuncData );
  HRESULT TheBoolean ([out, retval] VARIANT_BOOL* FuncData );
  HRESULT TheByte ([out, retval] unsigned char* FuncData );
  HRESULT TheCurrency ([out, retval] CURRENCY* FuncData );
  HRESULT TheDate ([out, retval] DATE* FuncData );
  HRESULT TheDouble ([out, retval] double* FuncData );
  HRESULT TheSingle ([out, retval] float* FuncData );
  HRESULT TheLong ([out, retval] long* FuncData );
  HRESULT TheInteger ([out, retval] short* FuncData );
  HRESULT TheString ([out, retval] BSTR* FuncData );
  HRESULT TheCustomObj ([out, retval] AllMethods** FuncData );
  HRESULT TheCustomEnum([out, retval] SampleEnum* FuncData );
  HRESULT TheCustomUDT ([out, retval] SampleUDT* FuncData );
};

[
  uuid(3D6C1728-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example interface showing VB equivalent methods and parameter passing."),
  nonextensible,
  oleautomation,
]
  interface AllMethods : IUnknown {
    [propget] HRESULT TheProperty([out, retval] VARIANT* PropData);
    [propput] HRESULT TheProperty([in] VARIANT PropData);
    [propputref] HRESULT TheProperty([in] VARIANT PropData);
    HRESULT TheFunction([out, retval] VARIANT* FuncData);
    HRESULT TheSub();
    HRESULT TheByValParams([in] long ByValLong,
         [in,
          optional] long OptByValLong,
         [in, optional,
          defaultvalue(2)] long OptDef2ByValLong );
    HRESULT TheByRefParams([in, out] long* ByRefLong,
         [in, out,
          optional] long* OptByRefLong,
         [in, out,
          optional,
          defaultvalue(4)] long* OptDef4ByRefLong );
    [vararg] HRESULT TheParamArray
        ([in, out] SAFEARRAY(VARIANT)* VntArgs );
};

[
  uuid(3D6C172A-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example set of constants that can be used in VB.")
]
  module Constants {
    [helpstring("Equivalent of vbCrLf. Uses cryptic C escape characters.")]
    const LPSTR seCrLf = "\r\n";

    const LPSTR seHello = "Hello World!.";

    [helpstring("Here's a Long integer.")]
    const long seZero = 0;
};

}



This might seem a little overwhelming, so lets go through the code step by step.

  1. The first part is a group of comments I added to help document the IDL. All comments are started with the // characters or surrounded by the /* and */ characters:
// TypeLib : EX3.IDL (EX3)
// TargetFile : EX3.TLB
//
// Version 1.0
// - Author : Philip G. Fucich
// - Completed: 01/01/2000
// - Notes : Types and interfaces for use in VB.

// UUID Usage:
// {3D6C1720-F527-11D3-A88C-ECBD3B289C70} : library EX3
// {3D6C1722-F527-11D3-A88C-ECBD3B289C70} : typedef SampleEnum
// {3D6C1724-F527-11D3-A88C-ECBD3B289C70} : typedef SampleUDT
// {3D6C1726-F527-11D3-A88C-ECBD3B289C70} : interface AllTypes
// {3D6C1728-F527-11D3-A88C-ECBD3B289C70} : interface AllMethods
// {3D6C172A-F527-11D3-A88C-ECBD3B289C70} : module Constants

Notice the UUID Usage: section I included here. The library, typedef, interface, and module type definitions require a UUID (Universally Unique Identifier) a.k.a. GUID (Globally Unique Identifier). This is where the Visual Studio tool, GUIDGEN.EXE, comes to the rescue. All you do is run GUIDGEN.EXE and click the Copy button. Then you paste the UUID right into the IDL. If you need more than one, just copy it and increment first hex number group as I have done here. You must consider each UUID you use as sacred. If you use a set of UUIDs in a type library you must never use them ever again. If you did, they wouldnt be unique. One could write an entire article on the subject of UUID use, extending interface definitions, and other binary compatibility issues, but this information already exists in print. Some of the best technical information about UUIDs and other COM issues can be found in the following references:

  • Programming Distributed Applications with COM and Microsoft Visual Basic 6.0, Ted Pattison, Microsoft Press
  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)COM
  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)Automation
  1. The next part of the IDL is the library statement which tells the MIDL compiler to create a type library:
[
  uuid(3D6C1720-F527-11D3-A88C-ECBD3B289C70),
  version(1.0),
  helpstring("Example 3 Library")
]
library EX3
{
  // lots of stuff in the middle then..
}

You can see I have used the first UUID and included a helpstring for the library structure. The code in brackets, [], is called an attribute list. When you compile the IDL and view the resulting type library in VBs object browser, youll be able to see the help string, the version, and the EX3 library name. All the IDL from here on out will be inside the library structure (i.e., between the braces, {}).

  1. The next statement is an importlib statement:
importlib("STDOLE2.TLB");

This allows one type library to reference types of another (included here just as an example).

  1. Next come a couple of forward declarations. This is concept foreign to VB programmers. Just remember that the MIDL compiler parses the IDL code from top to bottom so if you reference a type that has not yet been declared youll get an error. Its best to always create forward declarations for all your interfaces and put all typedef definitions in order at the top.
interface AllTypes;
interface AllMethods;

  1. After the forward declarations, I include my typedef definitions. This first one in the sample IDL is a typedef enum which is like a VB enum:
typedef
[
  uuid(3D6C1722-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Sample enum declaration."),
  v1_enum,
  version(1.0)
]
enum SampleEnum {
  [helpstring("Helpstring for seDefault.")]
  seDefault = 0,
  seOne = 1,
  seTwo = 2,
  [helpstring("Enum constant using hex notation.")]
  seHex7F000000 = 0x7F000000,
  seHex7F001001 = 0x7F001001
} SampleEnum;

Use the v1_enum attribute for all enumeration definitions. This makes them equivalent to VB enumeration definitions. Also notice that you can include a help string attribute for each constant in the enumeration.

  1. Next, I included a typedef struct definition. This is like a VB type statement.
typedef
[
  uuid(3D6C1724-F527-11D3-A88C-ECBD3B289C70),
  version(1.0)
]
struct SampleUDT {
  VARIANT_BOOL  Boolean;
  unsigned char Byte;
  CURRENCY      Currency;
  SampleEnum    CustEnum;
  AllMethods*   CustObj;
  DATE          Date;
  double        Double;
  short         Integer;
  long          Long;
  IDispatch*    Object;
  float         Single;
  BSTR          String;
  VARIANT       Variant;
  [helpstring("Array of Variants.")]
  SAFEARRAY(VARIANT) Array;
} SampleUDT;

In IDL the type of a variable is given before the name of the variable. For example, the first member of this structure is named Boolean and its type is VARIANT_BOOL. Of course, this is the opposite of VB declarations. The * character denotes that a variable is a pointer. You cannot use the * anywhere you want and expect your declarations to be usable in VB. The pointers here are for object type elements and will be acceptable to VB. You might be wondering if VB can handle this type definition because the some of the members are named using VB keywords. You can create a VB type definition equivalent to this one except for the Array element. In VB, this would be written, Array() As Variant, and would be illegal. One of the positive aspects of IDL is that you can name methods and type members with VB keywords and they will work just fine.

  1. Now we come to the interfaces. An interface is exactly like a VB class but has no implementation, which is like saying there is no working code for any of the methods. After we create the type library, we will create a VB class to implement all the methods of the interface. Interface definitions can vary widely depending on their intended use. Because we want to use interfaces in VB, all our interfaces will include the oleautomation and nonextensible attributes and will be derived from IUnknown. The references I included above will help you gain an understanding of what this means. Lets take a closer look at the first interface I included in the sample IDL:
[
  uuid(3D6C1726-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example interface showing VB compatible data types."),
  nonextensible,
  oleautomation,
]
interface AllTypes : IUnknown {
  HRESULT TheObject ([out, retval] IDispatch** FuncData );
  [helpstring("Helpstring for TheVariant function.")]
  HRESULT TheVariant ([out, retval] VARIANT* FuncData );
  HRESULT TheBoolean ([out, retval] VARIANT_BOOL* FuncData );
  HRESULT TheByte ([out, retval] unsigned char* FuncData );
  HRESULT TheCurrency ([out, retval] CURRENCY* FuncData );
  HRESULT TheDate ([out, retval] DATE* FuncData );
  HRESULT TheDouble ([out, retval] double* FuncData );
  HRESULT TheSingle ([out, retval] float* FuncData );
  HRESULT TheLong ([out, retval] long* FuncData );
  HRESULT TheInteger ([out, retval] short* FuncData );
  HRESULT TheString ([out, retval] BSTR* FuncData );
  HRESULT TheCustomObj ([out, retval] AllMethods** FuncData );
  HRESULT TheCustomEnum([out, retval] SampleEnum* FuncData );
  HRESULT TheCustomUDT ([out, retval] SampleUDT* FuncData );
};

The name of this interface is AllTypes and it contains function declarations only. Each function declared here returns two variables. The first is called an HRESULT, which is a specially defined long integer designed to signal the success or failure of any COM method call. The other is the return type parameter marked with the [out, retval] attributes. The return types here are given the name FuncData. With functions and property get procedures, the return type is named in IDL but the name will not be visible anywhere in VB.

  1. The second interface definition in the sample IDL is designed to show different types of method declarations and parameter passing acceptable to VB:
[
  uuid(3D6C1728-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example interface showing VB equivalent methods and parameter passing."),
  nonextensible,
  oleautomation,
]
interface AllMethods : IUnknown {
  [propget] HRESULT TheProperty([out, retval] VARIANT* PropData);
  [propput] HRESULT TheProperty([in] VARIANT PropData);
  [propputref] HRESULT TheProperty([in] VARIANT PropData);
  HRESULT TheFunction([out, retval] VARIANT* FuncData);
  HRESULT TheSub();
  HRESULT TheByValParams([in] long ByValLong,
          [in,
           optional] long OptByValLong,
          [in, optional,
           defaultvalue(2)] long OptDef2ByValLong );
  HRESULT TheByRefParams([in, out] long* ByRefLong,
          [in, out,
           optional] long* OptByRefLong,
          [in, out,
           optional,
           defaultvalue(4)] long* OptDef4ByRefLong );
  [vararg] HRESULT TheParamArray
         ([in, out] SAFEARRAY(VARIANT)* VntArgs );
};

All the IDL given here will make much more sense when you see what happens in VB when you implement the methods of these interfaces and use the declared typedefs. Notice here that all methods return the HRESULT. This is because all COM methods are essentially functions. Of course, VB hides this from you. In VB, all these methods will take on their familiar forms and thats all we care about. The propget, propput, and propputref attributes are used to mark a method as a property get, property let, or property set method respectively. The vararg attribute marks a method as having a variable number of arguments for the last parameter of the method. Youll notice that parameters are primarily marked as [in], [in, out], or [out, retval]. These correspond to VB in the following way:

[in] = ByVal
[in, out] = ByRef
[out, retval] = (Return type of function or property get)
  1. The last declaration in the sample IDL is the module. If you get really good with IDL, youll find that you can use module declarations to define Windows API function calls. This will help you avoid writing those pesky declare statements in VB. The module declaration included here is just for creating various constants in a type library:
[
  uuid(3D6C172A-F527-11D3-A88C-ECBD3B289C70),
  helpstring("Example set of constants that can be used in VB.")
]
module Constants {
  [helpstring("Equivalent of vbCrLf. Uses cryptic C escape characters.")]
  const LPSTR seCrLf = "\r\n";

  const LPSTR seHello = "Hello World!.";

  [helpstring("Here's a Long integer.")]
  const long seZero = 0;
};

To understand these declarations, look up the keywords in MSDN Library:

  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)MIDL

To see how this all translates to VB complete the following steps:

  1. Copy the IDL to Notepad and save the file as EX3.IDL.
  2. Compile the IDL using the steps in Example 2.
  3. Register the resulting type library with Windows using the steps in Example 2.
  4. Create a new ActiveX DLL project in VB, change the name of the project from Project1 to EX3Server, and reference the Example 3 Library in the project.
  5. Create a class called CAllTypes and include the following line at the top of the class module:
Implements EX3.AllTypes
  1. Implement all the methods of the AllTypes interface as you see fit.
  2. Create a class called CAllMethods and include the following line at the top of the class module:
Implements EX3.AllMethods
  1. Implement all the methods of the AllMethods interface as you see fit.
  2. Run the ActiveX DLL project in VB.
  3. Open another instance of VB, create a new Standard EXE project, and reference the Example 3 Library in the project.
  4. On Form1 of the project add a command button.
  5. In the Command1 click event, add the following code to test out the ActiveX DLL classes and their methods:
Dim p_AllTypes As EX3.AllTypes
Dim p_AllMethods As EX3.AllMethods

Set p_AllTypes = CreateObject("EX3Server.CAllTypes")
Set p_AllMethods = CreateObject("EX3Server.CAllMethods")

Debug.Print p_AllTypes.TheBoolean
Debug.Print p_AllTypes.TheByte
Debug.Print p_AllTypes.TheCurrency
  1. Set a break point on the Command1 click event, run the Standard EXE project, and step through the code.

Also, open the Object Browser in VB to see how all the declarations have translated to VB.

Here, again, are all the references Ive mentioned throughout this example. These references will provide you with important background that is too lengthy to include here:

  • Programming Distributed Applications with COM and Microsoft Visual Basic 6.0, Ted Pattison, Microsoft Press
  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)COM
  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)Automation
  • MSDN Library(+)Platform SDK(+)COM and ActiveX Object Services(+)MIDL


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