The way my application functions, is determined by Skype's view mode, due to the fact that my application is looking for windows of class TConversationWindow, which if in Default View is a child of tSkMainForm, and if in Compact View, it is not a child of tSkMainForm.
Here is what I tried to do:
Function IsCompactView:Boolean;
Var
Wnd : Hwnd;
Begin
Result := True;
Wnd := FindWindow('TConversationForm',nil);
if Wnd <> 0 then
begin
Wnd := GetParent(Wnd);
// Custom function that grabs the Window Text
if GetHandleText(Wnd) <> '' then
Result := False;
end;
End;
The above function will look for top-level (unless I am mistaken - the windows with no window parent) TConversationForm's, by checking if their parent has text or not. If Skype is in Default View, the TConversationForm's are children of tSkMainForm, which always has some text. It works as it is supposed to.
Now for the actual problem: Whenever the user switches between the 2 views, the top-level TConversationForm's are not "refreshed". They disappear alright, but in order for it to appear as a child of tSkMainForm again (so the change is visible in Winspector Spy), you have to select it in Skype, and I cannot rely on the user to do that.
In case you dont know, here is the difference between the 2 views:
Compact View
Default View
If you need more info please let me know, thanks!
Instead of detect if Skype is in “Compact View” or “Default View” using a windows approach try reading the config.xml file which store these kind of settings and is updated in "real-time" by skype. This file is located in
%AppData%\Skype\<your-skype-user-name>
for example in windows 7 this is the location
C:\Users\<your windows user>\AppData\Roaming\Skype\<your-skype-user-name>
Inside of this file in the exist a entry called MultiWindowMode
This is the Xpath location of the MultiWindowMode
/config/UI/General/MultiWindowMode'
The value of this entry is '1' for “Compact View” and '0' for “Default View”
Check this demo which uses XPath to parse the file and read the value of the MultiWindowMode.
{$APPTYPE CONSOLE}
uses
ComObj,
ActiveX,
Variants,
SysUtils;
function SkypeISCompactView(const SettingsFile : string) : Boolean;
var
XmlDoc : OleVariant;
Node : OleVariant;
begin
Result:=False;
if FileExists(SettingsFile) then
begin
XmlDoc := CreateOleObject('Msxml2.DOMDocument.6.0');
try
XmlDoc.Async := False;
XmlDoc.Load(SettingsFile);
XmlDoc.SetProperty('SelectionLanguage','XPath');
if (XmlDoc.parseError.errorCode <> 0) then
raise Exception.CreateFmt('Error in Xml Data %s',[XmlDoc.parseError]);
Node :=XmlDoc.selectSingleNode('/config/UI/General/MultiWindowMode');
if not VarIsClear(Node) then
Result:=Node.text='1';
finally
XmlDoc:=Unassigned;
end;
end;
end;
begin
try
CoInitialize(nil);
try
Writeln(BoolToStr(SkypeISCompactView('C:\Users\<your windows user>\AppData\Roaming\Skype\<skype user>\config.xml'),True));
except
on E:Exception do
begin
Writeln(E.Classname, ':', E.Message);
end;
end;
finally
CoUninitialize;
end;
Readln;
end.
Related
I would like to create a non visual component (like TTimer for example) that I can drop on the form and that I can set up directly from the Object Inspector, but I don't want to see its icon on the form (it'd just obstruct anything). For example TFloatAnimation works like this but I don't understand how.
The GExperts library (http://www.gexperts.org/) has a plug-in which can toggle the visibility
of non-visual components on a form, and it is apparently not Delphi-version-specific but it is
not exactly trivial.
The method which does this is
procedure THideNonVisualCompsExpert.ToggleNonVisualVisible(Form: TCustomForm);
const
NonVisualClassName = 'TContainer';
var
VisibleState: Boolean;
FormHandle: THandle;
CompHandle: THandle;
WindowClass: string;
FirstCompFound: Boolean;
WinControl: TWinControl;
ChildControl: TWinControl;
i: Integer;
begin
Assert(Assigned(Form));
Assert(Form.Handle > 0);
FirstCompFound := False;
WinControl := Form;
if InheritsFromClass(WinControl.ClassType, 'TWinControlForm') then
begin
for i := WinControl.ComponentCount - 1 downto 0 do
begin
if WinControl.Controls[i] is TWinControl then
begin
ChildControl := WinControl.Controls[i] as TWinControl;
if InheritsFromClass(ChildControl.ClassType, 'TCustomFrame') then
begin
WinControl := ChildControl;
Break;
end;
end;
end;
end;
FormHandle := GetWindow(WinControl.Handle, GW_CHILD);
CompHandle := GetWindow(FormHandle, GW_HWNDLAST);
VisibleState := False;
GxOtaClearSelectionOnCurrentForm;
while (CompHandle <> 0) do
begin
WindowClass := GetWindowClassName(CompHandle);
if AnsiSameText(WindowClass, NonVisualClassName) then
begin
if not FirstCompFound then
begin
VisibleState := not IsWindowVisible(CompHandle);
FirstCompFound := True;
end;
if VisibleState then
ShowWindow(CompHandle, SW_SHOW)
else
ShowWindow(CompHandle, SW_HIDE);
end;
CompHandle := GetWindow(CompHandle, GW_HWNDPREV);
end;
end;
in the unit GX_HideNonVisualComps.Pas.
As written, it toggles the visibility of all the non-visual components on the
target form, but looking at the code of the ToggleNonVisualVisible method it looks like it
ought to be possible (but I have not tried) to adapt it to operate on a selected component class and
force instances of the class to a non-visible state. Once you have done that, you would probably
need to experiment with how and when to invoke the method at design-time; if I was doing it, I would probably start
with somewhere like the target component's Loaded method.
(I would feel more comfortable posting this "answer" as a comment but obviously it would be too long)
I have thought about this. A Non Visual Component does not do any painting, in a Windows environment (like the IDE) it has no Window, and therefore cannot influence how the IDE chooses to render it.
One approach would be to derive from TWinControl, making your component a Visual Component, and then to ensure that it is not drawn. Try setting the positioning properties to be non-published, and when you are parented, always set your position outside the parent window. This means that your control is always clipped and never painted.
I haven't tried this, but I can see no reason why it wouldn't work.
You can also use this approach to have an apparently non visual component that renders information in the IDE at designtime, but not at runtime.
I am looking to find out which services/interface I need to use to add an item to the right-click menu of a source file in the Delphi IDE.
For example, if I right-click on a unit's tab, it has items to "Close page", "Close all other pages", "Properties", etc. I want to add custom items to that menu, if possible.
I looked over the ToolsAPI unit but I have no clue where to begin. I assume there's an interface I can use to enumerate items and add items, but I dont know where to start looking.
If that's not possible, I'd settle for the code editor's context menu.
Maybe there's some samples online for this, but I'm still looking and have found none.
Any help appreciated.
Remy Lebeau has pointed you in exactly the right directions with his link to
the GExperts guide.
If you've not done this sort of stuff before, it can still
be a bit of a performance to get started on writing your own IDE add-in, so
I've set out below a minimal example of how to add an item to the code editor's
pop-up menu.
What you do, obviously, is to create a new package, add the unit below to it,
and then install the package in the IDE. The call to Register in the unit
does what's necessary to install the new item in the editor pop-up menu.
Make sure that the code editor is open at the time you install the package. The
reason is that, as you'll see, the code checks whether there is an active editor
at the time. I've left how to ensure that the pop-up item gets added even if there
is no code editor active at the time. Hint: if you look at the ToolsAPI.Pas unit for whichever
version of Delphi you're using, you'll find that it includes various kinds of notifier,
and you can use a notification from at least one of them to defer checking if there
is an editor active until one is likely to be.
Btw, the code adds the menu item to the context menu which pops up over the editor window itself rather than the active tab. Part of the fun with IDE add-ins is the fun of experimenting to see if you can get exactly what you want. I haven't tried it myself, but I doubt that adding the menu item to the context menu of one of the editor tabs would be that difficult - seeing as the Delphi IDE is a Delphi app, as you can see from the code below, you can use FindComponent (or just iterate over a Components collection) to find what you want. However, it is better, if you can, to locate things via the ToolsAPI interfaces. See Update below.
interface
uses
Classes, Windows, Menus, Dialogs, ToolsAPI;
type
TIDEMenuItem = class(TNotifierObject, IOTAWizard, IOTAMenuWizard)
function GetName: string;
function GetIDString: string;
function GetMenuText: string;
function GetState: TWizardState;
procedure Execute;
end;
TIDEMenuHandler = class(TObject)
procedure HandleClick(Sender: TObject);
end;
procedure Register;
implementation
var
MenuItem: TMenuItem;
IDEMenuHandler: TIDEMenuHandler;
EditorPopUpMenu : TPopUpMenu;
procedure TIDEMenuItem.Execute;
begin
ShowMessage('Execute');
end;
function TIDEMenuItem.GetIDString: string;
begin
Result := 'IDEMenuItemID';
end;
function TIDEMenuItem.GetMenuText: string;
begin
Result := 'IDEMenuItemText';
end;
function TIDEMenuItem.GetName: string;
begin
Result := 'IDEMenuItemName';
end;
function TIDEMenuItem.GetState: TWizardState;
begin
Result := [wsEnabled];
end;
procedure TIDEMenuHandler.HandleClick(Sender: TObject);
begin
ShowMessage(TIDEMenuItem(Sender).GetName + ' Clicked');
end;
procedure AddIDEMenu;
var
NTAServices: INTAServices40;
EditorServices: IOTAEditorServices;
EditView: IOTAEditView;
begin
NTAServices := BorlandIDEServices as INTAServices40;
EditorServices := BorlandIDEServices as IOTAEditorServices;
EditView := EditorServices.TopView;
if Assigned(EditView) then begin
EditorPopUpMenu := TPopUpMenu(EditView.GetEditWindow.Form.FindComponent('EditorLocalMenu'));
Assert(EditorPopUpMenu <>Nil);
IDEMenuHandler := TIDEMenuHandler.Create;
MenuItem := TMenuItem.Create(Nil);
MenuItem.Caption := 'Added IDE editor menu item';
MenuItem.OnClick := IDEMenuHandler.HandleClick;
EditorPopUpMenu.Items.Add(MenuItem)
end
else
ShowMessage('Code editor not active');
end;
procedure RemoveIDEMenu;
begin
if MenuItem <> Nil then begin
EditorPopUpMenu.Items.Remove(MenuItem);
FreeAndNil(MenuItem);
IDEMenuHandler.Free;
end;
end;
procedure Register;
begin
RegisterPackageWizard(TIDEMenuItem.Create);
AddIDEMenu;
end;
initialization
finalization
RemoveIDEMenu;
end.
Update: The following code finds the TabControl of the editor window and adds the menu item to its context menu. However, note that it does not account for there being a second editor window open.
procedure AddIDEMenu;
var
NTAServices: INTAServices40;
EditorServices: IOTAEditorServices;
EditView: IOTAEditView;
TabControl : TTabControl;
function FindTabControl(AComponent : TComponent) : TTabControl;
var
i : Integer;
begin
Result := Nil;
if CompareText(AComponent.ClassName, 'TXTabControl') = 0 then begin
Result := TTabControl(AComponent);
exit;
end
else begin
for i := 0 to AComponent.ComponentCount - 1 do begin
if CompareText(AComponent.Components[i].ClassName, 'TXTabControl') = 0 then begin
Result := TTabControl(AComponent.Components[i]);
exit;
end
else begin
Result := FindTabControl(AComponent.Components[i]);
if Result <> Nil then
exit;
end;
end;
end;
end;
begin
NTAServices := BorlandIDEServices as INTAServices40;
EditorServices := BorlandIDEServices as IOTAEditorServices;
EditView := EditorServices.TopView;
if Assigned(EditView) then begin
TabControl := FindTabControl(EditView.GetEditWindow.Form);
Assert(TabControl <> Nil, 'TabControl not found');
EditorPopUpMenu := TabControl.PopupMenu;
Assert(EditorPopUpMenu <> Nil, 'PopUP menu not found');
//EditorPopUpMenu := TPopUpMenu(EditView.GetEditWindow.Form.FindComponent('EditorLocalMenu'));
Assert(EditorPopUpMenu <>Nil);
IDEMenuHandler := TIDEMenuHandler.Create;
MenuItem := TMenuItem.Create(Nil);
MenuItem.Caption := 'Added IDE editor menu item';
MenuItem.OnClick := IDEMenuHandler.HandleClick;
EditorPopUpMenu.Items.Add(MenuItem)
end
else
ShowMessage('No editor active');
end;
Assuming I have the Delphi IDE open, how can I open a .pas file selected in another app and open it in the Delphi IDE, as well as positioning it to a specific line number?
I've seen some editing tools do this.
I'm not sure if it's just an option to a normal file open (eg., using default file association), or a command-line option, or you need DDE or COM or something entirely different.
Note that I don't want to close the project and reopen a new or fake project.
Also, I don't want the file added to the project. I just want to open it.
For example, When you <ctrl>-click on a varible or type, the IDE will open the file containing that symbol and go to the line where that symbol is declared. That's all I want to do -- but from an external app. (I'm not looking for a symbol, just a line.)
I'm using Delphi XE5 at the moment, so I'm interested in newer Delphi versions, not pre-XE2 or so.
(Part of the question is, how do I ensure that if the IDE is already open, the the file is opened in anew tab inside of the current IDE rather than in another instance of the IDE?)
The code below (for D7) shows how this can be done by way of an IDE add-in .Dpk compiled
into a Bpl. It started as just a "proof of concept", but it does actually work.
It comprises a "sender" application which uses WM_COPYDATA to send the FileName, LineNo & Column to a receiver hosted in the .Bpl file.
The sender sends the receiver a string like
Filename=d:\aaad7\ota\dskfilesu.pas
Line=8
Col=12
Comment=(* some comment or other*)
The Comment line is optional.
In the .Bpl, the receiver uses OTA services to open the requested file and positions the editor caret, then inserts the comment, if any.
The trickiest thing was to find out how to handle one particular complication, the case where the named file to be opened is one with an associated form. If so, in D7 (and, I assume, other IDE versions with the floating designer option enabled) when the IDE
opens the .Pas file, it also opens the .Dfm, and left to its own devices, that would leave the form editor in front of the code editor. Calling the IOTASourceEditor.Show for the .Pas file at least puts the IDE code editor in front of the .Dfm form, but that didn't satisfy me, because by now my curiosity was piqued - how do you get a form the IDE is displaying off the screen?
I spent a lot of time exploring various blind alleys, because the OTA + NTA services don't seem to provide any way to explicitly close an IOTAEditor or any of its descendants. In the end it turned out that the thing to do is simply get a reference to the form and just send it a WM_CLOSE(!) - see comments in the code.
Fwiw, being a novice at OTA, at first (before I found out how IOTAModules work) I found that far and away the most difficult part of this was discovering how to get hold of the IEditView interface needed to set the editor caret position, but as usual with these interfacey things, once you get the "magic spell" exactly right, it all works.
Good luck! And thanks for the fascinating challenge!
unit Receiveru;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI;
type
TOTAEditPosnForm = class(TForm)
Memo1: TMemo;
private
FEdLine: Integer;
FEdCol: Integer;
FEditorFileName: String;
FEditorInsert: String;
procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
procedure HandleCopyDataString(CopyDataStruct : PCopyDataStruct);
procedure OpenInIDEEditor;
property EditorFileName : String read FEditorFileName write FEditorFileName;
property EdLine : Integer read FEdLine write FEdLine;
property EdCol : Integer read FEdCol write FEdCol;
property EditorInsert : String read FEditorInsert write FEditorInsert;
end;
var
OTAEditPosnForm: TOTAEditPosnForm;
procedure Register;
implementation
{$R *.dfm}
procedure MonitorFiles;
begin
OTAEditPosnForm := TOTAEditPosnForm.Create(Nil);
OTAEditPosnForm.Show;
end;
procedure Register;
begin
MonitorFiles;
end;
procedure TOTAEditPosnForm.OpenInIDEEditor;
var
IServices : IOTAServices;
IActionServices : IOTAActionServices;
IModuleServices : IOTAModuleServices;
IEditorServices : IOTAEditorServices60;
IModule : IOTAModule;
i : Integer;
IEditor : IOTAEditor;
ISourceEditor : IOTASourceEditor;
IFormEditor : IOTAFormEditor;
IComponent : IOTAComponent;
INTAComp : INTAComponent;
AForm : TForm;
IEditView : IOTAEditView;
CursorPos : TOTAEditPos;
IEditWriter : IOTAEditWriter;
CharPos : TOTACharPos;
InsertPos : Longint;
FileName : String;
begin
IServices := BorlandIDEServices as IOTAServices;
Assert(Assigned(IServices), 'IOTAServices not available');
IServices.QueryInterface(IOTAACtionServices, IActionServices);
if IActionServices <> Nil then begin
IServices.QueryInterface(IOTAModuleServices, IModuleServices);
Assert(IModuleServices <> Nil);
// Close all files open in the IDE
IModuleServices.CloseAll;
if IActionServices.OpenFile(EditorFileName) then begin
// At this point, if the named file has an associated .DFM and
// we stopped here, the form designer would be in front of the
// code editor.
IModule := IModuleServices.Modules[0];
// IModule is the one holding our .Pas file and its .Dfm, if any
// So, iterate the IModule's editors until we find the one
// for the .Pas file and then call .Show on it. This will
// bring the code editor in front of the form editor.
ISourceEditor := Nil;
for i := 0 to IModule.ModuleFileCount - 1 do begin
IEditor := IModule.ModuleFileEditors[i];
FileName := IEditor.FileName;
Memo1.Lines.Add(Format('%d %s', [i, FileName]));
if CompareText(ExtractFileExt(IEditor.FileName), '.Pas') = 0 then begin
if ISourceEditor = Nil then begin
IEditor.QueryInterface(IOTASourceEditor, ISourceEditor);
IEditor.Show;
end
end
else begin
// Maybe the editor is a Form Editor. If it is
// close the form (the counterpart to the .Pas, that is}
IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
if IFormEditor <> Nil then begin
IComponent := IFormEditor.GetRootComponent;
IComponent.QueryInterface(INTAComponent, INTAComp);
AForm := TForm(INTAComp.GetComponent);
//AForm.Close; < this does NOT close the on-screen form
// IActionServices.CloseFile(IEditor.FileName); <- neither does this
SendMessage(AForm.Handle, WM_Close, 0, 0); // But this does !
end;
end;
end;
// Next, place the editor caret where we want it ...
IServices.QueryInterface(IOTAEditorServices, IEditorServices);
Assert(IEditorServices <> Nil);
IEditView := IEditorServices.TopView;
Assert(IEditView <> Nil);
CursorPos.Line := edLine;
CursorPos.Col := edCol;
IEditView.SetCursorPos(CursorPos);
// and scroll the IEditView to the caret
IEditView.MoveViewToCursor;
// Finally, insert the comment, if any
if EditorInsert <> '' then begin
Assert(ISourceEditor <> Nil);
IEditView.ConvertPos(True, CursorPos, CharPos);
InsertPos := IEditView.CharPosToPos(CharPos);
IEditWriter := ISourceEditor.CreateUndoableWriter;
Assert(IEditWriter <> Nil, 'IEditWriter');
IEditWriter.CopyTo(InsertPos);
IEditWriter.Insert(PChar(EditorInsert));
IEditWriter := Nil;
end;
end;
end;
end;
procedure TOTAEditPosnForm.HandleCopyDataString(
CopyDataStruct: PCopyDataStruct);
begin
Memo1.Lines.Text := PChar(CopyDataStruct.lpData);
EditorFileName := Memo1.Lines.Values['FileName'];
edLine := StrToInt(Memo1.Lines.Values['Line']);
edCol := StrToInt(Memo1.Lines.Values['Col']);
EditorInsert := Trim(Memo1.Lines.Values['Comment']);
if EditorFileName <> '' then
OpenInIDEEditor;
end;
procedure TOTAEditPosnForm.WMCopyData(var Msg: TWMCopyData);
begin
HandleCopyDataString(Msg.CopyDataStruct);
msg.Result := Length(Memo1.Lines.Text);
end;
initialization
finalization
if Assigned(OTAEditPosnForm) then begin
OTAEditPosnForm.Close;
FreeAndNil(OTAEditPosnForm);
end;
end.
Code for sender:
procedure TSenderMainForm.btnSendClick(Sender: TObject);
begin
SendMemo;
end;
procedure TSenderMainForm.SendData(
CopyDataStruct: TCopyDataStruct);
var
HReceiver : THandle;
Res : integer;
begin
HReceiver := FindWindow(PChar('TOTAEditPosnForm'),PChar('OTAEditPosnForm'));
if HReceiver = 0 then begin
Caption := 'CopyData Receiver NOT found!';
end
else begin
Res := SendMessage(HReceiver, WM_COPYDATA, Integer(Handle), Integer(#CopyDataStruct));
if Res > 0 then
Caption := Format('Received %d characters', [Res]);
end;
end;
procedure TSenderMainForm.SendMemo;
var
MS : TMemoryStream;
CopyDataStruct : TCopyDataStruct;
S : String;
begin
MS := TMemoryStream.Create;
try
S := Memo1.Lines.Text + #0;
MS.Write(S[1], Length(S));
CopyDataStruct.dwData := 1;
CopyDataStruct.cbData := MS.Size;
CopyDataStruct.lpData := MS.Memory;
SendData(CopyDataStruct);
finally
MS.Free;
end;
end;
Whenever Skype is in Default View, the TConversationWindow's become children of the tSkMainForm Window.
I am having problems finding out which TConversationWindow is active - however it's not like an MDI interface - only one TConversationWindow is visible, like if it was a Tab/Page.
When I do GetForegroundWindow, Skype's MainForm handle is returned (tSkMainForm). Is there any way that I can get the foreground TConversationWindow within Skype?
This question of mine has screenshots of Skype's Default View, if you need it. :)
EDIT: Here is a screenshot of the Winspector Hierachy:
EDIT2: I tried going thru the windows like this:
procedure TForm1.Button1Click(Sender: TObject);
function GetClassName(Handle: HWND): String;
var
Buffer: array[0..MAX_PATH] of Char;
begin
Windows.GetClassName(Handle, #Buffer, MAX_PATH);
Result := String(Buffer);
end;
Var
Wnd: HWND;
SkypeWnd: HWND;
begin
SkypeWnd := FindWindow('tSkMainForm',nil);
Wnd := GetTopWindow(SkypeWnd);
while (GetClassName(Wnd) <> 'TConversationForm') and (Wnd <> 0) and (not IsWindowVisible(Wnd)) do
begin
Wnd := GetNextWindow(Wnd,GW_HWNDNEXT);
end;
Label1.Caption := GetClassName(Wnd)+' - '+GetHandleText(wnd);
end;
The above is supposed to find the visible window, however when I debug it, it never enters the Begin End within the While loop, and Label1 displays "TChromeMenu - ChromeToolbar". When I remove the IsWindowVisible check, it atleast finds a TConversationForm. What am I doing wrong?
EDIT3: By placing the IsWindowVisible and getClassName check inside the loop, and break when true, I managed to do it. :)
By placing the IsWindowVisible and getClassName check inside the loop, and break when true, I managed to do it. :)
I have a form one which I want to show a file open dialog box before the full form opens.
I already found that I can't do UI related stuff in FormShow, but it seems that I can in FormActivate (which I protect from being called a second time...)
However, if the user cancels out of the file open dialog, I want to close the form without proceeding.
But, a form close in the activate event handler generates an error that I can't change the visibility of the form.
So how does one do some UI related operation during form start up and then perhaps abort the form (or am I trying to stuff a function into the form that should be in another form?)
TIA
It would be best (i think) to show the file open dialog BEFORE you create and show the form. If you want to keep all code together you might add a public class procedure OpenForm() or something:
class procedure TForm1.OpenForm( ... );
var
O: TOpenDialog;
F: TForm1;
begin
O := TOpenDialog.Create();
try
// set O properties.
if not O.Execute then Exit
F := TForm1.Create( nil );
try
F.Filename := O.FIlename;
F.ShowModal();
finally
F.Free();
end;
finally
O.Free();
end;
end;
Set a variable as a condition of the opendialog and close the form on the formshow event if the flag is not set correctly.
procedure TForm1.FormCreate(Sender: TObject);
begin
ToClose := not OpenDialog1.Execute;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if ToClose then Close();
end;
or even more simply
procedure TForm1.FormShow(Sender: TObject);
begin
if not OpenDialog1.Execute then Close();
end;
If you want to keep the logic conditioning the opening self-contained in the Form, you can put a TOpenDialog in your Form and use a code like this in your OnShow event:
procedure TForm2.FormShow(Sender: TObject);
begin
if OpenDialog1.Execute(Handle) then
Color := clBlue
else
PostMessage(Handle, WM_CLOSE, 0, 0); // NB: to avoid any visual glitch use AlpaBlend
end;
If you don't need this encapsulation, a better alternative can be to check the condition before trying to show the form, for instance by embedding the Form2.Show call in a function that tests all the required conditions first.
Two Ways....
1. using oncreate and onactivate
create a global flag or even 2
var
aInitialized:boolean;
Set the flag to false in the oncreate handler.
aInitialized := false; //we have not performed our special code yet.
Inside onActivate have something like this
if not aInitialized then
begin
//our one time init code. special stuff or whatever
If successful
then set aInitialized := true
else aInitialized := false
end;
And how to close it without showing anything just add your terminate to the formshow. of course you need to test for some reason to close.. :)
Procedure Tmaindlg.FormShow(Sender: TObject);
Begin
If (shareware1.Sharestatus = ssExpired) or (shareware1.Sharestatus = ssTampered) Then
application.Terminate;
End;
In your DPR you will need to add a splash screen type effect. In my case I am showing progress as the application starts. You could also just show the form and get some data.
Code from the splash.pas
Procedure tsplashform.bumpit(str: string);
Begin
label2.Caption := str;
gauge1.progress := gauge1.progress + trunc(100 / items);
update;
If gauge1.progress >= items * (trunc(100 / items)) Then Close;
End;
Program Billing;
uses
Forms,
main in 'main.pas' {maindlg},
Splash in 'splash.pas' {splashform};
{$R *.RES}
Begin
Application.Initialize;
Application.Title := 'Billing Manager';
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update;
splash.items := 5;
SplashForm.bumpit('Loading Main...');
Application.CreateForm(Tmaindlg, maindlg);
SplashForm.bumpit('Loading Datamodule...');
Application.CreateForm(TfrmSingleWorkorder, frmSingleWorkorder);
SplashForm.bumpit('Loading SQL Builder...');
Application.CreateForm(TDm, Dm);
SplashForm.bumpit('Loading Security...');
Application.CreateForm(TSQLForm, SQLForm);
SplashForm.bumpit('Loading Reports...');
Application.CreateForm(Tpickrptdlg, pickrptdlg);
Application.Run;
End.