Showing posts with label COM. Show all posts
Showing posts with label COM. Show all posts

Friday, August 13, 2010

Sneak Peek - COM Registration

The menu items Run | Register ActiveX Server and Run | Unregister ActiveX Server have been changed. There is now one menu item under the Run menu called ActiveX Server with three sub menu items:

Register
Register for Current User
Unregister

Register for Current User will do just as it says. Handy if your IDE is installed on a UAC system or you need to test in an environment that will be running under UAC. The entire COM registration system has been adapted to account for things registered for current users. This means if you register something for current user then go to import it it will show up in the list.

Another handy feature is the above menu items handle local servers. No longer do you have to set the run parameters with /regserver.

tregsvr.exe now has the parameter -s for "Register or unregister for current user".

Friday, April 25, 2008

Delphi put by ref properties

In Delphi 6 and earlier there was a an unintended compiler feature, put by ref properties. It allowed properties to be passed by references. Here is an example:


function GetCount: Integer;
procedure SetCount(var Value: Integer);

property Count: Integer read GetCount write SetCount;

It was decided that the setter property by reference wasn't safe so starting with Delphi 7 this became a compiler error. But this is a requirement for COM so the compiler option {$VARPROPSETTER ON} was introduced to enable the put by ref behavior. At the top of each _TLB.pas file generated you will see this compiler directive.

Thursday, January 17, 2008

Using the Delphi Type Library Importer to Import Microsoft XML

Over the years I've gotten many questions about importing Microsoft XML with the type library importer and why the resulting *_TLB.pas file doesn't match that of Microsoft's documentation. There are a lot of interfaces with the same problem but lets take a closer look at one of them as an example. You can find the documentation for ISAXDXMLReader.getProperty here. The documentation provides the following C++ code for the getProperty function:


HRESULT getProperty(
const wchar_t* pwchName,
VARIANT* pvarValue
);

Notice the type of pwchName. It's type is const wchar_t* which should be const WideString but as you can see after importing the type library you get:

function getProperty(var pwchName: Word;
out pvarValue: OleVariant): HResult; stdcall;

Obviously the codegen is wrong but why is that. If you take a look at the .tlb in Ole View or the Type Library Editor you'll notice the following IDL:

HRESULT _stdcall getProperty(
[in] unsigned short* pwchName,
[out, retval] VARIANT* pvarValue);

Notice the unsigned short*. The type library importer generated it as a var Word. This means the type library is doing exactly what it should be doing.

So either the documentation is incorrect or the .tlb is incorrect.

Tuesday, August 28, 2007

COM is Hard

I get a lot of COM questions. I mean a LOT of COM questions. I eventually answer most of them, but some are easy and some are more difficult. That's one reason I started this blog, to centralize as much knowledge as possible. Once I put post about it I don't have to remember it, it's somewhere on my blog. I haven't had time to post half the things I want to. Eventually I'll get to it all.

COM usually overwhelms people. When someone asks me a questions and I say "oh, that's easy" they respond with "well you know COM". So it got me thinking, why is COM hard? Or I guess a better question is why do people think COM is hard? COM is just a bunch of simple concepts all piled on top of one another. I think most people add ActiveX, MTS, COM+ and whatever other new technology Microsoft added to "COM" by defining some new interfaces and a registration system.

COM is nothing more than a registration system, reference counting memory management and interfaces (classes with only pure virtual methods for C++). Really pretty simple. So why is it hard? My conclutions is the main reason COM is hard is because the registration system sucks! I mean it sucks!

The COM registration system sucks for two reasons. The first and primary reason is GUIDs are nasty to look at and the registry is littered with them making it impossible to clean or know what you are looking at. The second is the initialzation for the COM subsystem, CoInitialize, CoInitializeEx, OleInitialize, CoUninitialize and OleUnInitialize, are horribly complex even though they are very simple functions. These two problems make COM hard. Not just hard, but a monumental task for people who haven't made that epiphany to understanding and spent a lot of time to understand. These are the people who have to code for a living or hobby.

I thought Side-By-Side would solve the first half to the registry problem. Turns out it made the problem even worse because Side-By-Side is even more complex and hardly works. I plea Microsoft to make Side-By-Side easy. All I need is a .ini file that defines all the GUIDs and the .dll or .ocx containing the type library or Assembly. Just one .ini file per applications please. I don't want each .dll or .ocx to have their own .ini file. Just one .ini file for the entire application.

Thursday, August 23, 2007

Problems Compiling Old COM Projects with C++Builder 2007

There have been some reports of problems when opening old COM projects with C++Builder 2007. The error comes from the linker that the .tlb can't be found. A simple work around, and one that we should have used all along, is to add the line anywhere in your project:


#pragma resource "*.tlb"

I hope this work around helps anyone having the problem.

Tuesday, July 31, 2007

.NET Interop: Calling Managed from Unmanaged

I ran into another issue today when dealing with .NET Interop. This particular case had to do with invoking an unmanaged export. The exception happened on the .NET side but we never got to the exported method. To make a long story short the problem was the floating point control word wasn't set to what .NET expects. I have found this wasn't much of a problem with .NET 1.0 or 1.1 so something has obviously changed in .NET 2.0.

The moral of the story is to wrap every single one of your interop calls with this pattern:


var
SavedCW: Word;
begin
SavedCW := Get8087CW;
try
Set8087CW($027f);
// ToDo: Insert your interop code here.
finally
Set8087CW(SavedCW);
end;
end.

Monday, July 16, 2007

Default IDE Options when Importing a Type Library

Last week I posted How to Import a Type Library from the Command Line. Today I'm posting which of these options are the defaults for the IDE. This is useful if you want to reproduce what the IDE is doing from the command line and since the defaults are not exactly identical I thought it would be useful to have a list published.


  • -C- C++ Import file (the IDE will control this option)
  • -P- PASCAL Import file (the IDE will control this option)
  • -Ha+ Create IDE component for Controls
  • -Hs+ Create IDE component for Servers
  • -Hr+ Generate component registration
  • -O+ Generate CoClassCreator wrappers
  • -R+ Process dependent type libraries
  • -W+ Emit warnings in files
  • -Wc+ Emit comments in filesx
  • -Yc+ [CanCreate]
  • -Cd+ Generate dispinterfaces
  • -Cu+ Expose namespace with 'using'
  • -Ps+ Map dual HRESULT to safecall
  • -Cw+ BCB-style event server events
  • -Ct+ Force the use of a _TLB file


So there you have it, the defaults the IDE uses for the first time ever documented.

Wednesday, July 11, 2007

How to Import a Type Library from the Command Line

I am kind of biased but I think one of the most useful utilities in the Delphi or C++Builder bin directory is tlibimp.exe. In case you don't know what tlibimp.exe does, it allows you to use COM objects as if they are native interfaces, and .NET objects can be treated as COM objects, so there is a lot of potential here. This is the command line version of the Import Component Wizard, but who wants to use the IDE when we've got command line!

One of the most common problems users of Delphi run into only a few options are available from the IDE. Here is a list of all the options provided by tlibimp:

Borland TLIBIMP Version 11.0
Copyright (c) 1997-2005 Borland Software Corporation
Syntax: TLIBIMP [options]

Files to Generate (Required):
-C- C++ Import file
-P- PASCAL Import file
-Ic- CORBA IDL file
-Ip- PASCAL CORBA stubs & skeletons

Customize code generated:
-Ha+ Create IDE component for Controls
-Hs+ Create IDE component for Servers
-Hr+ Generate component registration
C++ options:
-Cd- Generate dispinterfaces
-Cm- Map disp interfaces to dual
-Cn Set namespace name
-Cs+ declspec(__selectany) for GUIDs
-Ct+ Force the use of a _TLB file
-Cu+ Expose namespace with 'using'
-Cv+ BCB4-style server events
-Cw- Use disp. in Control wrappers

Ignore Flags: -Ya- All special flags,
-Yh- [Hidden]

Debug Build Options:
-G+ Show Debug output
-Gr Read & write out type library
-It IDL file parser test

Output File Options:
-D Output directory path
-Fe TLB suffix (-Fe- none)
-Ft TLB filename (no suffix)
-Ce OCX suffix (-Ce- none)
-Co OCX filename (no suffix)
-Hpa Set palette name
-Hps Set palette name

PASCAL options:
-Ps+ Map dual HRESULT to safecall
-Pt- Map all HRESULT to safecall

MISC options:
-O+ Generate CoClassCreator wrappers
-R+ Process dependent type libraries
-XM- Use MS-style getter/setter
-W+ Emit warnings in files
-Wc+ Emit comments in files
-Yc+ [CanCreate],
-Yp- [Predefined], -Yr- [Restricted]

-Pu Generate Pascal UI file
-Xd+ Generate DispInterfaces
-Yi+ Ignore Standard Type Libraries

That's a lot of options. Some options are obsolete such as the CORBA options but we leave them in case they are being used.

Internally Delphi uses a lot of code generated by tlibimp to communicate to the .NET services such as MSBuild, the .NET designers, and a lot of internal services. The command line we use is:

tlibimp.exe -P+ -Ha- -Hr- -Hs- -R- -D .tlb


  • -P+ to generate Pascal
  • -Ha- to not generate IDE components for Controls
  • -Hr- to not generate component registration
  • -Hs- to not generate IDE components for Servers
  • -R- to not process dependent type libraries
  • -D to generate the .pas files to the specified directory
  • this is the type library to import

Note that the -Yc option has changed from off to on in Delphi and C++Builder 2007. For a little history CanCreate flag is typically used in Visual Basic libraries and defaults to off. This means that VB objects aren't meant to be created. But after years of bug reports I decided to change the default of this option to on so now we ignore the flag.

Tuesday, June 26, 2007

Delphi Tips And Tricks: CoInitialize/CoUninitialize Part III

Previously I talked about using CoInitialize and CoUninitialize in very simple situations, a single threaded console application. If you havn't read those posts you can read Part I here, Part II here and the adendum to Part II here. So for this post I'm going to demonstrate the use of CoInitializeEx and CoUninitialize in a multithreaded environment.

This demo consists of two files, two instances of a thread and a couple loops to give your system enough time to actually allow some processing to happen. Below find the code for this demo:


program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils,
ActiveX,
MyThread in 'MyThread.pas';

var
Thread1, Thread2: TMyThread;
Success: HResult;
begin
Success := CoInitializeEx(nil, COINIT_APARTMENTTHREADED);

try
Thread1 := TMyThread.Create(False, 'thread1');
Thread2 := TMyThread.Create(False, 'thread2');

try
// Note my implementation could have a
// problem if DoStuff completes too quicly.

DoStuff('main');
Thread1.Wait;
Thread2.Wait;
finally
Thread1.Free;
Thread2.Free;
end;
finally
CoUninit(Success, 'main');
end;
end.




unit MyThread;

interface

uses
Classes, SyncObjs;

type
TMyThread = class(TThread)
private
FLock: TCriticalSection;
FText: string;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; const Text: string);
destructor Destroy; override;
procedure Wait;
end;

procedure CoInitStatus(Success: HResult);
procedure CoUninit(Success: HResult; const Text: string);
procedure DoStuff(const Text: string);

implementation

uses
SysUtils, ActiveX, Windows;

procedure CoInitStatus(Success: HResult);
begin
case Success of
S_OK, S_FALSE: WriteLn('CoInitialize success');
RPC_E_CHANGED_MODE, E_INVALIDARG,
E_OUTOFMEMORY, E_UNEXPECTED: WriteLn('CoInitialize failed');
end;
end;

procedure CoUninit(Success: HResult; const Text: string);
begin
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;

procedure DoStuff(const Text: string);
var
Index: Integer;
begin
for Index := 0 to 10000 do
WriteLn(Text + ' ' + IntToStr(Index));
end;

{ TMyThread }

constructor TMyThread.Create(CreateSuspended: Boolean; const Text: string);
begin
FLock := TCriticalSection.Create;
inherited Create(CreateSuspended);
FText := Text;
UniqueString(FText);
end;

destructor TMyThread.Destroy;
begin
FLock.Free;
inherited;
end;

procedure TMyThread.Execute;
var
Success: HResult;
begin
FLock.Acquire;
Success := CoInitializeEx(nil, COINIT_MULTITHREADED);

try
DoStuff(FText);
finally
CoUninit(Success, FText);
FLock.Release;
end;
end;

procedure TMyThread.Wait;
begin
FLock.Acquire;
FLock.Release;
end;

end.


I want to bring your attention to two things. First, notice my use of the return value from CoInitializeEx. And second, the first thread to call CoInitializeEx (located in the main function) is also the last one to call CoUninitialize by use of the the function TMyThread.Wait.

I would talk more about it but I think the code explains everything. So read the source and if you have questions ask.

Update: I have moved the critical section to occur before the constructor to TThread to avoid a race condition and called UniqueString on the string passed to the thread as pointed out by Anders Melander.

Thursday, June 21, 2007

Delphi Tips And Tricks: CoInitialize/CoUninitialize Part IIb

I forgot to mention in Part II that a very simple call stack containing code like this:


CoInitializeEx(nil, COINIT_APARTMENTTHREADED)
CoInitializeEx(nil, COINIT_MULTITHREADED)
CoUninitialize;
CoUninitialize;


will cause one too many calls to CoUninitialize. The second call to CoInitializeEx will return RPC_E_CHANGED_MODE and does not require another call to CoUninitialize because the internal reference counting of CoInitialize didn't increment. The example I provided in Part II demonstrated this but I neglected to mention it specifically.

I just wanted to mention this before I forgot. Next post about CoInitialize will be as requested a multithreaded example.

Wednesday, June 20, 2007

Delphi Tips And Tricks: CoInitialize/CoUninitialize Part II

In Part I of CoInitialize/Uninitialize of Delphi Tips and Tricks I talked about encountering problems with CoUninitialize and how the Delphi compiler generates code to IUnknown._Release after a function block. In this part I want to discuss another common problem having to do with making multiple calls to CoInitialize and CoUninitialize.

Here are the rules to calling CoIninitialize and CoUninitialize:


  • Each thread must initialize the COM library by calling CoInitialize
  • Each call to CoInitialize with a return value of S_OK and S_FALSE must be paired with a call to CoUninitialize
  • Each thread can only be initialized with one threading model (remember GUI code is appartment threaded)
  • The first thread that calls CoInitialize must be the last thread to call CoUninitialize.
  • Do not call CoInitialize in DllMain


If you do not follow these five basic rules your application will not function properly. You can find these the documentation with these five basic rules on MSDN here. There is more information but the simple list above is much easier to read.

Below is a simple example that demonstrates correct usage and handling of the return value from CoInitialize and CoInitializeEx in a single threaded application. If there is any interested I will expand this to a multithreaded application?


program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils, ActiveX, Windows;

procedure CoInitStatus(Success: HResult);
begin
case Success of
S_OK, S_FALSE: WriteLn('success');
RPC_E_CHANGED_MODE, E_INVALIDARG,
E_OUTOFMEMORY, E_UNEXPECTED: WriteLn('failed');
end;
end;

procedure CoUninit(Success: HResult);
begin
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;

var
Success1, Success2, Success3: HResult;
begin
Success1 := CoInitialize(nil);
CoInitStatus(Success1);

Success2 := CoInitialize(nil);
CoInitStatus(Success2);

Success3 := CoInitializeEx(nil, COINIT_MULTITHREADED);
CoInitStatus(Success3);

CoUninit(Success3);
CoUninit(Success2);
CoUninit(Success1);
end.


The output from running this program should be:

success
success
failed

Wednesday, June 13, 2007

Customer Feedback About Delphi COM Features by Blog Readers

I wanted to thank everyone for all their feedback two weeks ago when I asked for feedback on the Delphi COM features. I received a lot of useful input, some of which I knew about and some that I did not, but I will take all of the input and incorporate as much as I can into our future plans. It really helps to get prioritization on what bugs to fix and features to do. So thank you again!

Friday, June 1, 2007

Customer Feedback About Delphi COM Features

I'm going to take a stab in the dark here and follow David Lock's lead and solicit feedback from all of you! I am responsible for quite a few more areas of Delphi and C++Builder than David since I've been with the company for over 12 years, but what I'm specifically looking for feedback about is our COM/ActiveX features. COM isn't going away anytime soon, in fact it is my belief that more of you are looking at COM when you are moving your applications to .NET. I want to know what your pain points are, what features you'd like to see, and what bugs are blocking you. Please respond to this post or email me at codegearalias@gmail.com with the word "blog" anywhere in the message (I've taken extra precautions as to avoid spam unlike David).

Wednesday, May 16, 2007

Example To Get Referenced Type Libraries

Here is a function that given a type library it will populate a string list with all the referenced type libraries.


procedure GetTypeLibraryReferences(const FileName: string;
References: TStrings);

function IndirectionTypeDesc(TypeDesc: PTypeDesc): PTypeDesc;
begin
Result := TypeDesc;

while Result <> nil do
begin
// Follow pointer type to pointed to type.
if Result.vt <> VT_PTR then
Break;

Result := Result.ptdesc;
end;
end;

procedure GetTypeLibName(const TypeInfo: ITypeInfo; RefType:
HRefType; References: TStrings);
var
CustomTypeInfo: ITypeInfo;
CustomTypeLib: ITypeLib;
Index: Integer;
Guid: TGuid;
CustomLibAttr: PTLibAttr;
TypeLibName: WideString;
begin
OleCheck(TypeInfo.GetRefTypeInfo(RefType, CustomTypeInfo));
OleCheck(CustomTypeInfo.GetContainingTypeLib(CustomTypeLib, Index));

// Get the name and guid of the type library that contains this type.
OleCheck(CustomTypeLib.GetLibAttr(CustomLibAttr));

try
OleCheck(CustomTypeLib.GetDocumentation(MEMBERID_NIL,
@TypeLibName, nil, nil, nil));
References.Add(Format('%s - %s',
[TypeLibName, GuidToString(CustomLibAttr.guid)]));
finally
CustomTypeLib.ReleaseTLibAttr(CustomLibAttr);
end;
end;

var
TypeLib: ITypeLib;
TypeInfoIndex: Integer;
Kind: TTypeKind;
TypeInfo: ITypeInfo;
TypeAttr: PTypeAttr;
FuncDescIndex: Integer;
FuncDesc: PFuncDesc;
ParamsIndex: Integer;
ElemDesc: PElemDesc;
TypeDesc: PTypeDesc;
ImplTypesIndex: Integer;
RefType: HRefType;
begin
OleCheck(LoadTypeLibEx(PWideChar(WideString(FileName)), REGKIND_NONE, TypeLib));

// Get the CoClasses, interfaces, enums, modules dispinterfaces,
// alias', unions and records.

for TypeInfoIndex := 0 to TypeLib.GetTypeInfoCount - 1 do
begin
TypeLib.GetTypeInfoType(TypeInfoIndex, Kind);
TypeLib.GetTypeInfo(TypeInfoIndex, TypeInfo);
OleCheck(TypeInfo.GetTypeAttr(TypeAttr));

try
case Kind of
TKIND_COCLASS:
begin
// Get the implemented interfaces.
for ImplTypesIndex := 0 to TypeAttr.cImplTypes - 1 do
begin
OleCheck(TypeInfo.GetRefTypeOfImplType(ImplTypesIndex,
RefType));
GetTypeLibName(TypeInfo, RefType, References);
end;
end;

TKIND_INTERFACE, TKIND_DISPATCH:
begin
for FuncDescIndex := 0 to TypeAttr.cFuncs - 1 do
begin
OleCheck(TypeInfo.GetFuncDesc(FuncDescIndex, FuncDesc));

try
// Item 0 is the method name. Ignore it
// for the parameter list.

for ParamsIndex := 1 to FuncDesc.cParams do
begin
ElemDesc := @FuncDesc.lprgelemdescParam[ParamsIndex - 1];
TypeDesc := IndirectionTypeDesc(@ElemDesc.tdesc);

if TypeDesc.vt = VT_USERDEFINED then
begin
RefType := TypeDesc.hreftype;
GetTypeLibName(TypeInfo, RefType, References);
end;
end;

finally
TypeInfo.ReleaseFuncDesc(FuncDesc);
end;
end;
end;
end;

finally
TypeInfo.ReleaseTypeAttr(TypeAttr);
end;
end;
end;

Friday, May 11, 2007

How To View And Edit The Registry Under Windows 64-bit

I've been running Windows XP 64-bit for nearly a year and there is one gotcha that I run into when debugging COM applications, dealing with the registry. In 64-bit Windows the entire 32-bit registry is located under the key HKEY_LOCAL_MACHINE\Software\WOW6432Node. 32-bit Delphi applications can only see the 32-bit keys, but a 64-bit application can see everything. I use a 32-bit 4NT which launches the 32-bit regedit so if I want the 64-bit regedit I just go to the start menu, click Run and type "regedit". You can also run the 32-bit regedit from the start menu by going to the start menu, clicking Run and typing "%systemroot%\syswow64\regedit". You can only run one at a time unless you use the -m switch. This can get confusing fast because Windows On Windows 64 (WOW64) mirrors certain registry keys and values between the 64-bit and 32-bit registry. So if you are on Windows 64-bit, open up regedit and do some twiddling and then try to run your COM app and things don't work as you expect, make sure you are twiddling with the correct registry.

Thursday, April 19, 2007

Changes To COM In Delphi 2007

There are two major but small changes to the COM features in Delphi 2007:

1. The tlibimp default for ignore the CanCreate option has changed to on, -Yc+. The reason for the change is since introducing the option and not generating code for CoClasses that were marked to not be created we noticed that a lot of type libraries out there are not using that type library flag correctly.

2. COM Servers (this includes applications with a Remote Data Module) do not auto register themselves when run the first time. You must pass the /regserver paramter once in order to register it. The reason for the change is it should never have been auto registering in the first place, and because of UAC on Vista we had to change it.

Thursday, March 22, 2007

Building COM Automation Servers with Delphi 2007

Sometime around Delphi 6 and C++Builder 5 there was a change made to Automation Servers (components written as an EXE) where they would automatically register themselves. This caused all sorts of problems for Vista UAC and just wasn't the correct thing to be doing. So for Delphi 2007 the code for Automation Servers to automatically register themselves has been removed. You are now required to run your Automation Server with the /regserver option. Example: "MyAutoServer.exe /regserver".

Friday, February 23, 2007

COM Registration Under Microsoft Windows Vista

Steve Trefethen and I have both been working on Windows Vista and I must say there is not one redeemable feature of Windows Vista over Windows XP. I keep hearing how it looks like Mac OSX, but the only resemblance I've been able to find is when minimizing apps they slide into the taskbar.

Enough of my complaining, I had a real issue to discuss in this post, COM Registration. Chances are you will need to use COM objects when you start developing on Windows Vista and there is a real problem that UAC has created. You cannot register a COM object unless you are running as Administrator. I don't mean you have Administrator privileges, I mean the process is running under the Administrator account (root for you Linux heads). Things you took for granted in previous Windows versions like running regedit.exe from a command prompt like I normally do fails with "Access denied". You must run it from the Start Search menu item (similar to the old Run menu item), then grant access for the process to run. Most likely you will have to create a shortcut to 4NT or CMD and right click on it and choose "Run as administrator".

So now let's look at registering your COM object. Take midas.dll as an example. It was written a lone time ago, before UAC. If in a non Administrator privileged command prompt you type "tregsvr midas.dll" you get "Call to DllRegisterServer was successful!", but it actually wasn't. So what happened? tregsvr doesn't have privileges to touch the registry so the DllRegisterServer in midas.dll failed. The implementation of DllRegisterServer in midas.dll could do potential harm to your system so Windows doesn't allow it access to the registry calls and there are sufficient checking down on those calls for DLLRegisterServer to return an error to tregsvr so the tregsvr reports a success. This can happen with any COM DLL so you can't rely on a call to tregsvr (or regsvr32) like in the good old days. Pre UAC COM objects need to be tested and verified and always registered in from an Administrator privileged account. This means that features in Delphi you've grown to know and love like Run | Register ActiveX Server will fail under Vista because of UAC.

Wednesday, February 14, 2007

Side-by-Side COM Registration Part II

I've been meaning to expand on Part I of Side-by-Side COM Registration for some time but other things have gotten in the way. The major road block is getting Side-by-Side working 100%. I can get it working in the simple case, even the complex case, but not the super complex case. In other words, the Delph IDE. Until then I don't want to increase the amount of misinformation out there on the subject, and believe me, there really is a lot. Of misinformation that is. One valuable piece of information has been recently published on MSDN Manifest Files Reference. Believe it or not, the information up on MSDN until this article went up was wrong. Thus far I'm glad to say I haven't posted any misinformation, just not enough to actually get Side-by-Side working in a complex case.

Tuesday, February 13, 2007

Using LoadTypeLib and LoadTypeLibEx

When most people use COM they stumble through it and if it works they just go with it. If you don't stumble with COM you probably can ignore this post but somehow I'd be willing to bet you'll read on. There are little idiosyncrasies in COM, ActiveX or COM Interop that aren't documented, so here's a little piece of information about type libraries. To load a type library into memory and get a pointer to the ITypeLib interface use the function LoadTypeLib.


HRESULT LoadTypeLib(const OLECHAR FAR *szFile, ITypeLib FAR* FAR *pptlib);


LoadTypeLib is the foundation of COM. Any call to LoadTypeLib will register the type library if it is not already registered. That's why Microsoft invented this function:


HRESULT LoadTypeLibEx(LPCOLESTR szFile, REGKIND regkind, ITypeLib** pptlib);


Any function is better with an "Ex" on the end. If you just want to go spelunking around in a type library and don't want it registered then use LoadTypeLibEx and pass in REGKIND_NONE as the regKind parameter. Problem is if your type library references other type libraries and they are in the current directory and they are not registered then they will be after this call, but the type library you passed in as the szFile parameter won't get registered.