Saturday, August 29, 2009

Exceptional Procrastination

I kept putting this post off… ok, ok… that was a really bad pun…

Seems there was a little bit of concern about my last post regarding the new ‘delayed’ directive. Christian Wimmer had a few critiques regarding the implementation and how the exception that was raised was an EExternalException with an obscure exception code. There is a simple explanation for this. The delay load helper functions were taken directly from the C++Builder RTL. By that I mean, the delayhlp.c file gets built to a .obj file by the C++ compiler and then directly linked into the Delphi System unit. There were several key reasons for this. The first of which was the code was already written, has been in many versions of C++Builder RTL (including back in the old Borland C++ days) and has been extensively tested. Another reason is that in order for Delphi and C++Builder to “play-nice,” when a Delphi unit is linked in C++Builder that contains a reference to a delay load import, ILink32 takes over the duties of generating the proper delay load functionality into the PE file. So the C++RTL version of delayhlp.c is what is used. In order to things to remain consistent, this is the route taken. Had there been two delay-load helper functions in the case of a C++Builder application, then any existing C++ code that used the delay load hooks would only work on other C++ code and vice-versa.

Fear not, all is not lost. To satisfy our good friend, Christian’s request, here is a unit that you can use to generate nice, unique Delphi specific exceptions. This is accomplished by leveraging the ability for the delayhlp.c code to be “hooked.” This code also demonstrates another new Delphi language feature, class constructors and destructors. I will describe them in better detail in a subsequent post. If you never reference any of the declared exception types in this unit, the hook is not installed and the normal EExternalException is raised. Presumably you would use this unit for the purpose of actually catching the exceptions and doing something interesting with them. Another thing to note is that you should also be able to add this unit to a C++Builder application and it work for any delay-load functions done in the existing way for C++ and any Delphi units that reference Delphi imports with the delayed directive.

unit DelayExcept;

interface

uses SysUtils;

type
EDliException = class(Exception)
private
class constructor Create;
class destructor Destroy;
end;

EDliLoadLibraryExeception = class(EDliException)
private
FDllName: string;
public
constructor Create(const ADllName: string); overload;

property DllName: string read FDllName;
end;

EDliGetProcAddressException = class(EDliException)
private
FDllName: string;
FExportName: string;
public
constructor Create(const ADllName, AExportName: string); overload;
constructor Create(const ADllName: string; AOrdinal: LongWord); overload;

property DllName: string read FDllName;
property ExportName: string read FExportName;
end;

implementation

{ EDliLoadLibraryExeception }

constructor EDliLoadLibraryExeception.Create(const ADllName: string);
begin
inherited Create(Format('Unable to load ''%s''', [ADllName]));
FDllName := ADllName;
end;

{ EDliGetProcAddressException }

constructor EDliGetProcAddressException.Create(const ADllName, AExportName: string);
begin
inherited Create(Format('Unable to locate export name ''%s'' in ''%s''', [AExportName, ADllName]));
FDllName := ADllName;
FExportName := AExportName;
end;

constructor EDliGetProcAddressException.Create(const ADllName: string;
AOrdinal: LongWord);
begin
inherited Create(Format('Unable to locate export ordinal ''%d'' in ''%s''', [AOrdinal, ADllName]));
FDllName := ADllName;
FExportName := IntToStr(AOrdinal);
end;

function DelayLoadFailureHook(dliNotify: dliNotification; pdli: PDelayLoadInfo): Pointer; stdcall;
begin
Result := nil;
if dliNotify = dliFailLoadLibrary then
raise EDliLoadLibraryExeception.Create(pdli.szDll);
if dliNotify = dliFailGetProcAddress then
if pdli.dlp.fImportByName then
raise EDliGetProcAddressException.Create(pdli.szDll, pdli.dlp.szProcName)
else
raise EDliGetProcAddressException.Create(pdli.szDll, pdli.dlp.dwOrdinal);
end;

{ EDliException }

class constructor EDliException.Create;
begin
SetDliFailureHook(DelayLoadFailureHook);
end;

class destructor EDliException.Destroy;
begin
SetDliFailureHook(nil);
end;

end.

Tuesday, August 25, 2009

Procrastinators Unite… Eventually!

We’re all taught at an early age to “Never put off until tomorrow that which can be done today.” In general, that is wise advice. However there are some cases where you do want to wait until the last possible moment to do (or not do) something. In fact, that is one of the overall tenets of  Agile Programming; delay decisions until the last possible moment because you always know more about a problem tomorrow than you do today and can make a better, more informed decision. But, I digress. I’m not here to talk about philosophies of life, or to introduce another “agile methodology” or even about a new weight loss plan based on bacon, lard and cheese puffs.

How many of you have written this same bit of boilerplate code or something similar over and over again?


if OSVersion >= 5.0 then
begin
Module = LoadLibrary('kernel32.dll');
if Module <> 0 then
begin
Proc := GetProcAddress(Module, 'APICall');
if Assigned(Proc) then
Proc(Param1, Param2);
end;
end else
{ gracefully fallback }

 


What if you could just do this?


if OSVersion >= 5.0 then
APICall(Param1, Param2);
else
{ gracefully fallback }

 


The astute among you would immediately see that with previous Delphi releases, the second form, while certainly preferable, at some point the call to “APICall” would eventually have to effectively go through some bit of code similar to the first bit of code. Normally, you would declare an external API reference like this:


procedure APICall(Param1, Param2: Integer); stdcall; external 'kernel32.dll' name 'APICall';

That will cause the linker to emit a external reference into the executable binary that is resolved at load time. Therein lies the problem. That is the situation that the first bit of code above was designed to avoid. If APICall didn’t existing in ‘kernel32.dll’ at load time, the whole application would fail to load. Game over. Thanks for playing. Now, what if you could write code similar the second block of code above and still declare your external API calls in a manner similar to above?


Starting with Delphi 2010, you can do exactly the scenario I describe. To make the second code block work even if “APICall” isn’t available on the version of the OS on which your application is currently running, we’ve introduced a new directive to be used only in the above context, delayed;

procedure APICall(Param1, Param2: Integer); stdcall; external 'kernel32.dll' name 'APICall' delayed;


Simply put, by adding the delayed directive, that instructs the linker to generate the external API reference differently in the executable binary. Now Delphi’s RTL will take care of all that ugly “late-binding” boilerplate code for you. Rather than generating the import in the normal “Imports” section of the executable’s PE file, it is generated into the “Delayed Imports” section following the published PE spec. This also requires that the RTL now has a generic function that does the proper lookups and binds the API whenever it is called the first time. Subsequent calls are just as fast as a normal import.


This is different than similar functionality available in ILink32 from C++Builder wherein you can only specify an entire dll in which all references are delay loaded. Also, in C++ you cannot specify kernel32.dll or even ntdll.dll to be delay loaded, since the very startup of any application or dll requires them to already be loaded. In Delphi you can, since it is on an API-by-API basis. The intent of this feature was to make managing all the new APIs from Windows Vista and now Windows 7 much easier without having to continuously and manually write all the delay loaded boilerplate code. Throughout VCL, we can simply make runtime decisions on which APIs to call without always going through those manually coded “thunks.” They are now simply handled by the compiler/linker and the source code barely reveals the fact that something is late-bound.


In a future release, we are considering adding both the API-by-API capability to C++Builder and a way to specify an entire dll to be late-bound in Delphi.

Friday, August 21, 2009

The “Nitty Gritty” details on Casting an Interface Reference in Delphi 2010

Malcolm Groves let loose on a new Delphi compiler feature where you can now cast an interface reference to the implementing Delphi class. Joe White was interested in the “gritty” details about how this was implemented. It turns out that it was deceptively easy once I figured out the internal compiler logic for handling “is” tests and “as” and hard casts. All three cases use the same internal mechanism implemented in the Delphi RTL.

Because all Delphi interfaces have three core methods, QueryInterface, _AddRef, and _Release, you know that you can always call QueryInterface on any interface reference. QueryInterface is declared thusly:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

The last parameter is an “untyped out” parameter meaning that it will accept a reference to any variable of any type. What if you called QueryInterface with a special GUID (remember, GUID = Globally Unique IDentifier) and instead of assigning an interface reference to the Obj parameter, the implementing class instance was assigned instead? The base implementation of QueryInterface simply calls TObject.GetInterface. It is in that method that code will now look for this special GUID (which is declared in the implementation section of System.pas) and assign Obj the object reference instead of a new interface reference.

Granted, this technique does have a couple of potential issues. If, for some reason, the user decides to manually implement QueryInterface and never calls TObject.GetInterface, then none of the above casting and “is” testing will work. It will behave as if the implementing class was not implemented in Delphi. Another caveat, is if you’re doing cross-process marshaling of interfaces where both sides are Delphi 2010, you should not attempt to use this technique. In fact, if you’re doing any kinds of traditional COM programming, using this feature can yield strange results. This feature was a direct result of many requests from customers who are using interfaces for purposes outside COM.

Thursday, August 20, 2009

A Tempest in a Teapot or something more sinister?

If you’re a well connected internet maven who also happens to be a Delphi user, you’re probably no doubt aware of some of the recent reports about a new virus/trojan threat that hits pretty close to home. There are many reports among the mainstream internet press that are discussing the emergence of a virus affectionately known as the “Compile-a-virus” or by the more mundane and sanitary name, W32/Induc-A. It seems that this particular virus is targeting older versions of Delphi (specifically Delphi 4-7) and replicates itself by rebuilding a new version of SysConst.dcu and placing in back in your <root>\Lib folder. Ironically, it doesn’t simply overwrite the existing SysConst.dcu without first backing up the original, called SysConst.bak. It does use the existence of this .bak file as a signal that it’s already done its job and it should just leave that installation alone. Because it doesn’t actually delete anything, it is a gentleman among viruses ;-). Once Delphi is infected, every executable and dll that is built which links in the code in SysConst.dcu will now carry the virus which when run will seek out any Delphi installation and the process repeats.

Aside from the pure mechanics of how this thing works, there are also a lot of response among the community and press from “OMG, the sky is falling” to “nothing to see here, move along.” Fear mongering is just as bad of a response as ignoring the problem. At this point, here at Embarcadero, we’re actively analyzing situation and overall impact to our community. We’re also working on recommendations about how to find out if you’re infected and what to do once you see that you are. Throughout all this we’re working on recommended steps can you take to guard against re-infections. Rest assured that we’re neither ignoring this threat, nor are we going to do anything to blow it out of proportion. All signs indicate this is a serious and credible threat and users should remain diligent in ensuring that they either install or update their virus scanners. Many of the popular freely available scanners are now able to detect this threat, both in compiled binaries and are able to detect an infected SysConst.dcu.

As we get more information and have more recommendations, we’ll make it available. Moving forward we’re brainstorming about possible solutions that you, the customer, can use to be not only guard against future similar threats, but also if a new threat were found there can be a prescribed mechanism to detect and correct the problem.