Passing Strings between the Application and the DLL


Now we're going to create a DLL that uses PChars to pass string data to the calling application. This application is displayed in Figure 21-6.

image from book
Figure 21-6: The MessageDlg text is from the DLL.

The first thing you have to do is create the host application:

  1. Create a new VCL Forms application project.

  2. Add two TRadioGroup components to the Designer Surface.

  3. Add English, German, and Croatian items to both TRadioGroup components.

Once you've created the host application's user interface, you have to create a unit for the languages enumeration that will be used by the host application and the DLL and then add a new DLL project to the project group.

Here's the MyTypes unit that contains the languages enumeration:

unit MyTypes; interface type   TMyLanguage = (mlEnglish, mlGerman, mlCroatian); implementation end.

There's only one decision that you have to make when you want to pass a PChar between the application and the DLL — how to manage the PChar's memory. Here's a list of things that you can and cannot do:

  • You can allocate and deallocate the PChar in the DLL.

  • You can allocate and deallocate the PChar in the calling application (this is the Windows API way).

  • You cannot allocate the PChar's memory in the DLL and release it in the host application, or vice versa (see Figure 21-7).

    image from book
    Figure 21-7: Bad things happen when the host application tries to release memory allocated in the DLL.

The following listing shows how to allocate and deallocate the PChar's memory in the DLL. The listing contains two routines: the GetStringData function that allocates memory for and returns a localized string, and the FreeString- Data procedure that must be used to release the memory allocated by the GetStringData function.

Listing 21-8: Routines that manage the memory in the DLL

image from book
library LanguageLib; uses SysUtils, MyTypes; const   STRING_DATA: array[TMyLanguage] of string = ('Good morning.',     'Guten Morgen.', 'Dobro jutro.'); { return a localized string } function GetStringData(Language: TMyLanguage): PChar; begin   { allocate the memory for the string and for the #0 character }   GetMem(Result, Length(STRING_DATA[Language]) + 1);   { the StrPCopy function copies a string to a null-terminated string }   StrPCopy(Result, STRING_DATA[Language]); end; { use this routine to release the PChars allocated with GetStringData } procedure FreeStringData(Data: PChar); begin   FreeMem(Data); end; exports   GetStringData,   FreeStringData; begin end.
image from book

Listing 21-9 shows how to use the GetStringData and the FreeStringData routines in the host application.

Listing 21-9: Using the GetStringData and the FreeStringData routines to retrieve a localized string from the DLL

image from book
procedure TMainForm.RadioGroup1Click(Sender: TObject); var   DLLHandle: THandle;   stringFunc: function(Language: TMyLanguage): PChar;   freeProc: procedure (Data: PChar);   buffer: PChar;   selectedLang: TMyLanguage; begin   selectedLang := TMyLanguage(RadioGroup1.ItemIndex);   DLLHandle := LoadLibrary('LanguageLib.dll');   try     { find both routines }     stringFunc := GetProcAddress(DLLHandle, 'GetStringData');     freeProc := GetProcAddress(DLLHandle, 'FreeStringData');     if not Assigned(stringFunc) or not Assigned(freeProc) then       MessageDlg('Error Loading DLL!', mtError, [mbOK], 0)     else begin       buffer := stringFunc(selectedLang);       try         MessageDlg(string(buffer), mtInformation, [mbOK], 0);       finally         { free the PChar's memory using the DLL's FreeStringData routine }         freeProc(buffer);       end;       // try..finally     end;         // if not Assigned   finally     FreeLibrary(DLLHandle);   end;          // try..finally end;
image from book

If you really need to pass string data between the application and the DLL, you can do things the API way — allocate and deallocate PChars in the host application and design DLL functions to accept the PChar buffer and the maximum number of characters to copy. When doing things the API way, you also have to enable the developer to call the function with invalid parameters (like nil pointers) to determine how big the buffer should be.

The following listing shows the GetStringDataEx function, which expects the developer to pass both a pointer to a valid buffer and the length of the buffer. The GetStringDataEx function also allows the developer to pass nil to determine the appropriate length for the buffer.

Listing 21-10: Doing things the API way

image from book
library LanguageLib; uses SysUtils, MyTypes; const   STRING_DATA: array[TMyLanguage] of string = ('Good morning.',     'Guten Morgen.', 'Dobro jutro.'); { the developer can pass nil to determine how big his/her buffer has to be } { if the developer passed a valid Buffer, StrPLCopy copies   BufferLen bytes from a Delphi string to the Buffer } function GetStringDataEx(Language: TMyLanguage;   Buffer: PChar; BufferLen: Integer): Integer; begin   Result := Length(STRING_DATA[Language]);   if Buffer <> nil then     StrPLCopy(Buffer, STRING_DATA[Language], BufferLen); end; exports   GetStringData,   FreeStringData,   GetStringDataEx; begin end.
image from book

Listing 21-11 shows how to use the GetStringDataEx function in the host application.

Listing 21-11: Calling the GetStringDataEx function

image from book
procedure TMainForm.RadioGroup2Click(Sender: TObject); var   DLLHandle: THandle;   selectedLang: TMyLanguage;   buffLen: Integer;   buffer: PChar;   stringProc: function(Language: TMyLanguage;     Buffer: PChar; BufferLen: Integer): Integer; begin   selectedLang := TMyLanguage(RadioGroup2.ItemIndex);   DLLHandle := LoadLibrary('LanguageLib.dll');   try     StringProc := GetProcAddress(DLLHandle, 'GetStringDataEx');     if Assigned(StringProc) then     begin       { pass nil to the GetStringDataEx function to determine the         appropriate size for the buffer }       buffLen := StringProc(selectedLang, nil, 0);       { allocate the buffer }       GetMem(Buffer, buffLen + 1);       try         { call the GetStringDataEx function normally }         StringProc(selectedLang, Buffer, buffLen);         MessageDlg(string(Buffer), mtInformation, [mbOK], 0);       finally         { release the buffer }         FreeMem(Buffer);       end;        // try..finally (Buffer)     end else       MessageDlg('Cannot load the DLL!', mtWarning, [mbOK], 0);   finally     FreeLibrary(DLLHandle);   end;       // try..finally (DLLHandle) end;
image from book



Inside Delphi 2006
Inside Delphi 2006 (Wordware Delphi Developers Library)
ISBN: 1598220039
EAN: 2147483647
Year: 2004
Pages: 212
Authors: Ivan Hladni

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net