6

I have added these two methods to the 1st unit of my Delphi 5 application.

function Inp(PortAddress: Integer): Integer; stdcall; external 'inpout32.dll' name 'Inp32';

procedure Output(PortAddress, Value: Integer); stdcall; external 'inpout32.dll' name 'Out32';

However I don't want to have to issue the inpout32 library with the software unless they explicitly need it. Currently the program says "Not Found" upon executing unless they're present in the root or System32.

Users will only call these methods if they have a specific option set, but this is not gathered from the .ini file until after the inpout library is used.

Is there a way to only use this library when required like some components do, rather than declaring it the way I have?

bluish
  • 26,356
  • 27
  • 122
  • 180
notidaho
  • 588
  • 8
  • 28
  • 3
    Yes, you can do 'dynamic loading'. Use `LoadLibrary` and `GetProcAddress`, as discussed [here](http://en.wikipedia.org/wiki/Dynamic_loading#Windows). – Andreas Rejbrand Feb 13 '12 at 16:05

2 Answers2

16

This facility, known as delay loading, was added in Delphi 2010.

Using your code as an example you could write your import like this:

function Inp(PortAddress: Integer): Integer; stdcall; 
    external 'inpout32.dll' name 'Inp32' delayed;

The binding to this external function will be performed only when the function is first called. If the binding fails then an exception is raised at runtime.

You can use SetDliNotifyHook and SetDliFailureHook to customise the delay loading behaviour should you need even more fine-grained control.

Some blog articles to supplement the product documentation:


On older versions of Delphi you can use LoadLibrary and GetProcAddress. Or, if you want something a little slicker I can heartily recommend Hallvard Vassbotn's delay load class which he describes in this blog article. This code wraps up all the boiler plate of calling LoadLibrary and GetProcAddress and is only slightly more cumbersome to use than the new Delphi 2010 built-in feature.

I successfully used Hallvard's library for many years. One minor word of caution is that it is not threadsafe so if multiple threads attempt to bind to a function at the same time then the code can fail. This is easy enough to fix by adding internal locks to Hallvard's code.

JRL
  • 3,363
  • 24
  • 36
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Oh, so there is no need to `GetProcAddress` and all that anymore? Too bad, that was fun... – Andreas Rejbrand Feb 13 '12 at 16:06
  • 1
    @Andreas Personally I'm still using GetProcAddress and my own class to handle delay loading, but no, they added this new feature in D2010. Too late for you I fear. – David Heffernan Feb 13 '12 at 16:09
  • Thanks David seems like a good idea but is there any way I can do this in Delphi5? – notidaho Feb 13 '12 at 16:10
  • Gah, I just saw the D5 in your text. Missed it on first read. Better off in a tag FWIW. In D5 I would steer you to Hallvard Vassbotn's delay load library - see my updated answer. – David Heffernan Feb 13 '12 at 16:17
  • Not to worry David maybe it'll be useful to some one else. Personally I think I'm gonna go down LoadLibrary and GetProcAddress route. cheers – notidaho Feb 13 '12 at 19:45
16

In Delphi versions prior to 2010, you have to use classic dynamic loading. Consider this typical (and simple) example calling the Beep function from Kernel32.dll (which you should not hardcode the path to in real code, of course!):

type
  TBeepFunc = function(dwFreq: DWORD; dwDuration: DWORD): BOOL; stdcall;

procedure TForm4.FormClick(Sender: TObject);
var
  lib: HMODULE;
  prc: TBeepFunc;
begin

  lib := LoadLibrary('C:\WINDOWS\System32\Kernel32.dll');
  if lib = 0 then RaiseLastOSError;
  try
    @prc := GetProcAddress(lib, 'Beep');
    if Assigned(prc) then
      prc(400, 2000)
    else
      ShowMessage('WTF? No Beep in Kernel32.dll?!');
  finally
    FreeLibrary(lib);
  end;
end;
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Thanks @Andreas this looks like the easiest solution. RaiseLastOSError wasn't recognised in my Delphi5 though. Am I safe to remove the If statement as the error will always be caught by the second message "WTF? Either no process or library not loaded?!" – notidaho Feb 13 '12 at 19:42
  • 1
    @notidaho: No, if `lib = 0` you must not enter the `try` block. You can replace `RaiseLastOSError` with any exception-raising code, such as `raise Exception.Create('Couldn't load library.')`. Of course, you can also replace the line in questoin with `if lib <> 0 then`, so that the entire `try..finally..end` will be run only if the `lib` is valid. But it is probably better to display an error message. – Andreas Rejbrand Feb 13 '12 at 19:49
  • 5
    `RaiseLastOSError` = `RaiseLastWin32Error` in D5 – kobik Feb 13 '12 at 20:34
  • This method has worked an absolute treat thanks. I issued a user friendly message as I'm not sure how useful the last raised error is? As a point of interest even though I just try to LoadLibrary('input.dll') from the root it still works if the library is in System32. – notidaho Feb 14 '12 at 11:17