Bruce Weinel
Bruce Weinel

Reputation: 41

Get DLL caller info in Delphi

In a Delphi DLL need to establish the caller -- which may be a simple ".exe" or a DBMS runtime module -- which means it must obtain the command which is running in the process.

I know that CmdLine won't work, and probably ParamStr(0), and cannot use "main window" based techniques as caller will sometimes not have a window. I suspect that GetModuleHandle is the starting point, but need assistance to get from there to command being executed.

Upvotes: 4

Views: 1426

Answers (2)

David Heffernan
David Heffernan

Reputation: 612954

In fact ParamStr(0) will work fine. It is, on Windows, implemented with a call to the API function GetModuleFileName, passing a value of 0 as the module handle. This retrieves the file name associated with the main executable module. This works just the same no matter that the call is made from a DLL or the main executable.

We don't really need to dig into the implementation if we would trust the Delphi documentation. Admittedly this can sometimes be a risky business! The documentation for ParamStr says:

ParamStr(0) returns the path and file name of the executing program (for example, C:\TEST\MYPROG.EXE).

If you need to know the arguments that were passed to the executable process, you can use ParamStr passing indices greater than zero. Or you could call GetCommandLine and parse the command line yourself.

Do beware that GetCommandLine will not always give the same executable file name as GetModuleFileName. The documentation says:

The name of the executable in the command line that the operating system provides to a process is not necessarily identical to that in the command line that the calling process gives to the CreateProcess function. The operating system may prepend a fully qualified path to an executable name that is provided without a fully qualified path.

All this feels a little dirty though. It might be cleaner to export an initialization function from the DLL and require callers to pass whatever information you need.

Upvotes: 1

Ian Boyd
Ian Boyd

Reputation: 256711

I created a test dll:

library Project2;

uses
  System.SysUtils, System.Classes, Vcl.Forms, Vcl.Dialogs, Winapi.Windows;

{$R *.res}

procedure DoStuff; stdcall;
begin
    ShowMessage(
            'ParamStr(0): '+ParamStr(0)+#13#10+
            'GetCommandLine: : '+GetCommandLine);
end;

exports
    DoStuff;
begin
end.

And then call it from a test application:

procedure TForm1.Button1Click(Sender: TObject);
var
    module: HMODULE;
    doStuff: procedure; stdcall;
begin
    module := LoadLibrary('D:\Temp\Win32\Debug\Project2.dll');
    if module = 0 then
        RaiseLastOSError;
    try
        doStuff := GetProcAddress(module, 'DoStuff');

        if @doStuff = nil then
            raise Exception.Create('Could not find export "DoStuff"');

        DoStuff;
    finally
        FreeLibrary(module);
    end;
end;

And it sees the command line, using both:

  • ParamStr(0)
  • GetCommandLine

enter image description here

GetCommandLine obviously shows the entire command line, while ParamStr(0) is (by definition) just the process executable path.

Upvotes: 4

Related Questions