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.