A site devoted to discussing techniques that promote quality and ethical practices in software development.

Tuesday, October 23, 2007

Delphi in-proc server registration/unregistration code has incomplete coverage

I have just discovered the DllRegisterServer() and DllUnregisterServer() code located in Delphi's ComServ.pas file for the ComServ unit lacks complete coverage of COM usage. It is not entirely a bug in a sense. It only means that it does not cater for all situations permitted by their language framework and supported by their IDE and COM.

However, if you are in that situation, you will not be shown any visible sign other than to discover the interfaces you are publishing are not registered. OleView.exe can show you the lack of result.

Description of the problem

When you create an ActiveX project in D2006, the IDE basically generates a plain old DLL and in Delphi's parlance, a library. What it does is to export the 4 required COM In-Proc server functions, DllRegisterServer(), DllUnreqisterServer(), DllCanUnloadNow and DllGetClassObject(). The implementations of these functions are found in ComServ.pas file.

Now if you then include a type library, you can begin to define interfaces in this library. This DLL, while devoid of any implementation, is of great significance to a COM-base solution as other in-proc or local servers can implement interfaces published in this registered type library. There is no common tool, definitely not from Microsoft, to register type library (tlb) and hence it is customarily to embed this interface only type library in an in-proc server that can be registered with DllRegisterServer() and unregistered with DllUnregisterServer().

When you do this, the D2006 produced interface only COM in-proc server will not register the type library and its interfaces as well performing the unregistration process.

RegSvr32, the Microsoft standard in-proc COM registration program, dutifully reports the information reported by DllRegisterServer() and DllUnregisterServer() supplied by CodeGear's code.

Where is the problem

It has been identified that this is caused by a crack in the design and implementation code in ComServ.pas. The implementation is based on a very narrow usage scenario, perhaps in quest of efficiency.

CodeGear assumes an in-proc server always has implementation code, known as coclass, that implements interfaces described in the type library. However, this scenario is not enforced in the IDE. You can describe as many interfaces as you like in the type library without one single coclass and the IDE nor compiler complaints.

In the CodeGear narrow usage scenario, the code in ComServ.pas expects the IClassFactory implementation in the coclass, found in the unit's initialization section generally in the form of the TAutoObjectFactory.Create(), responsible for loading the type library. This then has the flow-on effect of setting ComServer.FTypeLib in ComServ.pas.

Since the unit initialization sections are executed prior to any user code, by the time TComServer.UpdateRegistry() is called, TComServer.FTypeLib is not nil and the type library registration (unregistration) function will then be called.

However, in an ActiveX, whose sole existence is to publish interfaces, the above scenario is not realized and hence by the time TComServer.UpdateRegistry() called from DllRegisterServer() or DllUnregisterServer(), the TComServer.FTypeLib remains nil.

This situation is not considered as a bug in the UpdateRegistry() and dutifully returns S_OK resulting in fooling the user.

Incidentally, code review of Delphi 3's source code shows the same incompleteness and thus expecting the same malfunction.

Work arounds

The work arounds are listed from the most preferred method to the least.
Correct the code and embedded ComServ.pas in your project
The best way is to take a copy of ComServ.pas from CodeGear's source directory and include that into your project. It is worth removing the declaration of using ComServ in your uses statement in the DPK prior to adding the customised ComServ.pas. Failure to include this file will not bring in the fixed code.

You only need to fix the DllRegisterServer() and DllUnregisterServer() as follows:
function DllRegisterServer: HResult;
begin
Result := S_OK;
try
ComServer.GetTypeLib; // **** Added
ComServer.UpdateRegistry(True);
except
Result := E_FAIL;
end;
end;

function DllUnregisterServer: HResult;
begin
Result := S_OK;
try
ComServer.GetTypeLib; // **** Added
ComServer.UpdateRegistry(False);
except
Result := E_FAIL;
end;
end;
You only have to ensure that the type library is loaded prior to the calling of TComServer.UpdateRegistry() and hence simple addition as marked above is sufficient to rectify this problem. It only introduces slight inefficient if the CodeGear anticipated scenario is realized. As a word of optimisation, one could move the call of TComServer.GetTypeLib into the TComServer.UpdateRegistry(). But these functions are hardly frequently called functions, such operation is not really warranted.
Add a dummy coclass into project
The next best solution for those not wanting to tamper with CodeGear's code is to create a dummy coclass in a unit. This unit will then include the TAutoObjectFactory.Create call in the initialization to support the scenario expected by CodeGear. At this moment, I have not explore whether or not this coclass can be made as ole non-createable to prevent code from outside this DLL from creating it via COM API, such as CoCreateInstance().

The presence of this coclass can confuse users as that coclass will show up in tools like OleView and you then need to document its reason for existence.

This represents a compromise to a clean design.
Only good for development - use the Component Install facility
This is not really a solution as such but rather a desperate move to get them registered so that you can begin to develop with those interfaces.

This technique requires one to use the "Component | Install Component ... |Import a type library" facility available in the IDE to register the type library. Since this technique does not call DllRegisterServer() and hence it can register the type library.

However, in a deployment situation, installer relies on the invocation of DllRegisterServer() and hence this technique offers no solution in deployment scenario. Furthermore, if DllUnregisterServer() fails to unregister the type library and the interfaces, this technique does not have its complementary operation.

Tuesday, October 16, 2007

Post Delphi Studio 2006 installation experience

With the replacement of my old trusty work horse machine, I have to go through the ritual of reinstalling all the software packages that I use on daily basis.

One of them is the Delphi Studio 2006. With the new machine that is relatively clean in terms of LUA conformance it is also a good opportunity to see how well D2006 behaves in LUA. On my previous machine, it seemed fine but I might have been less stringent with the conformance.

As required, D2006 was installed in an Administrator account. This is an interesting fact to remember. It is not in an Administrator console. I actually logged into the Administrator's account to install. The installation went pretty flawlessly.

After that I logged back into my normal account, which is an LUA, things got interesting. With ProcMon and ProcExp configured to monitor BDS.EXE, I was ready to fire up Delphi Studio and here are the problems encountered:
1) During the start up, bds tried to copy "<.Net 1.1 Framework SDK Dir>\bin\lc.exe to the "C:\Program Files\Borland\BDS\4.0\bin" as lc.dll. Of course this is futile as I am only a LUA.

I am just wondering why Borland would want to change Microsoft's License Compiler into a License DLL. Doesn't Borland know that it can late bind into LC.EXE just as easily as LC.DLL by virtue of the CLR assembly probing algorithm coupled with reflection? All you need to do is to specify the type name correctly. But perhaps Borland did an early binding in their lab. Not very elegant in my mind. Is it also ethical to rename a Microsoft executable?

Anyway, realizing BDS is trying to do the impossible, I gave it a helping hand so as to go past this hurdle.

2) Next that showed up on the radar screen was that BDS trying to change the value of this registration item:
HKCR\TypeLib\{F939BACD-3FD5-437A-833F-BA3535A45966}\a.0\0\Win32\(Default)

Of cource this is another futile exercise. I am not allow to write to HKCR! But this issue did not seem to both BDS.

3) Next, with the IDE fully up, I began to test it. It could easily debug a VCL.Win32 HelloWorld application. But a different story when I tried to debug a VCL.Net application. As soon as I pressed the Run menu, it popped up an assertion failure dialog box (could Borland been shipping debug version of their C++ packages?)

Like this:

My machine, prior to installing D2006, has every version of .Net Frameworks installed, include 3.5 as well as all version of Visual Studio. .Net Framework are by design to support side-by-side installation and hence should be able to live in harmony. Pressing OK brought BDS crashing down instantly.

A search of Google brought me into contact with a report of this problem in Borland's Developers Network's Quality Central. You can find my rather less drastic and more .Net correct solution to this problem posted as a reply to the brutal solution offered on the Quality Central.

After the simple addition of a .Net Config file to rmtdbg100.exe, the IDE can then debug .Net application.

Incidentally, if you are building .Net COM component to be used in say Excel, make sure you give Excel an application config file to set the supported run time, otherwise, your component may fail to load because one that loads before you could change the CLR version to become incompatible with yours.

4) These operations did not cause any problem but were observed in the tools. BDS seemed to demand Generic Read/Generic Write whenever it was opening .Net system dcpil, such as System.Xml.Dcpil, etc. When the access was denied for obvious reasons, it then tried to open with Generic Read.

These operations look very strange and highly inefficient. It should demand the lease privilege particularly in a non-installation situation. Very strange. Not only BDS opening DCPIL files like this but BDS did this with other DLLs as well.

5) BDS had trouble executing in an Adminstrative Console. This is one that is constructed from RunAs command with an embedded /Netonly to provide network connectivity. It fired up fine by using the "Run As" from its short cut and from a cmd.

So far, the reason for this misbeviour has eluded me. Since this is the only program that fail to run in my Administrator console, it is not a big deal. One day, the penny will drop and so stay tune.

The most bizarre installation I have ever experienced

I have been using and beta testing Visual Studio since it was 1.x and with the release of VS2008 VSTS Beta 2 some months ago, I am obviously eager to try to install it on my machines.

I must say, this has to be one of the hardest and toughest VS beta installation that I have dealt with so far. As you will see that I have to engage one of the weirdest and most bizarre techniques to finally crack the installation.

I must admit I had lots of very alpha .Net framework 3 stuff installed on my previous machine. Hence, I would expect the VS2008 Beta 2 to fail to install. Even after I'd cleaned up all those crumbs, it still failed. But I was shocked when this happened to me on a new machine with only released version of .Net Framework on it.

Anyway, when I performed the normal VS2008 Beta 2 set up on a machine with VS2003 and VS2005 VSTS, the process proceeded normally but hung up on the installation of .Net Framework 3.5. The presence of those released products should not matter.

After several attempts and probing the process, I decided to try to install just the .Net Framework 3.5 alone. This installation proceeded to about 80% and then hung.

Searching the net looking for similar symptom, I came across this one that described installation problem behind an authenticated proxy, a situation similar to mine. Armed with information from Aaron's blog, I ventured into the installation log to try to discover what's happening.

The installation appeared to be hung on trying to access a web site as reported in the last 3 lines of the file dd_dotnetfx35Install.txt and they are reproduced here:
[10/15/07,16:41:18] Setup.exe: GetGlobalCustomProperty - Property:   {8297A38B-6431-4F1D-9F6E-C3D371CEA383} - PropertyName: WebSetup - Value: 1
[10/15/07,16:41:18] VS Scenario: Checking if new setup is available. Url=
[10/15/07,16:41:19] VS Scenario: http://go.microsoft.com/fwlink/?LinkId=91778&clcid=0x409
Armed with the weird suggestion of unplugging the Ethernet cable from the wall, cleaning up all the crumbs (those folders in C:\ with a GUID as a name) and rebooting the machine to ensure no likely installation was still running, I decided to give this a try.

First I tried the .Net Framework 3.5 installation and this flied past the furthest part I had ventured. So my confidence was boosted.

Then I decided to cancel that installation to proceed with the real VS2008 Beta 2 set up. That also went flawlessly to a no-error completion!!

What a bizarre experience. Don't know why an installation program wanted to access a site. Furthermore, I could access that site from IE and that caused me to initially doubt that could be the reason why the installation was hung. But that seemed to be the stumbling block.

Unusual and Bizarre.

Monday, October 8, 2007

Don't dispair if your e-mail does not bring about the desirable result

Recently an article from NYTimes caught my attention reporting that e-mail is often being mis-read by the recipient. This helps me to understand some of the issues I have with e-mail, particularly non-social ones.

The author discovers recent research points out the reason why e-mail is often mis-read is:
e-mail can be emotionally impoverished when it comes to nonverbal messages that add nuance and valence to our words. The typed words are denuded of the rich emotional context we convey in person or over the phone.
[...]
Still, if we rely solely on e-mail at work, the absence of a channel for the brain’s emotional circuitry carries risks. In an article to be published next year in the Academy of Management Review, Kristin Byron, an assistant professor of management at Syracuse University’s Whitman School of Management, finds that e-mail generally increases the likelihood of conflict and miscommunication.

One reason for this is that we tend to misinterpret positive e-mail messages as more neutral, and neutral ones as more negative, than the sender intended. Even jokes are rated as less funny by recipients than by senders.
[...]
On the upside, the familiarity that develops between sender and receiver can help to reduce these problems, according to findings by Joseph Walther, a professor of communication and telecommunication at Michigan State University. People who know each other well, it turns out, are less likely to have these misunderstandings online.
One way to overcome this as proposed by Professor Shirky, an adjunct professor in New York University’s interactive telecommunications program is:
a “banyan model,” after the Asian tree that puts down roots from its branches.

In this approach, he said, “you put down little roots of face-to-face contact everywhere, to strategically augment electronic communications.”

A final note from Professor Shirky:
“social software” like e-mail “is not better than face-to-face contact; it’s only better than nothing.”
So pick up the phone or walk around the cubicle.

Blog Archive