1

EDIT: Dumb question, already fixed. Form1 was nil because I didn't assign it a new TForm1, I forgot Delphi doesn't do that for you like C++.

I have a Delphi DLL that I want to use for the GUI of my C++ program, so just for starters, I created a form, and have a function that will show the form which is exported so that C++ can call it. However, the program crashes when it calls the function. Here is my code. (I am using Delphi 2010)

The delphi part:

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Tabs, ComCtrls;

type
  TForm1 = class(TForm)
    TabControl1: TTabControl;
    TabSet1: TTabSet;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

function ShowForm(i: Integer) : Integer; export; cdecl;

exports
  ShowForm name 'ShowForm';

implementation

{$R *.dfm}

function ShowForm(i: Integer) : Integer; export; cdecl;
begin
  Form1.Show();

  Result := 3; // random value, doesn't mean anything
end;

end.

And here is the C++ code:

HMODULE h = LoadLibrary("delphidll.dll");

if (!h) {
    printf("Failed LoadLibrary (GetLastError: %i)\n", GetLastError());

    return 0;
}

FARPROC p = GetProcAddress(h, "ShowForm");

if (p)
    printf("Found it @ %p\n", p);
else
    printf("Didn't find it\n");

((int(__cdecl *)(int))p)(34);

system("PAUSE");

return 0;

The program prints "Found it @ " and then crashes. If I comment out Form1.Show() in the Delphi DLL, it doesn't crash, and the function returns 3 (tested by printf). Am I missing some initialization or something? Thanks.

Okey
  • 105
  • 5

1 Answers1

2

The reason it crases is that the var Form1: TForm1; is not initialized.

The reason that the var Form1: TForm1; is not initialized, is most likely because you put the unit Main into a DLL project, but it originally came from a Delphi VCL project where you had Form1 on the auto-creation list.

The auto-creation list means that the Delphi .dpr will initialize the form.

Now you need to manually create the form, so you need to export these 3 new routines from your DLL, and have the C++ DLL call them:

function CreateForm() : Integer; export; cdecl;
begin
  try
    Application.CreateForm(TForm1, Form1);
    Result := 0;
  except
    Result := -1;
  end;
end;

function DestroyForm() : Integer; export; cdecl;
begin
  try
    if Assigned(Form1) then
    begin
      FreeAndNil(Form1);
      Application.ProcessMessages();
    end;
    Result := 0;
  except
    Result := -1;
  end;
end;

function DestroyApplication() : Integer; export; cdecl;
begin
  try
    FreeAndNil(Application);
    Result := 0;
  except
    Result := -1;
  end;
end;

In addition, you should put a try...except block around the implementation of your ShowForm function implementation, as exceptions and other language dependent run-time features should not cross DLL boundaries.

You probably should do similar things for releasing other potentially allocated pieces of dynamic memory too.

--jeroen

Community
  • 1
  • 1
Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • Jeroen, good answer, but OP had already reached that conclusion and edited the Q so! – David Heffernan Feb 06 '11 at 17:00
  • There are two important things of my answer not in his edit: is the try/except blocks, and the freeing of the Application. – Jeroen Wiert Pluimers Feb 06 '11 at 17:28
  • @Jeroen I don't understand why you call `ProcessMessages` and I see no reason for freeing the application. – David Heffernan Feb 06 '11 at 19:49
  • I remember having seen a project where the ProcessMessages was needed because the forms did stupid things when being destroyed. The Application instance is auto-created upon first access, so it needs to be destroyed; the DLL won't do that by itself. – Jeroen Wiert Pluimers Feb 06 '11 at 21:46
  • @Jeroen If he's going to be showing in an app a form created in a DLL, he's going to need to be running a message loop himself. I never feel comfortable pumping my owner's messages! The Application objects is created in `Controls.InitControls` which is called from `initialization`. It is destroyed in the matching routine `DoneControls` called from `finalization`. You shouldn't be freeing it. – David Heffernan Feb 07 '11 at 09:11
  • I'm not sure that unit initialization/finalization is always performed right in DLL's ("The Old New Thing" made some very nice posts on the underlying mechanism), hence the suggestion. Thanks for bringing up the other point too: I totally forgot about the message pumping. – Jeroen Wiert Pluimers Feb 07 '11 at 10:23
  • @Jeroen I do know about `initialization` and `finalization` and DLL issues, as it happens. The thing is, in a Delphi DLL, the Controls.pas `initialization` will run inside DLLMain, as will the `finalization`. There's nothing you can do about that. That's what runs the lifetime of the `Application` object in your suggested code. So you may as well accept that and in any case it works fine. I also wonder why you call `Application.CreateForm` rather than `TForm1.Create`. I'm not sure one would want `Application.MainForm` to be set here. – David Heffernan Feb 07 '11 at 12:03
  • Forgive me if I'm wrong, but I read (I thought it was in the "old new thing", but can't find it right now) that `DllMain` should consist of very few things and is not always called. Any case: I have seen horrible things go wrong with `DllMain`, especially if it is not called on the UI thread, and also with delayed loading: hence the explicitness. Since most code wants a main form (too much code I have seen depends on it), I opted for the `CreateForm` in stead of the `TForm1.Create`. If we'd live in a perfect world, code would be much simpler, and these kludges would not be needed. – Jeroen Wiert Pluimers Feb 07 '11 at 12:58
  • @Jeroen You are quite right about DLLMain, but you can't control what happens in the VCL initialization/finalization and they are going to run inside DLLMain whether you like it or not. The reality is that they don't result in problems. Your initialization/finalization may have problems if you are not careful but I believe that the VCL is safe with respect to DLLMain. Raymond has talked about it and it is also documented on the DLLMain topic at MSDN. Also you keep forgetting to use @David so that I get notified!! – David Heffernan Feb 07 '11 at 13:08
  • @David: Sorry, I didn't know that @David would notify you. I thought people always got notifications of comments in the threads they were participating in. What if I change my answer to replace the freeing of Application by a message loop? – Jeroen Wiert Pluimers Feb 07 '11 at 13:45
  • @jeroen I wouldn't bother. Poster has his answer. – David Heffernan Feb 07 '11 at 13:54