Trouble adding an image

Hi,
Thanks for the sample project, we were able to reproduce it here. The issue is that you are passing a TXlsFile object from the main program to the dll, and you can't pass complex objects through the dll. Imagine if the dll was compiled with a different version of Delphi: The TXlsFile object you create in your app might have a different memory layout from the TXlsFile in the dll.

When passing parameters to a dll, you can't even pass strings (but you can pass widestrings). Only simple types. the rules for dll are:
1)Never pass more than simple types from one side to the other.
2)Never pass exceptions from one side to the other.

The correct (non crashing :) way to do your test would be with this
In the dll, define a CreateXlsFile and FreeXlsFile methods which return pointers:


library Project2;

{ Remarque importante sur la gestion m?moire de la DLL : ShareMem doit ?tre la
  premi?re unit? de la clause USES de votre biblioth?que ET la clause USES
  (s?lectionner Projet-Voir le source) de votre projet si votre DLL exporte toute proc?dure ou
  fonction qui passe des cha?nes par le biais de param?tres ou de r?sultats de fonctions. Cela
  s'applique ? toutes les cha?nes pass?es vers et depuis votre DLL -- m?me celles qui
  sont imbriqu?es dans des enregistrements et des classes. ShareMem est l'unit? d'interface au
  gestionnaire de m?moire partag?e BORLNDMM.DLL, qui doit ?tre d?ploy?
  avec votre DLL. Pour ?viter l'emploi de BORLNDMM.DLL, passez des informations cha?ne
  par le biais de param?tres PChar ou ShortString. }

uses
  FastMM4,
  Vcl.Dialogs,
  System.SysUtils,
  System.Classes,
  VCL.FlexCel.Core,
  FlexCel.XlsAdapter;

{$R *.res}

type
  PXlsFile = pointer;

function CreateXlsFile: PXlsFile; stdcall;
begin
  try
    Result := TXlsFile.Create(1, true);
  except
    Result := nil;
  end;
end;

procedure FreeXlsFile(const xls: PXlsFile); stdcall;
begin
  TXlsFile(xls).Free;
end;

function yTest(pxls: PXlsFile): boolean; stdcall;
var
  fs: TFileStream;
  ImgProps: IImageProperties;
  xls: TXlsFile;
begin
   Result := True;
   try
      xls := TXlsFile(pxls);
      fs := TFileStream.Create('client-icon.gif', fmOpenRead or fmShareDenyNone);
      try
         ImgProps := TImageProperties_Create();
         ImgProps.Anchor := TClientAnchor.Create(TFlxAnchorType.MoveAndDontResize, 2, 102, 4, 883, 6, 38, 6, 38);
         ImgProps.ShapeName := 'Logo';
         xls.AddImage(fs, ImgProps);
      finally
         fs.Free;
      end;
   except
      on E:Exception do
      begin
         showmessage(E.Message);
         Result := False;
      end;
   end;
end;

function ySave(pxls : PXlsFile): boolean; stdcall;
var
  xls: TXlsFile;
begin
   try
      xls := TXlsFile(pxls);
      xls.Save('..\test.xlsx');

      Result := True;
   except
      on E:Exception do
      begin
         showmessage(E.Message);
         Result := False;
      end;
   end;
end;

exports
   CreateXlsFile,
   FreeXlsFile,
   yTest,
   ySave;

begin

end.



Then, in the program, call it like this:


procedure TForm1.Button1Click(Sender: TObject);
var
   xls : Pointer;
   Crt : function : Pointer; stdcall;
   Fct : function (xls: Pointer): boolean; stdcall;
   DLL : THandle;

begin

   FlexCelDllInit;
   xls := nil;

   DLL := LoadLibrary('dll\Project2.dll');
   if DLL <> 0 then
   try
      try
         @Crt := GetProcAddress(Dll, 'CreateXlsFile');
         if Assigned(Crt) then
            xls := Crt();
         if (xls = nil) then raise Exception.Create('Can''t create Excel file');

      except
         on E:Exception do
         begin
            showmessage(E.Message);
         end;
      end;

      try
         @Fct := GetProcAddress(Dll, 'yTest');
         if Assigned(Fct) then
            Fct(xls);
      except
         on E:Exception do
         begin
            showmessage(E.Message);
         end;
      end;
...
Remember to free the XlsFile object by calling the FreeXlsFile method in the dll, not with xls.Free. You can't do anything directly with FlexCel in your main app: everything should be done in the dll. In fact, you shouldn't be using FlexCel units in the main app at all (And if you do, make sure to never mix it with dll calls.



I am not sure the reasons you have to go with a dll but if I might, I would suggest a couple of things:
1)You might try to do the full work in the dll instead of trying to pass parameters from one side to the other. So you would have a:
DoEverything() in the dll which will create the TXlsFile, do the stuff, then free it. From the app side, you just call DoEverything() in the dll and don't care about FlexCel at all. All FlexCel calls are encapsulated in the dll, and your app doesn't use any FlexCel units.
Remember you can pass widestrings from one side to another, so having a procedure like:
DoEverything(filename: widestring) is ok.

2)If you want a more granular control like in your example (having a method to open the file, other to set cell values, other to save), then you need to use pointers as shown above, but this can be a lot of work soon. I know, I've done it :) http://www.tmssoftware.com/site/flexceldll.asp

And since I have already done it, my suggestion would be to use that instead of creating the full dll. You can mail me and I can send you a free version of FlexCel dll if this approach would work for you.

So to resume I would either do full procedures that encapsulate the full business logic (methods like CreateBusinessReport, etc), or go the full other way with FlexCel dll: Have every FlexCel method encapsulated using pointers. What is best for you depends in what you are doing and the reasons you have to use a dll.