How to enter Windows Flip 3D mode on Windows Vista and above? - windows

Is it possible to trigger the Flip 3D mode on Windows Vista above systems programmatically?
It is the same as if you manually press CTRL + WIN + TAB

The Shell object has the WindowSwitcher method which can invoke this mode.
Here is the Delphi code example:
uses
ComObj;
procedure EnterWindowSwitcherMode;
var
Shell: OleVariant;
begin
try
Shell := CreateOleObject('Shell.Application');
Shell.WindowSwitcher;
finally
Shell := Unassigned;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Win32MajorVersion >= 6 then // are we at least on Windows Vista ?
begin
try
EnterWindowSwitcherMode;
except
on E: Exception do
ShowMessage(E.ClassName + ': ' + E.Message);
end;
end;
end;
Update:
Or as Norbert Willhelm mentioned here, there is also IShellDispatch5 object interface which in fact introduces the WindowSwitcher method. So here's another version of the same...
The following piece of code requires the Shell32_TLB.pas unit, which you can in Delphi create this way (note, that you must have at least Windows Vista where the IShellDispatch5 interface was used the first time):
go to menu Component / Import Component
continue with selected Import a Type Library
select Microsoft Shell Controls And Automation and finish the wizard
And the code:
uses
Shell32_TLB;
procedure EnterWindowSwitcherMode;
var
// on Windows Vista and Windows 7 (at this time :)
// is Shell declared as IShellDispatch5 object interface
AShell: Shell;
begin
try
AShell := CoShell.Create;
AShell.WindowSwitcher;
finally
AShell := nil;
end;
end;

Related

Inno Setup: Overwrite existing installation or show dir prompt

It would be nice if I have this in my setup:
If there is no previous installation, then an edit field for the destination directory should be shown.
If there is a previous installation, than the user should should be asked if he wants to overwrite the existing installation (no directory
prompt should be visible) or if he wants to install this version in a
different directory as a separate installation (two entries in the uninstall list). If this option is chosen, than the edit field
for the destination directory should be shown.
Is it possible to use existing Inno Setup options to achieve this? Or do I have to build a custom dialog page?
At the beginning (InitializeSetup event function), check if the application is installed already (see GetUninstallString in the code below). If it is, ask user, what to do (see MsgBox use in the code and the first screenshot). If user chooses to update the existing installation, proceed normally. Inno Setup by default does not allow changing installation path of an existing installation (see DisableDirPage).
If uses chooses to install another copy, set AppId to a new unique value (GetAppId function in the code). This will make Inno Setup treat the installation as new, so it will prompt for the installation path. Update also UninstallDisplayName, so that the user can distinguish the installations when choosing which copy to uninstall (see GetAppIdentification and the third screenshot). Also update DefaultDirName to a new unique path (see GetAppIdentification and the third screenshot).
#define AppName "My Program"
#define AppVersion "1.5"
[Setup]
AppId={code:GetAppId}
AppName={#AppName}
AppVersion={#AppVersion}
UninstallDisplayName={#AppName} {#AppVersion}{code:GetAppIdentification}
UsePreviousLanguage=no # Needed when AppId is dynamic
DefaultDirName={autopf}\My Program{code:GetAppIdentification}
[Code]
var
Instance: string;
function GetAppId(Param: string): string;
begin
Result := '{#AppName}' + Instance;
end;
function GetAppIdentification(Param: string): string;
begin
if Instance <> '' then Result := ' (' + Instance + ')';
end;
function GetUninstallString(): string;
var
UninstallKey: string;
begin
UninstallKey :=
'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + GetAppId('') + '_is1';
RegQueryStringValue(HKA, UninstallKey, 'UninstallString', Result);
Log(Result)
end;
function InitializeSetup(): Boolean;
var
Message: string;
Answer: Integer;
begin
Result := True;
if GetUninstallString() = '' then
begin
Log('Application is not installed yed, installing the first copy');
end
else
begin
Log('Application is installed already, asking what to do');
Message :=
'This program is installed already, ' +
'do you want to update the existing installation? ' +
'Press No to install another copy of the program';
Answer := MsgBox(Message, mbConfirmation, MB_YESNOCANCEL);
if Answer = IDYES then
begin
Log('User chose to update the installation');
end
else
if Answer = IDNO then
begin
Log('User chose to install another copy');
Instance := '2';
end
else
begin
Log('User chose to abort the installation');
Result := False;
end;
end;
end;
Now the question is what to do if there are already two installations. To make a third (or more), it's easy, just loop, increasing the value in Instance, until GetUninstallString returns an empty string. But had you wanted the user to be able to choose what copy to update, it would be more difficult. That's too much for one question.
What you want to do is quite complicated. If you want to keep the flexibility, I think that the easiest solution is to treat every new version as a separate software. In addition, when starting the installation, as a courtesy to those who want to keep the latest version only, offer to uninstall the previous (latest) installation automatically. If the user already has multiple installations, do nothing specific (or just inform the user).
I solved this problem by putting the responsibility on the user (the one installing the application) to explicitly specify that they want to install a separate instance of the application by specifying a /instancename parameter on the installer's command line (the AppId directive uses a scripted constant).

How do I run an external application with Free Pascal/Lazarus?

How do I run an external application with Free Pascal/Lazarus (using Windows)? I found the "official" reference page, with several implementations and examples. Although I'm sure it works for many people, I, with my current knowledge level, am some what lost (I don't have much routine programming with Free Pascal yet, and other examples I found on the web didn't work for me).
Is there a "clear" example that helps me to do the "first steps"? Thanks.
If you don't need piping you can just use execute process.
uses sysutils;
begin
executeprocess('notepad.exe',['document.txt']);
end.
Here's a working example (source) using TProcess:
uses Process;
var
RunProgram: TProcess;
begin
RunProgram := TProcess.Create(nil);
RunProgram.CommandLine := ‘Path and Name of Program’;
RunProgram.Execute;
RunProgram.Free;
end;
For example, this will open the application "MS Notepad":
uses Process;
var
RunProgram: TProcess;
begin
RunProgram := TProcess.Create(nil);
RunProgram.CommandLine := ‘notepad.exe’;
RunProgram.Execute;
RunProgram.Free;
end;

Open file in OS X

In Delphi, I would like to open a file in OS X. My approach is as follows:
const
Filename = 'test.bmp';
procedure SaveAndOpen;
begin
Chart.SaveToBitmapFile(Filename);
{$IFDEF MSWINDOWS}
ShellExecute(0, 'open', Filename, '', '', SW_Normal);
{$ELSE}
_System(Filename);
{$ENDIF}
end;
But nothing happens. What am I doing wrong?
This article from Embarcadero's Malcolm Groves covers this topic: Opening files and URLs in default applications in OS X.
In summary, all you need is this:
uses
Macapi.Appkit, // for NSWorkspace
Macapi.Foundation; // for NSSTR
....
var
Workspace: NSWorkspace; // interface, no need for explicit destruction
....
Workspace := TNSWorkspace.Create;
Workspace.openFile(NSSTR(FileName));
For sake of completeness, should you wish to open a URL rather than a file, then you call openURL instead:
Workspace.openURL(NSSTR(URL));
Regarding your Windows code, I would recommend not using ShellExecute. That function does not have reasonable error reporting. Use ShellExecuteEx in its place.
And finally, you should probably abstract this functionality away so that it can be re-used by other parts of your program. You want to write that IFDEF as few times as possible.
You must add the open verb like so
_System(PAnsiChar('open ' + Filename));

How to get the sort order in Delphi as in Windows Explorer?

Summarization:
The terminology that I have been
looking for seems to be "natural
sort".
For behaviors in operating systems:
For Windows (version >= XP), Windows Explorer utilizes natural
sort.
For Linux terminals: use "ls -v" instead of plain "ls" to get natural
sort.
For programing in Delphi, use StrCmpLogicalW Windows API to get natural sort.
For programing in Delphi & Kylix & Lazarus, use hand-crafted functions to get
natural sort:
(1) Delphi wrapper for Natural Order String Comparison by Martin Pool.
http://irsoft.de/web/strnatcmp-and-natsort-for-delphi
(2) Codes of alphanum sorting algorithm in other languages from davekeolle site.
http://www.davekoelle.com/alphanum.html
(3) Other knowledgable pages:
http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html
http://objectmix.com/delphi/722211-natural-sorting-optimizing-working-solution.html
http://groups.google.com/group/borland.public.delphi.language.delphi.general/browse_thread/thread/1141d49f8bbba577
http://objectmix.com/delphi/401713-alphanumeric-sort-routine-delphi.html
==========================
The following file names will be ordered in the Windows Explorer as shown below:
test_1_test.txt
test_2_test.txt
test_11_test.txt
test_12_test.txt
test_21_test.txt
test_22_test.txt
If, for example, I put them in a TStringList instance and call Sort, the sorted order is as below:
test_1_test.txt
test_11_test.txt
test_12_test.txt
test_2_test.txt
test_21_test.txt
test_22_test.txt
And for record, the above file names will be ordered in the rxvt terminal of Cygwin or xterm terminal of Linux distributions such as CentOS as shown below:
test_11_test.txt
test_12_test.txt
test_1_test.txt
test_21_test.txt
test_22_test.txt
test_2_test.txt
Could you help to comment on how to understand this difference of sorting behaviors? Furthermore, is it possible to get the same order as in Windows Explorer? Any suggestion is appreciated!
PS: My Windows locale is set to Chinese but I would think the same for English locale.
StrCmpLogicalW is able to handle numbers, the other alternative is CompareString
Thanks to Anders - the answer is StrCmpLogicalW; I have not found it's declaration in Delphi 2009 sources, so I declared it myself in the test below:
type
TMyStringList = class(TStringList)
protected
function CompareStrings(const S1, S2: string): Integer; override;
end;
function StrCmpLogicalW(P1, P2: PWideChar): Integer; stdcall; external 'Shlwapi.dll';
function TMyStringList.CompareStrings(const S1, S2: string): Integer;
begin
Result:= StrCmpLogicalW(PChar(S1), PChar(S2));
end;
procedure TForm11.Button2Click(Sender: TObject);
var
SL: TMyStringList;
begin
SL:= TMyStringList.Create;
try
SL.Add('test_1_test.txt');
SL.Add('test_11_test.txt');
SL.Add('test_12_test.txt');
SL.Add('test_2_test.txt');
SL.Add('test_21_test.txt');
SL.Add('test_22_test.txt');
SL.Sort;
Memo1.Lines:= SL;
finally
SL.Free;
end;
end;

Do Windows shortcuts support very long argument lengths?

I am trying to create a shortcut (on the Desktop) that contains a long argument string (> MAX_PATH).
The MSDN documentation clearly states that for Unicode string the string can be longer than MAX_PATH.
The resulting shortcut is cut exactly after MAX_PATH characters (that is the Path + the Arguments).
Is there something wrong with my implementation or is this some Windows limitation?
procedure CreateShortcut(APath: WideString;
AWorkingDirectory: WideString; AArguments: WideString; ADescription: WideString;
ALinkFileName: WideString);
var
IObject : IUnknown;
ISLink : IShellLinkW;
IPFile : IPersistFile;
begin
IObject := CreateComObject(CLSID_ShellLink);
ISLink := IObject as IShellLinkW;
ISLink.SetPath( PWideChar(APath));
ISLink.SetWorkingDirectory(PWideChar(AWorkingDirectory));
ISLink.SetArguments( PWideChar(AArguments));
ISLink.SetDescription( PWideChar(ADescription));
IPFile := IObject as IPersistFile;
IPFile.Save(PWideChar(ALinkFileName), False);
end;
PS: OS is Windows XP (and above).
It turns out that this issue is in fact solely a limitation in the Explorer shell dialog. The generated shortcut file does not have a 260 character limitation. It's simply that the dialog refuse to display a Target with more characters than that. Presumably it calls GetPath with a fixed length buffer.
procedure TForm11.Button1Click(Sender: TObject);
var
sl: IShellLinkW;
pf: IPersistFile;
begin
CoCreateInstance(CLSID_ShellLink, nil,
CLSCTX_INPROC_SERVER, IID_IShellLinkW, sl);
sl.SetPath('c:\desktop\test.bat');
sl.SetWorkingDirectory('c:\desktop\');
sl.SetArguments(PChar(StringOfChar('x', 300)+'_the_end'));
pf := sl as IPersistFile;
pf.Save('c:\desktop\test.lnk', False);
end;
My test.bat looks like this:
echo %1> test.out
The resulting test.out goes right the way to _the_end!
Thanks all who contributed to this thread - it helped me immensely.
However, if I may, I would like to add the below information I discovered in crafting my solution:
On Windows 7 Enterprise ~SP1, it would seem that using VBS to create the shortcut there is still a limit on maximum characters in (at least) the arguments field. I tested up to 1023 chars before it got trunicated. I presume the same limit would apply to the Delphi method likewise.
On Windows XP Professional ~SP3, while the VBS method will create a shortcut longer than 260 characters (lnk file contains the data), it seems to trunicate it at about this number when executing it.

Resources