I have a Delphi form inside a DLL (I know that this restricts the use of the DLL to Delphi but this is not a problem in this case).
The DLL exports a function ShowForm that looks roughly like this:
procedure ShowForm (App : TApplication);
begin
OldApp := Application;
try
Application := App;
MyForm := TMyForm.Create (nil);
try
MyForm.ShowModal;
finally
FreeAndNil (MyForm);
end;
finally
Application := OldApp;
end;
end;
Now on the form I use a TAdvOfficeHint (from the TMS component pack). Unfortunately the hints do not show up.
Am I missing something here? How can I make the form behave exactly as it would if I showed it from the main application?
Thanks!
I don't know TAdvOfficeHint but I guess it hooks Application.OnShowHint to set its own THintWindowClass, and even if both the main executable and the DLL are linking in the TMS unit, they each have their own copy of the class which is where things go wrong.
Assigning Application is not enough: there are other global variables, like Screen, Mouse, etc. Others are even hidden in the implementation so I'd say your chances to make the form behave exactly as from the main application are slim.
Just found the reason why it does not work. As TOndrej states, TAdvOfficeHinthooks Application.OnShowHint and internally executes the following line of code:
FHintInfo.Assign (AHintInfo);
Assign internally uses a dynamic type check
if (Source is TAddvHintInfo) then ...
which fails due to the separate type registries of the DLL and the main application.
I have run into this problem a few times now and maybe I really have to switch to runtime packages to avoid all this stuff.
Anyway, if there's anything I can do to prevent this, please comment.
Wrong setting of Application.
Try this and see if it solves your problem:
procedure ShowForm (AppHandle : THandle);
begin
OldAppHandle := Application.Handle;
try
Application.Handle := AppHandle;
........
finally
Application.Handle := OldAppHandle;
end;
end;
I guess in Delphi 2006 and later versions you can call System.ShareMemoryManager method in the EXE code, so that its memory manager is shared with other modules loaded in the process memory space.
Related
I'm using great TExceptionDialog from JEDI JCL package to show unhandled exceptions inside a C++ builder XE project, everything running ok so far. I've decided to enhance it a little bit by writing my own custom form to upload crash report to a server via FTP.
Problem is that I can't open my custom form from delphi PAS unit, tried to define as an external (no delphi programmer here, sorry :( ) but don't know how to properly code that. I've read lots of tutorials but couldn't find anything useful besides writing a DLL or an OLE container for my custom form, realy overkill for this project.
Question is, how can I properly execute this task? how to do ShowModal() of a form defined in a C++ unit, from a PAS delphi unit?
I've found an easy and practical way of doing it, kinda ugly but works!
Trick is to get form by iterating thru all forms with Screen.Forms object. I've set TAG property for my form to a predefined number just to get an easy id of it.
In short, inside C++ unit of my form, I'll do this:
MyForm->Tag=9999; // easy way of Iding my form
Then, inside my delphi unit of TExceptionDialog, in SEND button click method:
procedure TExceptionDialog.SendBtnClick(Sender: TObject);
var
i: integer;
form: TForm;
begin
for i := 0 to Screen.FormCount-1 do // all forms
begin
form := Screen.Forms[i]; // get a form
if(form.Tag = 9999) then // check if its my form
begin
form.ShowModal; // if its mine, call showmodal
break;
end;
end;
ModalResult := mrOk; // return to my app
end;
i am developing a a component in delphi 7 and delphi 2006,component uses a .pas (Not mine )file which requires a DLL file to be present in the application directory.
It is possible to embed the DLL file into the component, so that when the user drops it on the form or create it at run time the DLL will be placed in the application directory?
currently
1) i am telling the user to place DLL file in the application directory.
2) Add the DLL file in the Resources , so that on create, i can drop the DLL into the application directory? from delphidabbler_embed_resource. This i have done using
{Drop the Resource..!!!}
procedure DropDllToApplicationDirectOry(applicationPath : string);
var
RS: TResourceStream;
begin
// Create resource stream
RS := TResourceStream.CreateFromID(HInstance, 100, RT_RCDATA);
try
// applicationPath : example c:\MyTestProject Lee\
if DirectoryExists(applicationPath) then RS.SaveToFile(applicationPath+'myDllFileWhichIsNeeded.dll')
finally
// Free the stream
RS.Free;
end;
end;
this DropDllToApplicationDirectOry take the resource from the {$RmyDllFileWhichIsNeeded.dll.RES} and drope to the location but
how do i call DropDllToApplicationDirectOry this when i drop the component on the Form?
i tried initialization of the component but DLL is not copied so i get the error
EDIT
For RXControls's TRxClock when we drop the clock runs on this form, the clock begins to run(show the curent time)...
so i tried this
constructor Tmycomponeny.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{add dll}
DropDllToApplicationDirectOry(ExtractFilePath(Application.ExeName));
end;
But this doesnt work..
The code OF RXControls
constructor TRxClock.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
if not Registered then begin
ClockInit;
Registered := True;
end;
Caption := TimeToStr(Time);
ControlStyle := ControlStyle - [csSetCaption]
{$IFDEF WIN32} - [csReplicatable] {$ENDIF};
BevelInner := bvLowered;
BevelOuter := bvRaised;
FTimer := TRxTimer.Create(Self);
FTimer.Interval := 450; { every second }
FTimer.OnTimer := TimerExpired;
FDotsColor := clTeal;
FShowSeconds := True;
FLeadingZero := True;
GetTime(FDisplayTime);
if FDisplayTime.Hour >= 12 then Dec(FDisplayTime.Hour, 12);
FAlarmWait := True;
FAlarm := EncodeTime(0, 0, 0, 0);
end;
This idea is not going to work, in general. You are assuming that the developer will always drop your component onto a form. But they could just as well check an existing project out from revision control and then the DLL would not be created.
In my opinion you should simply document the dependency and let the developer ensure that the dependency is met. Suggest that they add the DLL to their revision control system so that it is checked out into the application directory.
The developer is going to need to be aware of this dependency when it comes to deployment. That has to be the developer's responsibility. So it is just cleaner and easier to let the developer manage this full stop.
Although David is correct that documenting is probably the only reliable approach, your question is an interesting one.
You seem to have written the code to write the dll to a file so the problem is responding to an event that will be fired when the component is created?
If I was to do this I think I would check on component creation that the dll exists and if not create it at this stage if not. Is it possible for you to load the dll dynamically? If you can only use the dll linked statically, then David's way is really the only reliable way.
What you are asking for is technically doable, but ONLY if the DLL is dynamically loaded at runtime using LoadLibrary(). The "Unable to Locate Component" error is a result of statically linking to the DLL at compile-time instead, which makes it impossible to extract the DLL at runtime (unless the static linked DLL is delay-loaded, which utilizes LoadLibrary() internally).
If, and only if, the DLL is dynamically/delay loaded, then you can extract it from your component's resources at runtime before the DLL is used. When calling TResourceStream.CreateFromID(), do not use the global HInstance variable, use the FindClassHInstance() function instead. The global HInstance variable will not point to the component's module if the component is used in a project with Runtime Packages enabled, but FindClassHInstance() can find the correct module no matter how the component is linked in to the project.
Try putting the dll file in the Contains folder of the Package you're creating, using the Project Manager from Delphi.
I have a Com Object, setup/create/working from a DataModule.
creating/running/freeing the Datamodule from an Application works with out an issue.
but putting the datamodule into a DLL works fine the first time, runing the com object etc.. but after a few calls with out restarting the application, this error appears.
Error Message image http://darkaxi0m.name/so/errormessage.GIF
There is a fare bit of code in the App, so i cant post it all,
I have tried MadExcept in both the Application and Dll, with no luck. The IDE Breaks at a point that does not seem much help...
alt text http://darkaxi0m.name/so/cpubreak.gif
this is the code that handles the DataModule, the same function is used in the Application and the Dll in both tests
function GetAmount( Amount : integer; var Info: PChar): integer; stdcall;
var
tempInfo: string;
workerDM : TworkerDM;
begin
Result := 0;
workerDM := TworkerDM.Create(nil);
try
tempInfo:= Info;
Result := workerDM.GetAmount(Amount, tempInfo);
StrPCopy(Info, tempInfo);
finally
workerDM.Free;
end;
end;
i would like to blame the Ole Object, but it works fine out of the Dll
I'm at a loss to even think where to start looking.
In the finally, you are calling Free, but should call workerDM.Free.
I don't believe this question can be answered any more.
The project has be scraped, and the object that produce the error no longer used.
My Delete Requests have gone unanswered.
So this is now my answer.
I am going through most of my applications and porting them to D2009 and I have one application that makes use of dynamic packages. For the life of me I cannot get my host application to recognize classes registered in a package. I traced through and the initialization section in the package being loaded was called and RegisterClasses was called but when I do a GetClass() call the classes are not available. Is there someone out there who can enlighten me as to what might be going on? I have researched and looked to see if there are any issues with the D2009 release and dynamic packages and so far I have found nothing. I'm beginning to wonder if I have a corrupted installation of Delphi or some other problem.
TIA
If you are using a 3rd party memory manager then make sure it is proven to work with D2009 (actually 2007 and up).
With FastMM (which is the default MM since 2007) you would have to set the UseRuntimePackages define in FastMM4Options.inc
make sure that the following steps are done:
Create a new package in Delphi;
Insert a form in this package;
Insert a "inicialization" section in the form and uses the RegisterClass method. (registerClass(TForm1)); Don't forget the "T".
Save and compile the package;
Close all;
Copy the .bpl file (c:\Users\Public\Documents\RAD Studio\5.0\Bpl) to the application folder;
Create a new aplication in Delphi;
Go in Project > Options > Packages, and check the box "Build with runtime packages";
Leave only "vcl;rtl" in the text field and click OK button;
Insert a button;
In the source of the button, insert the code:
procedure TForm1.Button1Click(Sender: TObject);
var
PackageModule: HModule;
AClass: TPersistentClass;
begin
PackageModule := LoadPackage('Package1.bpl');
if PackageModule <> 0 then
begin
AClass := GetClass('TForm2');
if AClass <> nil then
with TComponentClass(AClass).Create(Application)
as TCustomForm do
begin
ShowModal;
Free;
end;
UnloadPackage(PackageModule);
end;
end;
Compile the application. =)
I'm writing a property editor for Delphi and I would like it to show up on the correct screen for multi-monitor support. In order to position it, I would like a reference to the "main" form for the Delphi IDE.
I've tried using the Application's MainForm property, and the Application object itself, but neither seems to work. I believe this is because the MainForm is actually the hidden TApplication instance referenced in this article by Nathanial Woolls (search for "application form"):
http://www.installationexcellence.com/articles/VistaWithDelphi/Original/Index.html
Does anyone know how to get a handle to the visible main form for the IDE. I'm trying to avoid something cheesy like iterating all forms and searching for "CodeGear RAD Studio" in the caption.
The IDE's main form is Application.MainForm. My quick test design package:
procedure DoStuff(Form: TCustomForm);
var
S: string;
begin
S := Form.Caption;
Form.Caption := S + ' - this one';
try
ShowMessage(Format('%s [%s] on monitor %d', [Form.Name, Form.ClassName, Form.Monitor.MonitorNum]));
finally
Form.Caption := S;
end;
end;
initialization
DoStuff(Application.MainForm);
This in my case displays "AppBuilder [TAppBuilder] on monitor 0" and I can see the " - this one" suffix in the main form's caption.
What doesn't seem to work in your case?
IIRC the main form is called TAppBuilder, so something like FindWindow('TAppBuilder',nil) might be a starting point for you.