#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0

    Post Dll & FreeLibrary


    Hy all!

    I have a main program that may call a function that's stored in dll.

    The code I use for calling and freeing library:

    Code:
    type TEdgeDetection = function (B : TBitmap) : TBitmap; 
    var  DLLHandle: THandle; 
         EdgeDetection : TEdgeDetection; 
     begin 
           DLLHandle := LoadLibrary('PEdgeDetect.dll'); 
           if DLLHandle <> 0 then 
               begin 
                    @EdgeDetection := GetProcAddress(DLLHandle, 'EdgeDetection'); 
                    Image1.Picture.Bitmap := EdgeDetection(Image1.Picture.Bitmap);
                    FreeLibrary(DLLHandle); 
               end; 
      end;
    The thing is, I can't figure out is why my main program closes after FreeLibrary() command. (I don't want it to close)
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    Haven't used this dll before, but usually when you call a dll, the calling convention is stdcall. so this line:

    TEdgeDetection = function (B : TBitmap) : TBitmap;

    should be

    TEdgeDetection = function (B : TBitmap) : TBitmap; stdcall;

    Although I'm not confident that this is the reason why your application terminates.

    Also, you may want to check to ensure EdgeDetection is valid:
    Code:
    if DLLHandle <> 0 then 
    begin 
       @EdgeDetection := GetProcAddress(DLLHandle, 'EdgeDetection'); 
       if assigned(EdgeDetection) then
          Image1.Picture.Bitmap := EdgeDetection(Image1.Picture.Bitmap)
       else
          ShowMessage('Error Assigning EdgeDetection');
       FreeLibrary(DLLHandle); 
    end;
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    I wrote this dll and it works when I staticaly load it, so I don't think the problem lie in calling convention... I think stdcall is used when you build dll that will have to work with different programming languages. This one is written in delphi and it will be used in delphi. Also I did use ShareMem.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    ShareMem wouldn't be an issue in this case. Sharemem is only if you want to pass Pascal strings between the DLL and your application or application to DLL.

    I don't think we have enough information with what you've posted to assess why the application is terminating.

    How are you calling this code?
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    Code:
    if DLLHandle <> 0 then 
    begin 
       @EdgeDetection := GetProcAddress(DLLHandle, 'EdgeDetection'); 
       if assigned(EdgeDetection) then
          Image1.Picture.Bitmap := EdgeDetection(Image1.Picture.Bitmap) //dll function
       else
          ShowMessage('Error Assigning EdgeDetection');
       FreeLibrary(DLLHandle); 
    end;
    this is the function which I use from my code:
    EdgeDetection(B:Bitmap):Bitmap;
    It works when I link it statically:
    function EdgeDetection(B:Bitmap):Bitmap; external ('ED.dll');
    but when I try to load it dynamically, code in my first post, it closes my application on FreeLibrary.. Dll still works but it closes my aplication :S
    p.s. It's a button click.
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,055
    Rep Power
    383
    I suggest you show the entire context of the call. So far I do not believe you have even included the procedure header within which your code is running.
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    Here's a link to .rar file with all projects, .exe and .dll files. In Win32/debug folder is executable. You can open projects/units with delphi or any text editor.

    Code:
    http://www.sendspace.com/file/oxfccj
    To start downloading press this when you are on web-page:
    Code:
     Click here to start download from sendspace
    If you modify .dll and add:
    Showmessage('sadasd');
    In it's
    begin
    end
    section at the bottom then it will draw requested color on Image1.
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    OK, the issue you had was that you were trying to return an object from a DLL, which is technically possible, but not easy. See here:

    http://books.google.com/books?id=HWo0H6GYuqYC&pg=PT316

    Read the section on: Exporting Objects from DLLs.

    So, the way around this is to work with Windows HBitMap rather than Delphi's TBitMap.

    The code to the updated DLL follows
    Code:
    library PEdgeDetect;
    
    uses
      WinAPI.Windows,
      WinAPI.Messages,
      System.SysUtils,
      System.Classes,
      Vcl.Graphics,
      Vcl.Dialogs;
    
    {$R *.res}
    
    procedure EdgeDetection(var B : TBitmap);
    TYPE
       TRGB32 = packed record
          B, G, R, A: Byte;
       end;
       TRGB32Array = packed array[0..MaxInt div SizeOf(TRGB32)-1] of TRGB32;
       PRGB32Array = ^TRGB32Array;
    
    VAR
       line : PRGB32Array;
       y, x : word;
       Width, Height: Integer;
    begin
       Width := B.Width;
       Height := B.Height;
       B.PixelFormat := pf32bit;
       for y := 0 to (Height - 1) do
       begin
          line := B.ScanLine[y];
          for x := 0 to (Width - 1) do
          begin
             line[x].B := 0;
             line[x].G := 255;
             line[x].R := 0;
             line[x].A := 0;
          end;
       end;
    end;
    
    procedure ModifyBitmap(ParentWnd: HWND; out BitmapHandle: HBITMAP); safecall;
    var
       Bitmap: TBitmap;
    begin
       BitmapHandle := 0;
       Bitmap := TBitmap.Create;
       try
          Bitmap.Handle := ParentWnd;
          EdgeDetection(Bitmap);
          BitmapHandle := Bitmap.ReleaseHandle;
       finally
          Bitmap.Free;
       end;
    end;
    
    exports ModifyBitmap;
    
    begin
    end.
    and you call it like so:
    Code:
    procedure TForm1.Button2Click(Sender: TObject);
    type
       TEdgeDetectionFunc = procedure (ParentWnd: HWND; out BitmapHandle: HBITMAP); safecall;
    var
       DLLHandle: cardinal;
       EdgeDetection : TEdgeDetectionFunc;
       BitmapHandle: HBITMAP;
    begin
       DLLHandle := LoadLibrary('PEdgeDetect.dll');
       if DLLHandle <> 0 then
       begin
          @EdgeDetection := GetProcAddress(DLLHandle, 'ModifyBitmap');
          if Assigned(EdgeDetection) then
          begin
             EdgeDetection(Image1.Picture.Bitmap.Handle, BitmapHandle);
             Image1.Picture.Bitmap.Handle := BitmapHandle;
          end;
          FreeLibrary(DLLHandle);
       end;
    end;

    Comments on this post

    • dexter_cro agrees : Very helpful!
    Last edited by majlumbo; November 29th, 2012 at 10:52 PM.
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    It works.. Thank you!
  18. #10
  19. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    And it stopped working O.o

    Test Form:
    Code:
    unit Forma;
    
    interface
    
    uses
      Winapi.Windows, jpeg, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        Image1: TImage;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.Button1Click(Sender: TObject);
      begin
        Image1.Picture.LoadFromFile('Bikesgray.bmp');
      end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    type
       TEdgeDetectionFunc = procedure (ParentWnd: HWND; out BitmapHandle: HBITMAP); safecall;
    var
       DLLHandle: cardinal;
       EdgeDetection : TEdgeDetectionFunc;
       BitmapHandle: HBITMAP;
    begin
       DLLHandle := LoadLibrary('PDll.dll');
       if DLLHandle <> 0 then
       begin
          @EdgeDetection := GetProcAddress(DLLHandle, 'ModifyBitmap');
          if Assigned(EdgeDetection) then
          begin
             EdgeDetection(Image1.Picture.Bitmap.Handle, BitmapHandle);
             Image1.Picture.Bitmap.Handle := BitmapHandle;
             Image1.Repaint;    // So we can see that dll did make change
          end;
          FreeLibrary(DLLHandle);
       end;
    end;
    
    
    procedure TForm1.FormCreate(Sender: TObject);
      begin
        Image1.Picture.LoadFromFile('Bikesgray.bmp');
      end;
    
    end.
    dll:
    Code:
    library PDll;
    
    { Important note about DLL memory management: ShareMem must be the
      first unit in your library's USES clause AND your project's (select
      Project-View Source) USES clause if your DLL exports any procedures or
      functions that pass strings as parameters or function results. This
      applies to all strings passed to and from your DLL--even those that
      are nested in records and classes. ShareMem is the interface unit to
      the BORLNDMM.DLL shared memory manager, which must be deployed along
      with your DLL. To avoid using BORLNDMM.DLL, pass string information
      using PChar or ShortString parameters. }
    
    uses
      ShareMem,
      WinAPI.Windows,
      WinAPI.Messages,
      System.SysUtils,
      System.Classes,
      Vcl.Graphics,
      Vcl.Dialogs;
    
    {$R *.res}
    
    procedure EdgeDetection(var B : TBitmap);
    TYPE
       TRGB32 = packed record
          B, G, R, A: Byte;
       end;
       TRGB32Array = packed array[0..MaxInt div SizeOf(TRGB32)-1] of TRGB32;
       PRGB32Array = ^TRGB32Array;
    
    VAR
       line : PRGB32Array;
       y, x : word;
       Width, Height: Integer;
    begin
       B.PixelFormat := pf32bit;
       Width := B.Width;
       Height := B.Height;
       for y := 0 to (Height -1) do
       begin
          line := B.ScanLine[y];
          for x := 0 to (Width -1) do
          begin
             line[x].B := 0;
             line[x].G := 255;
             line[x].R := 0;
             line[x].A := 0;
          end;
       end;
    end;
    
    procedure ModifyBitmap(ParentWnd: HWND; out BitmapHandle: HBITMAP); safecall;
    var
       Bitmap: TBitmap;
    begin
       BitmapHandle := 0;
       Bitmap := TBitmap.Create;
       try
          Bitmap.Handle := ParentWnd;
          EdgeDetection(Bitmap);
          BitmapHandle := Bitmap.ReleaseHandle;
       finally
          Bitmap.Free;
       end;
    end;
    
    exports ModifyBitmap;
    
    begin
      showmessage('Closing dll');
    end.
    It rapidly closes after executing dll...
  20. #11
  21. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    Ok, it works when I comment ShareMem... why is ShareMem responsible for closing whole application? :S
  22. #12
  23. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    Originally Posted by dexter_cro
    Ok, it works when I comment ShareMem... why is ShareMem responsible for closing whole application? :S
    I'm at a loss why adding Sharemem would crash the application? Ensure you have ShareMem as the first unit used in both the DLL and the DPR file.

    I've never used ShareMem, always avoided it. The only reason why ShareMem should be used is to allow your application and the DLL to share Delphi strings because Delphi manages the memory associated with strings. So if you don't plan on passing strings (That includes structures like TStringList) back and forth from your app and the dll, then don't include ShareMem in the uses list at all.

    UPDATE:
    Since ShareMem is the interface to BORLNDMM.DLL, you may want to make sure that you also have that DLL and that it is in on your PATH. If it's not in your path, then that could explain why the app is crashing.
    Another possibility, are you creating a x64 application? Again, you'd need a x64 version of BORLNDMM.DLL along with a x64 version of your DLL.
    Last edited by majlumbo; December 4th, 2012 at 03:54 PM.
  24. #13
  25. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    8
    Rep Power
    0
    I put ShareMem first in dll uses and unit uses as it's specified in dll commented header and I did put BORLNDMM.DLL in same folder as my executable. I will not exchange any strings with dll (maybe some chars) so I don't think I'll need it. And I'm doing x32 application.
    The app doesn't crash with any error, it just closes like I used Form.close procedure...

IMN logo majestic logo threadwatch logo seochat tools logo