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.