DLL
  1. FTP4W32.DLL & Delphi 2.0
  2. About Thunking D2 to 16bit DLL
  3. Alternatives to DLL?
  4. Creating MDI child forms in a DLL and calling them from an MDI parent executable...?[NEW]

FTP4W32.DLL & Delphi 2.0

From: bstowers@pobox.com (Brad Stowers)

Here's a unit that works for me. Also, I'm working on a component that wraps up the DLL. If anyone is interested in testing it, let me know by email (bstowers@pobox.com) and I'll send it to you.


unit FTP4W;
{ Updated Feb. 1997 by Brad Stowers (bstowers@pobox.com) for use with  }
{ FTP4W v2.6.  Modified to add new functions, fix some errors, make it }
{ "cleaner", and work with Delphi 2.  I do not use Delphi 1 at all, so }
{ it is extremely likely that this won't work with Delphi 1, i.e.      }
{ 'stdcall' won't compile.  If you need to use with Delphi 1, use the  }
{ Pascal 'UseFTP4W.pas' sample file, or try deleting all the 'stdcall' }
{ directives.  This code based on previous work as credited below:     }

{by Barbara Tikart  Polarwolf Hard & Software, D-63906 Erlenbach am Main}
{and AStA Uni Konstanz (AStA = Allgemeiner Studierenden Ausschuss)}
{eMail to Andreas.Tikart@uni-konstanz.de or AStA@uni-konstanz.de}
{Declarations for FTP module to use with 'FTP4W' Version 2.2g or higher}
{Released into Public Domain}
{Get the newest version via
http://www.uni-konstanz.de/studis/asta/software/index.html}


interface

uses Windows, WinSock, SysUtils;


const
  FTP4W_Loaded: boolean = FALSE;        { Check to see if the DLL was
loaded.           }
  FTP4W_RightVersion: boolean = FALSE;  { Check to see if we have right
version of DLL. }


const
{ Transfer modes. }
  TYPE_A       = 'A'; { ASCII }
  TYPE_I       = 'I'; { Image (Bin) }
  TYPE_L8      = 'L'; { Local 8 }
  TYPE_DEFAULT = #0;  { Whatever server thinks it is. }

{ Actions requested by user.... What are these? }
  FTP_STORE_ON_SERVER  = 65;
  FTP_APPEND_ON_SERVER = 87;
  FTP_GET_FROM_SERVER  = 223;

{ Firewall Types, for when Philippe gets firewall done. }
  FTP4W_FWSITE          = 100;
  FTP4W_FWPROXY         = 103;
  FTP4W_FWUSERWITHLOGON = 106;
  FTP4W_FWUSERNOLOGON   = 109;

{ Return codes of FTP functions }
  FTPERR_OK            = 0; { succesful function }
  FTPERR_ENTERPASSWORD = 1; { userid need a password }
  FTPERR_ENTERACCOUNT  = 2; { user/pass OK but account required }
  FTPERR_ACCOUNTNEEDED = 2; { user/pass OK but account required }
  FTPERR_RESTARTOK     = 3; { Restart command successful        }
  FTPERR_ENDOFDATA     = 4; { server has closed the data-conn   }
  FTPERR_CANCELBYUSER = -1; {Transfer aborted by user FtpAbort}


{ User's or programmer's errors }
  FTPERR_INVALIDPARAMETER    = 1000; { Error in parameters }
  FTPERR_SESSIONUSED         = 1001; { User has already a FTP session }
  FTPERR_NOTINITIALIZED      = 1002; { FtpInit has not been call }
  FTPERR_NOTCONNECTED        = 1003; { User is not connected to a server }
  FTPERR_CANTOPENFILE        = 1004; { can not open specified file }
  FTPERR_CANTWRITE           = 1005; { can't write into file (disk full?) }
  FTPERR_NOACTIVESESSION     = 1006; { FtpRelease without FtpInit }
  FTPERR_STILLCONNECTED      = 1007; { FtpRelease without any Close }
  FTPERR_SERVERCANTEXECUTE   = 1008; { file action not taken }
  FTPERR_LOGINREFUSED        = 1009; { Server rejects usrid/passwd }
  FTPERR_NOREMOTEFILE        = 1010; { server can not open file }
  FTPERR_TRANSFERREFUSED     = 1011; { Host refused the transfer }
  FTPERR_WINSOCKNOTUSABLE    = 1012; { A winsock.DLL ver 1.1 is required }
  FTPERR_CANTCLOSE           = 1013; { close failed (cmd is in progress) }
  FTPERR_FILELOCKED          = 1014; { temporary error during FtpDelete }
  FTPERR_FWLOGINREFUSED      = 1015; { Firewallrejects usrid/passwd }
  FTPERR_ASYNCMODE           = 1016; { FtpMGet only in synchronous mode }

{ TCP errors }
  FTPERR_UNKNOWNHOST         = 2001; { can not resolve host adress }
  FTPERR_NOREPLY             = 2002; { host does not send an answer }
  FTPERR_CANTCONNECT         = 2003; { Error during connection }
  FTPERR_CONNECTREJECTED     = 2004; { host has no FTP server }
  FTPERR_SENDREFUSED         = 2005; { can't send data (network down) }
  FTPERR_DATACONNECTION      = 2006; { connection on data-port failed }
  FTPERR_TIMEOUT             = 2007; { timeout occurred }
  FTPERR_FWCANTCONNECT       = 2008; { Error during connection with FW }
  FTPERR_FWCONNECTREJECTED   = 2009; { Firewall has no FTP server }

{ FTP errors }
  FTPERR_UNEXPECTEDANSWER    = 3001; {answer was not expected}
  FTPERR_CANNOTCHANGETYPE    = 3002; { host rejects the TYPE command }
  FTPERR_CMDNOTIMPLEMENTED   = 3003; { host recognize but can't exec cmd }
  FTPERR_PWDBADFMT           = 3004; { PWD cmd OK, but answer has no " }
  FTPERR_PASVCMDNOTIMPL      = 3005; { Server don't support passive mode }

{ Resource errors }
  FTPERR_CANTCREATEWINDOW    = 5002; { Insufficent free resources }
  FTPERR_INSMEMORY           = 5003; { Insufficient Heap memory }
  FTPERR_CANTCREATESOCKET    = 5004; { no more socket }
  FTPERR_CANTBINDSOCKET      = 5005; { bind is not succesful }
  FTPERR_SYSTUNKNOWN         = 5006; { host system not in the list }


{ FTP4W internal data structures  You'll probably never need these. }
const
  FTP_DATABUFFER = 4096; {a good value for X25/Ethernet/Token Ring}

type
  PFtp_FtpData = ^TFtp_FtpData;
  TFtp_FtpData = packed record
    ctrl_socket: TSocket;             { control stream       init
INVALID_SOCKET }
    data_socket: TSocket;             { data stream          init
INVALID_SOCKET }
    cType: Char;                      { type (ASCII/binary)  init TYPE_A
}
    bVerbose: Bool;                   { verbose mode         init FALSE
}
    bPassif: Bool;                    { VRAI -> mode passif
}
    nPort: u_short;                   { connexion Port       init
FTP_DEFPORT    }
    nTimeOut: u_int;                  { TimeOut in seconds   init
FTP_DEFTIMEOUT }
    hLogFile: HFile;                  { Log file
}
    szInBuf: Array [0..2047] of Char; { incoming Buffer
}
    saSockAddr: TSockAddrIn;          { not used anymore
}
    saAcceptAddr: TSockAddrIn;        { not used anymore
}
  end; { TFtp_FtpData }

  PFtp_FileTrf = ^TFtp_FileTrf;
  TFtp_FileTrf = packed record
    hf: HFile;          { handle of the file which is being transfered }
    nCount: uint;       { number of writes/reads made on a file        }
    nAsyncAlone: uint;  { pause each N frame in Async mode  (Def 40)   }
    nAsyncMulti: uint;  { Idem but more than one FTP sssion (Def 10)   }
    nDelay: uint;       { time of the pause in milliseconds            }
    bAborted: Bool;     { data transfer has been canceled              }
    szBuf : Array [0..FTP_DataBuffer-1] Of Char; { Data buffer         }
    bNotify: Bool;      { application receives a msg each data packet  }
    bAsyncMode: Bool;   { synchronous or asynchronous Mode             }
    lPos: LongInt;      { Bytes transfered                             }
    lTotal: LongInt;    { bytes to be transfered                       }
  end; { TFtp_FileTrf }

  PFtp_Msg = ^TFtp_Msg;
  TFtp_MSG = packed record
    hParentWnd: hWnd;        { window which the msg is to be passed  }
    nCompletedMessage: uint; { msg to be sent at end of the function }
  end; { TFtp_Msg }

  PFtp_Verbose = ^TFtp_Verbose;
  TFtp_Verbose = packed record
    hVerboseWnd: hWnd;  { window which the message is to be passed    }
    nVerboseMsg: uint;  { msg to be sent each time a line is received }
  end; { TFtp_Verbose }

  PFtp_ProcData = ^TFtp_ProcData;
  TFtp_ProcData = packed record
    { Task data }
    hTask: HTask;              { Task Id                              }
    hFtpWindow: hWnd;          { Handle of the internal window        }
    hParentWnd: hWnd;          { Handle given to the FtpInit function }
    hInstance: HInst;          { Task Instance                        }
    bRelease:  Bool;           { FtpRelease has been called           }
    { Mesasge information }
    MSG: TFtp_Msg;
    VMSG: TFtp_Verbose;
    { File information }
    FileTrf: TFtp_FileTrf;
    {Ftp information}
    Ftp: TFtp_FtpData;
    {Linked list}
    Next,
    Prev: PFtp_ProcData;
  end; { TFtp_ProcData }

{ FtpMGet callback function type. }
  TFtpMGetCallback = Function (szRemFile, szLocalFile: PChar; Rc: integer):
bool; stdcall;


{ FTP4W Functions }

var
{ Utilities functions}
  FtpDataPtr:     function: PFtp_ProcData; stdcall;
  FtpBufferPtr:   function: PChar; stdcall;
  FtpErrorString: function(Rc: integer): PChar; stdcall;
  Ftp4wVer:       function(szVerStr: PChar; nStrSize: integer): Integer;
stdcall;

{ Change default parameters}
  FtpSetVerboseMode:       function(bVerboseMode: bool; hWindow: hWnd;
                                    wMsg: UINT): Integer; stdcall;
  FtpBytesTransferred:     function: LongInt; stdcall;
  FtpBytesToBeTransferred: function: LongInt; stdcall;
  FtpSetDefaultTimeOut:    procedure(nTo_in_sec: Integer); stdcall;
  FtpSetDefaultPort:       procedure(nDefPort: Integer); stdcall;
  FtpSetAsynchronousMode:  procedure; stdcall;
  FtpSetSynchronousMode:   procedure; stdcall;
  FtpIsAsynchronousMode:   function: Bool; stdcall;
  FtpSetNewDelay:          procedure(X: Integer); stdcall;
  FtpSetNewSlices:         procedure(X, Y: Integer); stdcall;
  FtpSetPassiveMode:       procedure(bPassive: Bool); stdcall;
  FtpLogTo:                procedure(hLogFile: HFile); stdcall;

{ Init functions}
  FtpRelease: function: Integer; stdcall;
  FtpInit:    function(hWindow: hWnd): Integer; stdcall;
  FtpFlush:   function: Integer; stdcall;

{ Connection }
  FtpLogin:           function(Host, User, Password: PChar;
                               hWindow: hWnd; wMSG: UINT): Integer;
stdcall;
  FtpOpenConnection:  function(Host: PChar): Integer; stdcall;
  FtpCloseConnection: function: Integer; stdcall;
  FtpLocalClose:      function: Integer; stdcall;

{ Authentification}
  FtpSendUserName: function(UserName: PChar): Integer; stdcall;
  FtpSendPasswd:   function(Passwd: PChar): Integer; stdcall;
  FtpSendAccount:  function(Acct: PChar): integer; stdcall;

{ Commands }
  FtpHelp:       function(Arg, Buf: PChar; BufSize: UINT): Integer;
stdcall;
  FtpDeleteFile: function(szRemoteFile: PChar): Integer; stdcall;
  FtpRenameFile: function(szFrom, szTo: PChar): Integer; stdcall;
  FtpQuote:      function(Cmd, ReplyBuf: PChar; BufSize: UINT): Integer;
stdcall;
  FtpSyst:       function(var szSystemStr: PChar): Integer; stdcall;
  FtpSetType:    function(cType: char): Integer; stdcall;
  FtpCWD:        function(Path: PChar): Integer; stdcall;
  FtpCDUP:       function: Integer; stdcall;
  FtpPWD:        function(szBuf: PChar; uBufSize: UINT): Integer; stdcall;
  FtpMKD:        function(szPath, szFullDir: PChar; uBufSize: UINT):
Integer; stdcall;
  FtpRMD:        function(szPath: PChar): Integer; stdcall;

{ file transfer }
  FtpAbort:              function: Integer; stdcall;
  FtpSendFile:           function(Local, Remote: PChar; cType: char;
Notify: Bool;
                                  hWindow: hWnd; wMSG: UINT): Integer;
stdcall;
  FtpAppendToRemoteFile: function(Local, Remote: PChar; cType: char;
Notify: Bool;
                                  hWindow: hWnd; wMSG: UINT): Integer;
stdcall;
  FtpRecvFile:           function(Remote, Lcl: PChar; cType: char; Notify:
Bool;
                                  hWindow: hWnd; wMSG: UINT): Integer;
stdcall;
  FtpAppendToLocalFile:  function(Remote, Lcl: PChar; cType: char; Notify:
Bool;
                                  hWindow: hWnd; wMSG: UINT): Integer;
stdcall;
  FtpGetFileSize:        function: DWORD; stdcall;
  FtpMGet:               function(szFilter: PChar; cType: char; bNotify:
bool;
                                  Callback: TFtpMGetCallback): integer;
stdcall;
  FtpRestart:            function(ByteCount: longint): integer; stdcall;
  FtpRestartSendFile:    function(hLocal: HFile; szRemote: PChar; cType:
char;
                                  bNotify: bool; ByteCount: Longint;
                                  hWindow: hWnd; wMsg: UINT): integer;
stdcall;
  FtpRestartRecvFile:    function(szRemote: PChar; hLocal: HFile; cType:
char;
                                  bNotify: bool; ByteCount: Longint;
                                  hWindow: hWnd; wMsg: UINT): integer;
stdcall;

{ Directory }
  FtpDir: function (Def, LocalFile: PChar; LongDir: Bool;
                    hWindow: hWnd; wMSG: UINT): Integer; stdcall;

{ Advanced }
  FtpOpenDataConnection:        function(szRemote: pchar; nAction: integer;
                                         cType: char): integer; stdcall;
  FtpRecvThroughDataConnection: function(szBuf: Pchar;
                                         var BufSize: UINT): integer;
stdcall;
  FtpSendThroughDataConnection: function(szBuf: PChar; BufSize: UINT):
integer; stdcall;
  FtpCloseDataConnection:       function: integer; stdcall;

{ Firewall }
  FtpFirewallLogin: function (szFWHost, szFWUser, szFWPass, szRemHost,
szRemUser,
                              szRemPass: PChar; nFirewallType: integer;
                              hParentWnd: hWnd; wMsg: UINT): integer;
stdcall;

{ Misc }
  InitFtpGetAnswerCode: function: integer; stdcall;


implementation

const
  ftp4wdll = 'FTP4W32.dll'; { DLL file name }

var
  hFtp4W: THandle; { DLL handle }


{ Load the DLL and get all the procedure addresses. }
function LoadFtp4WDLL: boolean;
var
  OldMode: UINT;
begin
  if hFtp4W <> 0 then
    FreeLibrary (hFtp4W);
  OldMode := SetErrorMode(SEM_NOOPENFILEERRORBOX); { No system messages if
can't load. }
  hFtp4W := LoadLibrary (ftp4wdll);
  Result := hFtp4W <> 0;
  SetErrorMode(OldMode);
  if not Result then exit;

  { Get all the function addresses }
  @FtpDataPtr :=                   GetProcAddress(hFtp4W, 'FtpDataPtr');
  @FtpBufferPtr :=                 GetProcAddress(hFtp4W, 'FtpBufferPtr');
  @FtpErrorString :=               GetProcAddress(hFtp4W,'FtpErrorString');
  @Ftp4wVer :=                     GetProcAddress(hFtp4W, 'Ftp4wVer');
  @FtpSetVerboseMode :=            GetProcAddress(hFtp4W,'FtpSetVerboseMode');
  @FtpBytesTransferred :=          GetProcAddress(hFtp4W,'FtpBytesTransferred');
  @FtpBytesToBeTransferred :=      GetProcAddress(hFtp4W,'FtpBytesToBeTransferred');
  @FtpSetDefaultTimeOut :=         GetProcAddress(hFtp4W,'FtpSetDefaultTimeOut');
  @FtpSetDefaultPort :=            GetProcAddress(hFtp4W,'FtpSetDefaultPort');
  @FtpSetAsynchronousMode :=       GetProcAddress(hFtp4W,'FtpSetAsynchronousMode');
  @FtpSetSynchronousMode :=        GetProcAddress(hFtp4W,'FtpSetSynchronousMode');
  @FtpIsAsynchronousMode :=        GetProcAddress(hFtp4W,
'FtpIsAsynchronousMode');
  @FtpSetNewDelay :=               GetProcAddress(hFtp4W,
'FtpSetNewDelay');
  @FtpSetNewSlices :=              GetProcAddress(hFtp4W,
'FtpSetNewSlices');
  @FtpSetPassiveMode :=            GetProcAddress(hFtp4W,
'FtpSetPassiveMode');
  @FtpLogTo :=                     GetProcAddress(hFtp4W, 'FtpLogTo');
  @FtpRelease :=                   GetProcAddress(hFtp4W, 'FtpRelease');
  @FtpInit :=                      GetProcAddress(hFtp4W, 'FtpInit');
  @FtpFlush :=                     GetProcAddress(hFtp4W, 'FtpFlush');
  @FtpLogin :=                     GetProcAddress(hFtp4W, 'FtpLogin');
  @FtpOpenConnection :=            GetProcAddress(hFtp4W,
'FtpOpenConnection');
  @FtpCloseConnection :=           GetProcAddress(hFtp4W,
'FtpCloseConnection');
  @FtpLocalClose :=                GetProcAddress(hFtp4W, 'FtpLocalClose');
  @FtpSendUserName :=              GetProcAddress(hFtp4W,
'FtpSendUserName');
  @FtpSendPasswd :=                GetProcAddress(hFtp4W, 'FtpSendPasswd');
  @FtpSendAccount :=               GetProcAddress(hFtp4W,
'FtpSendAccount');
  @FtpHelp :=                      GetProcAddress(hFtp4W, 'FtpHelp');
  @FtpDeleteFile :=                GetProcAddress(hFtp4W, 'FtpDeleteFile');
  @FtpRenameFile :=                GetProcAddress(hFtp4W, 'FtpRenameFile');
  @FtpQuote :=                     GetProcAddress(hFtp4W, 'FtpQuote');
  @FtpSyst :=                      GetProcAddress(hFtp4W, 'FtpSyst');
  @FtpSetType :=                   GetProcAddress(hFtp4W, 'FtpSetType');
  @FtpCWD :=                       GetProcAddress(hFtp4W, 'FtpCWD');
  @FtpCDUP :=                      GetProcAddress(hFtp4W, 'FtpCDUP');
  @FtpPWD :=                       GetProcAddress(hFtp4W, 'FtpPWD');
  @FtpMKD :=                       GetProcAddress(hFtp4W, 'FtpMKD');
  @FtpRMD :=                       GetProcAddress(hFtp4W, 'FtpRMD');
  @FtpAbort :=                     GetProcAddress(hFtp4W, 'FtpAbort');
  @FtpSendFile :=                  GetProcAddress(hFtp4W, 'FtpSendFile');
  @FtpAppendToRemoteFile :=        GetProcAddress(hFtp4W,
'FtpAppendToRemoteFile');
  @FtpRecvFile :=                  GetProcAddress(hFtp4W, 'FtpRecvFile');
  @FtpAppendToLocalFile :=         GetProcAddress(hFtp4W,
'FtpAppendToLocalFile');
  @FtpGetFileSize :=               GetProcAddress(hFtp4W,
'FtpGetFileSize');
  @FtpMGet :=                      GetProcAddress(hFtp4W, 'FtpMGet');
  @FtpRestart :=                   GetProcAddress(hFtp4W, 'FtpRestart');
  @FtpRestartSendFile :=           GetProcAddress(hFtp4W,
'FtpRestartSendFile');
  @FtpRestartRecvFile :=           GetProcAddress(hFtp4W,
'FtpRestartRecvFile');
  @FtpDir :=                       GetProcAddress(hFtp4W, 'FtpDir');
  @FtpOpenDataConnection :=        GetProcAddress(hFtp4W,
'FtpOpenDataConnection');
  @FtpRecvThroughDataConnection := GetProcAddress(hFtp4W,
'FtpRecvThroughDataConnection');
  @FtpSendThroughDataConnection := GetProcAddress(hFtp4W,
'FtpSendThroughDataConnection');
  @FtpCloseDataConnection :=       GetProcAddress(hFtp4W,
'FtpCloseDataConnection');
  @FtpFirewallLogin :=             GetProcAddress(hFtp4W,
'FtpFirewallLogin');
  @InitFtpGetAnswerCode :=         GetProcAddress(hFtp4W,
'InitFtpGetAnswerCode');
end;

{ Procedure called when unit is finished, i.e. app exiting. }
procedure MyExitProc; far;
begin
  if hFtp4W <> 0 then begin
    { Make sure we shut everything down so we don't cause FTP4W to leak. }
    FtpAbort;
    FtpFlush;
    FtpCloseConnection;
    FtpLocalClose;
    FTPRelease;
    { Unload the DLL. }
    FreeLibrary(hFtp4W)
  end;
end;


var
  VerInfo: array[0..100] of char;
  FVer: integer;
Begin
  hFtp4W := 0;
  AddExitProc(MyExitProc);
  FTP4W_Loaded := LoadFtp4WDLL;
  if FTP4W_Loaded then begin
    { Check to make sure we have a version we can use. }
    if @Ftp4wVer = NIL then
      FVer := 0
    else
      FVer := Ftp4wVer(VerInfo, sizeof(VerInfo));
    FTP4W_RightVersion := not ((HiByte(FVer) < 2) or ((HiByte(FVer) = 2)
and (LoByte(FVer) < 96)));
  end;
end.



About Thunking D2 to 16bit DLL

From: Mike Heacock <cerebus@islandnet.com>

Please include this URL...as it has an article and sample code within its pages.

http://members.aol.com/delphimag/index.htm

Alternatives to DLL?

From: "Dmitry" <dimon@diogen.nstu.nsk.su>

> I was wondering is there any way that I can have a main form compiled
> into a EXE and 
> EACH SECONDARY FORM COMPILED INDIVIDUALLY
> and then dynamically load in these other secondary forms in runtime 
> ( DLL concept)
Why not?
(* The DLL unit *)
library MyLib;
type 
  TMyDLLForm = class(TForm)
     ...
  end;

{$IF dynamically creation}
function CreateForm: TForm; {$IFDEF WIN32} stdcall;{ENDIF} export;
begin
  Result := TMyDLLForm.Create(Nil);
  Result.OnClose := @FreeForm;
end;
{$ELSE}
var
  MyDllForm: TForm;

function CreateForm: TForm; {$IFDEF WIN32} stdcall;{ENDIF} export;
begin
  Result := MyDLLForm;
end;
{$ENDIF}

exports
  CreateForm;
end.
(****************)

(* The Main Form in exe module. Calling the MyDLLForm *)

function CreateForm: TForm; {$IFDEF WIN32} stdcall;{ENDIF} external
'MyLib';

procedure TMyMainExeForm.OnOpenDllFormBtnClick(Sender: TObject);
var
   form: TForm;
begin
   form := CreateForm;
   form.owner := self;
   form.parent := self;
end;

Creating MDI child forms in a DLL and calling them from an MDI parent executable...?[NEW]

From: kduffey@transecureinc.com

Basically you have to do a number of things to get DLL forms to appear as MDI child windows. The nice thing is, you can add the WINDOWS main menu to your main exe menu, and all child windows appear under this window when created! Here goes:

First, I assume you are loading the DLLs dynamically? With LoadLibrary and GetProcAddress?? If not, you will need to do so, as far as I know.

The key is to pass the Application.Handle to the DLLs, and assign that passed variable to the Application.Handle in each DLL. Also, each DLL should export the same procedure or function name. Your main exe that loads the DLLs should keep a TLIST of each DLLs HANDLE. That is to say, when you load a DLL dynamically, you MUST keep it "alive" by not freeing (freelibrary) its handle. Thus, you need some way to keep track of every DLL you load. The easiest way is to use a TLIST. I will show you:


PROGRAM MainProgram;

INTERFACE

uses Windows, etc...;

IMPLEMENTATION


type
  SimpleProc = procedure; stdcall;
  DLLInitProc = procedure( AppHandle : HWND; CallBackProc : SimpleProc
); stdcall;

var
  HandleList : TList;
  DLLCallProc : SimpleProc;
  
procedure LoadDLL( name : string );
var
  DLLHandle : THandle;
  DLLInit : DLLInitProc;

begin
   if FileExists( name ) then
   begin
      DLLHandle := LoadLibrary( name );
      if DLLHandle <> 0 then
     begin
         // loaded, so get the procedure we call to pass info to it
         @DLLInit := GetProcAddress( DLLHandle, 'ExportedDLLProc' );
         if Assigned( DLLInit ) then
         begin
             // the DLL exported procedure is found, so lets call it
             DLLInit( Application.Handle, DLLCallProc );
             
             // now we save this DLLs handle to later FREE it
             // when the program ends.
             HandleList.Add( DLLHandle );
         end;
     end;
  end;
end;         

procedure LoadAllDLLs;

begin
   // without coding it, somehow you will have to
   // figure out how to find ALL the dlls path/filenames
   // possibly in a .CFG file, the Registry, etc.

   // loop, getting each path/filename string
    LoadDLL( DLLName ); // DLLName = path/filename
end;

procedure TForm1.SomeMenuItemClickToOpenDLLWindow;
begin
  DLLCallProc; // call the passed back DLL proc
end;

INITIALIZATION
  HandleList := TList.Create;

FINALIZATION
  HandleList.Free;

END.

DLL CODE:
LIBRARY DLLUnit;

INTERFACE

USES
  DLLSource in 'DLLSource.pas';


EXPORTS
  DLLInit name 'DLLInit';

IMPLEMENTATION

END.



UNIT DLLSource;

INTERFACE

uses Windows, etc..;

  procedure DLLInit( AppHandle : HWND ); stdcall; export;

IMPLEMENTATION

USES 
  DLLFormSource;

var
  OldAppHandle : THandle;


procedure HandleMenuClick;
begin
  if Form1 = nil then
    DLLForm := TDLLForm.Create( Application.Handle )
  else
    BringWindowToTop( Form1.Handle );
end;


procedure DLLInit;
begin
  // first we assign the passed in Application handle
  Application.Handle := AppHandle;

  // now we assign the passed in CallBackProc to the address
 // of the procedure we use to handle when the user clicks
 // on a menu item activating this MDI form.
CallBackProc := @HandleMenuClick;
end;

INITIALIZATION
  OldAppHandle := Application.Handle;

FINALIZATION
  Application.Handle := OldAppHandle

END.


DLL Form source:


UNIT DLLFormSource;

INTERFACE

USES
  Windows, etc..;

  TDllForm  = class( TForm );
      procedure OnClose( TObject, Action, etc);
      procedure OnDestroy();
   end;

var
  DLLForm : TDLLForm;

IMPLEMENTATION

procedure TDLLForm.OnClose;
begin
  action := caFree;
end;

procedure TDLLForm.OnDestroy;
begin
  DLLForm := nil;
end;

END.

Ok. Ok. There is a "bit" of code here..but really its mostly done for you when you create the 2 projects. The MAIN program, you automatidcally get the main form. This is where you put the MainMenu on the form, and the handler for a menu click that calls an MDI form. Also, you make this form fsMDIForm. the DLL forms are ALL fsMDIChild. One thing I goofed on in the code above, is that I didnt create a TLIST to store each DLLS "CallbackProc" variable. That is to say, in my plug-in core, each DLL calls a REGISTER procedure passed to it just like Application.Handle is passed. Each DLL then calls that procedure (implemented in the MAIN exe program), which in turn keeps track of each DLLs "HANDLE" routine to call when the associated menu item is clicked. So, you will have to implement that if you have more than one DLL loading. And dont forget to keep all DLL handles in memory until you quit the program.

Anyways. So, the MAIN program loads each DLL, and calls the DLLInit routine, passing to it the Application.Handle variable, and a variable called CallBackProc. Do NOT forget to use the STDCALL on these types of declarations. They too will generate a GPF if you dont do this. Now, this variable is passed to each DLL. It is assigned in the DLL to a procedure in the DLL. With the DLL(s) loaded in memory, if the user clicks on the menu item associated with one DLL, it then calls this routine. This routine, implemented in the DLL source, first checks to see if the dynamic form, DLLForm is created. If it is, it then brings that window to the top, just as if you selected it via the WINDOW menu. If it isn't, it CREATES it. Since the DLL forms are all MDI Child windows, they automagically appear when you create them. If you were doing a normal app (like Delphis User Interface) without MDI windows, but normal windows, then you would also need to add something like:

 DLLForm.Show
or
DLLForm.ShowModal; 
to the section that creates the window.


Please email me and tell me if you liked this page.