If you have a non-themed, non-Unicode VCL application with an TEdit "TestEdit" and set TestEdit.Font.Charset to RUSSIAN_CHARSET TestEdit displays cyrillic characters. If however you switch the app to use theming this doesn't work anymore. Try the following to see this:
Create a new VCL app.
Close the default Unit1 without saving.
Replace the project source code (Project1.pas) with the code at the bottom of this posting and save as CharsetTest.pas.
Uncheck runtime theming in the project options.
Run the program, click the radio buttons, watch the edit box' font.
Now check runtime theming in the project options or add XPMan to the uses clause.
Repeat step 5.
My question is: Is there a way to make the app honor the charset even when themed? (Without switching to Unicode.)
program CharsetTest;
uses
Windows,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
StdCtrls,
ExtCtrls;
{$R *.res}
type
TForm1 = class(TForm)
private
CharsetRadioGroup: TRadioGroup;
TestEdit: TEdit;
procedure CharsetRadioGroupClick(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
end;
constructor TForm1.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
BorderWidth := 8;
Caption := 'Charset Test';
ClientHeight := 180;
ClientWidth := 250;
CharsetRadioGroup := TRadioGroup.Create(Self);
CharsetRadioGroup.Name := 'CharsetRadioGroup';
CharsetRadioGroup.Height := 105;
CharsetRadioGroup.Align := alTop;
CharsetRadioGroup.Caption := 'Charset';
CharsetRadioGroup.Parent := Self;
CharsetRadioGroup.Items.Add('Default');
CharsetRadioGroup.Items.Add('Russian');
CharsetRadioGroup.Items.Add('Greek');
CharsetRadioGroup.OnClick := CharsetRadioGroupClick;
TestEdit := TEdit.Create(Self);
TestEdit.Name := 'TestEdit';
TestEdit.Align := alBottom;
TestEdit.Font.Size := 20;
TestEdit.Font.Name := 'Courier New';
TestEdit.Text := 'äöüÄÖÜß';
TestEdit.Parent := Self;
CharsetRadioGroup.ItemIndex := 1;
end;
procedure TForm1.CharsetRadioGroupClick(Sender: TObject);
begin
case CharsetRadioGroup.ItemIndex of
0:
TestEdit.Font.Charset := DEFAULT_CHARSET;
1:
TestEdit.Font.Charset := RUSSIAN_CHARSET;
2:
TestEdit.Font.Charset := GREEK_CHARSET;
end;
end;
var
Form1: TForm1;
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Not a direct answer, but you can use the TMS Unicode Controls to add Unicode support for just the edits, and leave the rest of your application as-is. We did that a few years back to get support in a single combobox, and the overhead wasn't bad.
The original TNT Unicode library that the TMS pack was based on is available here, but TMS isn't expensive, and they've made a bunch of improvements since they bought it.
This seems to be an issue with the windows edit control:
Edit Controls and Charset and XP
XPMan charset problem (try 2)
Until we upgrade to a recent (read "Unicode enabled") Delphi some of our customers will have to live without themes.
Related
The address book sample displays Arabic characters messed up, like that س ا ش ص. I tried to change the font of the text item to "Arial" or "Courier New", but the result is the same. Is there a way to display non-English characters correctly in TListView?
Loading contacts code:
procedure TForm1.FillContactList(Source: TAddressBookSource);
var
I: Integer;
Contacts: TAddressBookContacts;
begin
Contacts := TAddressBookContacts.Create;
try
AddressBook1.AllContacts(Source, Contacts);
ListViewContacts.BeginUpdate;
try
ListViewContacts.Items.Clear;
for I := 0 to Contacts.Count - 1 do
AddListViewItem(Contacts.Items[I]);
finally
ListViewContacts.EndUpdate;
end;
finally
Contacts.Free;
end;
end;
procedure TForm1.AddListViewItem(Contact: TAddressBookContact);
var
ListViewItem: TListViewItem;
begin
ListViewItem := ListViewContacts.Items.Add;
ListViewItem.Text := Contact.DisplayName;
ListViewItem.Tag := Contact.ID;
end;
Skia indeed solved the problem. More Details:
https://github.com/skia4delphi/skia4delphi#right-to-left
Right-to-Left
Using Skia's Canvas, your application will now support Right-To-Left text rendering. But for that you will need to make 3 changes to your project:
Open the source of your Delphi Application Project (.dpr), include the line Application.BiDiMode := TBiDiMode.bdRightToLeft;, like below:
program Project1;
uses
System.StartUpCopy,
FMX.Forms,
System.Classes,
Skia.FMX,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.BiDiMode := TBiDiMode.bdRightToLeft;
GlobalUseSkia := True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Set the property BiDiMode of your forms to bdRightToLeft;
Keyboard input controls like TEdit and TMemo, need to be fixed by Embarcadero, meanwhile, as a workaround, set the ControlType property of these controls to Platform.
Using Delphi Seattle in W7 x64. When using a custom VCL style and you select a large amount of files (like 2-3k+), the filenames are corrupted. Without a custom style this doesn't happen.
program Project1;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1},
Vcl.Themes,
Vcl.Styles;
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
TStyleManager.TrySetStyle('Onyx Blue');
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
procedure TForm1.FormCreate(Sender: TObject);
Var s: string;
ts: TStringList;
begin
if OpenDialog1.Execute then begin
ts := TStringList.Create;
for s in OpenDialog1.Files do
ts.Add(s);
end else Exit;
ts.SaveToFile('z:\files.txt');
ts.Free;
end;
object OpenDialog1: TOpenDialog
Filter = 'Pictures (jpg,png,bmp,gif)|*.jpg;*.png;*.bmp;*.gif|All Files|*.*'
Options = [ofReadOnly, ofAllowMultiSelect, ofEnableSizing, ofForceShowHidden]
Title = 'Select files to upload'
Left = 201
Top = 64
end
On my end, this code results in only 769 files written to the log out of ~5000, and their initial path "z:" gets corrupted with other characters "?"
Any way to fix this?
Note: the new TFileOpenDialog does not seem to have this issue, however it does bring others as shown here: (besides being Vista+)
You are experimenting this behavior because the size of the buffer passed to the Classic Open Dialog Box to retrieve the results of the selected file(s), the buffer is represented by the lpstrFile element which is part of the OPENFILENAME structure. The VCL internally set the size of this buffer to High(Word) - 16 (65519) bytes when the multi-select option is enabled and a MAX_PATH buffer size when the single file selection is enabled.
This is part of the VCL code (Vcl.Dialogs) which show this
function TOpenDialog.DoExecute(Func: Pointer; ParentWnd: HWND): Bool;
const
MultiSelectBufferSize = High(Word) - 16;
...
...
if ofAllowMultiSelect in FOptions then
nMaxFile := MultiSelectBufferSize else
nMaxFile := MAX_PATH;
SetLength(TempFilename, nMaxFile + 2);
lpstrFile := PChar(TempFilename);
...
...
Starting with Windows Vista and the introduction of the New Dialogs (IFileOpenDialog), the selected files are returned by the IFileOpenDialog::GetResults method in a IShellItemArray, So the buffer limitation doesn't apply.
In summary this behavior is not caused by the VCL Styles.
Finally if you want use the modern dialogs with a custom style try the VCL Styles Utils project.
That is not about Delphi it seems.
Just five seconds in Google and we have this:
" File Open dialog cannot return more than 1000 files "
http://answers.microsoft.com/en-us/windows/forum/windows_7-files/shameful-limitation-file-open-dialog-cannot-return/bfff43fc-0da5-48d6-8703-dc0eac3c7581?auth=1
How to add support of HTML help files (.chm) on Delphi XE2? We need to use A-links (A-keywords) on HelpContext property of every control to lookup help pages. Delphi XE2 has native support of HTML help files by unit HTMLHelpViewer. But how to use it?
It's not hard with F1 jump to a context.
Select Edit1 and press F1 . Help opens and Overview.htm is shown.
Prerequisite.
Edit1 Help settings:
sample.chm source settings.
sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm
sample.h
#define IDH_Creating_Projects_and_Topics 1005
#define IDH_Overview 1003
#define IDH_UsingtheMenus 1009
Unit1.pas
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, HTMLHelpViewer, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
HHALINKLOOKUP: TButton;
JumpAnchor: TButton;
Edit1: TEdit;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure HHALINKLOOKUPClick(Sender: TObject);
procedure JumpAnchorClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
hpPath : string;
link : HH_AKLINK;
procedure TForm1.FormCreate(Sender: TObject);
begin
hpPath := ExtractFilePath(Application.ExeName) +
'HelpFile\sample.chm';
Application.HelpFile := hpPath;
end;
procedure TForm1.HHALINKLOOKUPClick(Sender: TObject);
var
link : HH_AKLINK;
szUrl,szKey,szMsgText,szMsgTitle,szWindow : AnsiString;
begin
szKey := Edit1.Text; // 'UsingtheMenus';
szUrl :='Overview.htm';
szMsgText :='Error: Can''t find "'+Edit1.Text+'"!';
szMsgTitle :='Error: HH_ALINK_LOOKUP';
szWindow :='main';
with link do begin
cbStruct := sizeof(HH_AKLINK) ;
fReserved := False;
pszKeywords := PChar(szKey);
pszUrl := nil;
pszMsgText := PChar(szMsgText);
pszMsgTitle := PChar(szMsgTitle);
pszWindow := PChar(szWindow);
fIndexOnFail:= False;
end;
HtmlHelpW(0, hpPath+'>main', HH_DISPLAY_TOPIC, DWORD_PTR(nil));
HtmlHelpW(0, hpPath, HH_ALINK_LOOKUP, DWORD_PTR(#link));
end;
procedure TForm1.JumpAnchorClick(Sender: TObject);
begin
HtmlHelpW(0, hpPath+'::/Overview.htm#'+Edit1.Text+'>main', HH_DISPLAY_TOPIC, DWORD(nil));
end;
end.
Here is a ready to use sample.chm and the source Download
There is a trick how to easily, to jump, not only to the .htm file but jumps directly to an anchor.
Change sample.ali
IDH_Overview=Overview.htm
IDH_welcom=FirstTopic.htm
IDH_UsingtheMenus=Overview.htm#UsingtheMenus
Insert an anchor at the place, you want to jump to in Overview.htm
[...]
<A NAME="UsingtheMenus" </A>
<P><STRONG>Using the Menus and Toolbars</STRONG>
<P>The menus and toolbars provide a complete set of tools
[...]
Now it is possible with F1, jump directly to the desired point in overview.htm.
I suspect that to use A-links you need to do the following:
Assign an Application.OnHelp handler as described below.
Assign Application.HelpFile during program startup.
Call Application.HelpKeyword if you wish to invoke the help system with an A-link.
Set the HelpKeyword property for any GUI controls that you wish to respond to context sensitive F1 key presses.
The OnHelp handler looks like this:
function TMainForm.ApplicationHelp(Command: Word;
Data: THelpEventData; var CallHelp: Boolean): Boolean;
var
Link: THH_AKLink;
ALink: string;
begin
CallHelp := False;
Result := True;
//argh, WinHelp commands
case Command of
HELP_COMMAND:
begin
ZeroMemory(#Link, SizeOf(Link));
Link.cbStruct := SizeOf(Link);
ALink := PChar(Data); // we are going to re-purpose the keyword as an A-link
Link.pszKeywords := PChar(AnsiString(ALink)); // seems we have to pass a PAnsiChar ..
Link.fIndexOnFail := True;
HtmlHelp(GetDesktopWindow, Application.HelpFile, HH_ALINK_LOOKUP,
DWORD_PTR(#Link));
end;
end;
end;
The HtmlHelpViewer unit contains methods named LookupALink which do the same. But I don't see how they could ever be called.
The above approach is a little bit hacky because it interprets keywords as A-Links. If you want context sensitive help, I can't see what else you can do.
Not sure how Xe2 viewer works (I'm on 2007) but I just use Eric Granges port of the Microsoft HTML help API, which unsurprisingly, is called HTMLhelpAPI.pas.
You can call an Alink using the function
ChmShowTopic(const filename,atopic:string):HWND;
Although I realise that in addition to the included Delphi docking demo there are other and better docking libraries available such as the Developer Express Library and the JVCL Docking Library, but for a specific demonstration project I am restricted to use only the Delphi built-in capability (despite some of the noted flaws).
My question relates to adding persistence to the docking state. I see from examining Controls.pas that TDockTree is the default dock manager and it has Stream I/O routines. Digging around on SO and in various forums though I cant see how anyone has called these routines. I've tried loading and saving to a file from the relevant Create and OnDrop events but I'm stabbing in the dark. I am happy saving and restoring form sizes and states but am struggling with the concepts of what I should be saving. Would any kind person give me a starting place?
I'm using Delphi XE3, so all (?) things are possible!
Many thanks.
I'm using Toolbar 2000 from J. Russels. It is providing panels, toolwindow's and toolbar's.
That one provides functions like TBRegSavePositions and TBRegSavePositions to store the user customization into registry.
Loading a "view" get's easily done by on code line:
TBRegLoadPositions(self, HKEY_CURRENT_USER, c_BaseUserRegKey);
in this case self is my form.
You can load and save your docking configuration with the LoadFromStream and SaveToStream methods by storing the data in a string.
Therefore, the following methods are required:
save the current docking configuration to a string
load the current docking configuration from a string
Here is some code to do this:
function GetDockString(const AManager: IDockManager): AnsiString;
var
LStream: TMemoryStream;
begin
LStream := TMemoryStream.Create();
try
AManager.SaveToStream(LStream);
SetLength(Result, 2 * LStream.Size);
BinToHex(LStream.Memory, PAnsiChar(Result), LStream.Size);
finally
FreeAndNil(LStream);
end;
end;
procedure ReadDockString(const ADockString: AnsiString; const AManager: IDockManager);
var
LStream: TMemoryStream;
begin
LStream := TMemoryStream.Create();
try
LStream.Size := Length(ADockString) div 2;
HexToBin(PAnsiChar(ADockString), LStream.Memory, LStream.Size);
LStream.Position := 0;
AManager.LoadFromStream(LStream);
finally
FreeAndNil(LStream);
end;
end;
I've used such methods in an application to create dockable windows, but the vcl provides only a very basic user experience. You can do something about it, but it is hard to test and debug - I already spent too much time to use and override TCustDockDragObject and TCaptionedTabDockTree, so I would recommend using a docking framework.
Here is a minimal example which creates two forms and reads a docking configuration.
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDblClick(Sender: TObject);
private
FPanel: TPanel;
end;
Implementation:
procedure TForm1.FormCreate(Sender: TObject);
var
LWindow: TForm;
const
LDockExample = '0000080000000000000000000000000000000000000000000000000100000000000000000B0000004368696C6457696E646F77FFFFFFFF';
begin
FPanel := TPanel.Create(Self);
FPanel.Align := alTop;
FPanel.Height := 300;
FPanel.DockSite := true;
FPanel.Parent := Self;
LWindow := TForm.CreateNew(Self);
LWindow.Name := 'ChildWindow';
LWindow.DragKind := dkDock;
LWindow.BoundsRect:=Rect(10, 10, 400, 400);
LWindow.Color := clGreen;
LWindow.Show;
ReadDockString(LDockExample, FPanel.DockManager);
end;
procedure TForm1.FormDblClick(Sender: TObject);
begin
ShowMessage(GetDockString(FPanel.DockManager));
end;
i've been trying to get some modeless forms in my application to appear on the taskbar - taking advantage of the new useful taskbar in Windows 7.
There's are many issues with the VCL that need to be undone before a form can exist on the taskbar.
But the final issue is that minimizing the form that the VCL has designated the main form causes all windows in the application to vanish.
Ten years ago, Peter Below (TeamB) documented these problems, and attempts to work around them. But there are some issues that cannot be solved. The issues run so deep within the VCL itself, that it's effectively impossible to make Delphi applications behave properly.
It all stems from the fact that the button you see on the toolbar does not represent the application's window; it represents the TApplications window, which is hidden and never seen. And then there is the application's MainForm, which is then imbued with special abilities where if it is minimized then it instructs the application to hide itself.
It seems to me that if i can do
Application.MainForm := nil;
then all these bugs would go away. The application can have its hidden window, and in the meantime i'll override every other form in the application, including my main form, with:
procedure TForm2.CreateParams(var params: TCreateParams );
begin
inherited CreateParams(params);
params.ExStyle := params.ExStyle or WS_EX_APPWINDOW;
end;
But in Delphi the Application.MainForm property is read-only.
How can i not have a MainForm in Delphi?
See also
(stackoverflow) Delphi: What is Application.Handle?
(newsgroup) Hiding Main Window but not child
You cannot run a GUI project without a MainForm assigned. The main message loop will exit immediately without one. However, that does not mean that the MainForm has to run your UI. You can use a blank hidden TForm as the assigned MainForm, and then have it instantiate your real MainForm as a secondary TForm. For example:
HiddenMainFormApp.dpr:
project HiddenMainFormApp;
uses
..., Forms, HiddenMainForm;
begin
Application.Initialize;
Application.CreateForm(THiddenMainForm, MainForm);
Application.ShowMainForm := False;
Application.Run;
end.
HiddenMainForm.cpp:
uses
..., RealMainForm;
procedure THiddenMainForm.FormCreate(Sender: TObject);
begin
RealMainForm := TRealMainForm.Create(Self);
RealMainForm.Show;
end;
RealMainForm.cpp:
procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Application.Terminate;
end;
Alternatively:
HiddenMainFormApp.dpr:
project HiddenMainFormApp;
uses
..., Forms, HiddenMainForm, RealMainForm;
begin
Application.Initialize;
Application.CreateForm(THiddenMainForm, MainForm);
Application.ShowMainForm := False;
RealMainForm := TRealMainForm.Create(Application);
RealMainForm.Show;
RealMainForm.Update;
Application.Run;
end.
RealMainForm.cpp:
procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Application.Terminate;
end;
You can't, especially in Delphi 5.
Your quote concerning the TApplication window being the one seen on the task bar hasn't been true for several Delphi versions now (I believe D2007 changed it).
Because you're using Delphi 5, you're using an outdated copy of Delphi; current versions have almost none of the things you're writing about any longer. I'd suggest you upgrade to a later version of Delphi (D5 is extremely old); Delphi 2007 if you need to avoid Unicode, Delphi XE if you can use (or don't mind having) Unicode support in the VCL and RTL.
The things you're describing are not bugs, BTW. They were intentional design decisions made at the time Delphi 1 was being designed, and through Delphi 7 worked fine with the versions of Windows that were available. Changes in later versions of Windows (XP/Vista/Win7 and the equivalent Server versions) made changes in that architecture necessary, and they were made as Delphi progressed along with Windows. Because you've chosen not to progress with your version of Delphi to keep it more recent doesn't make the things you write about magically become bugs. :-)
Having Application.MainForm assigned seems not to be a problem here for showing another modeless form on the taskbar while minimizing the MainForm.
Project1.dpr:
program Project1;
uses
Forms,
Windows,
Unit1 in 'Unit1.pas' {MainForm},
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
var
MainForm: TMainForm;
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
ShowWindow(Application.Handle, SW_HIDE);
Application.Run;
end.
Unit1.pas:
unit Unit1;
interface
uses
Windows, Messages, Classes, Controls, Forms, StdCtrls, Unit2;
type
TMainForm = class(TForm)
ShowForm2Button: TButton;
ShowForm2ModalButton: TButton;
procedure ShowForm2ButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ShowForm2ModalButtonClick(Sender: TObject);
private
FForm2: TForm2;
procedure ApplicationActivate(Sender: TObject);
procedure Form2Close(Sender: TObject; var Action: TCloseAction);
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
implementation
{$R *.dfm}
procedure TMainForm.FormCreate(Sender: TObject);
begin
Visible := True; //Required only for MainForm, can be set designtime
Application.OnActivate := ApplicationActivate;
end;
procedure TMainForm.ApplicationActivate(Sender: TObject);
{ Necessary in case of any modal windows dialog or modal Form active }
var
TopWindow: HWND;
I: Integer;
begin
TopWindow := 0;
for I := 0 to Screen.FormCount - 1 do
begin
Screen.Forms[I].BringToFront;
if fsModal in Screen.Forms[I].FormState then
TopWindow := Screen.Forms[I].Handle;
end;
Application.RestoreTopMosts;
if TopWindow = 0 then
Application.BringToFront
else
SetForegroundWindow(TopWindow);
end;
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
{ Testing code from here }
procedure TMainForm.ShowForm2ButtonClick(Sender: TObject);
begin
if FForm2 = nil then
begin
FForm2 := TForm2.Create(Application); //Or: AOwner = nil, or Self
FForm2.OnClose := Form2Close;
end;
ShowWindow(FForm2.Handle, SW_RESTORE);
FForm2.BringToFront;
end;
procedure TMainForm.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
FForm2 := nil;
end;
procedure TMainForm.ShowForm2ModalButtonClick(Sender: TObject);
begin
with TForm2.Create(nil) do
try
ShowModal;
finally
Free;
end;
end;
end.
Unit2.pas:
unit Unit2;
interface
uses
Windows, Messages, Classes, Controls, Forms;
type
TForm2 = class(TForm)
private
procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
implementation
{$R *.dfm}
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TForm2.WMSysCommand(var Msg: TWMSysCommand);
begin
if Msg.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
end.
(Tested with D5 and D7 on XP and Win7.)
(And yes, you may flag this as being not an answer, because it isn't: There still is a MainForm. But I dó like to think this answers the question behind the question...)
I can't speak for Delphi 5, but in Delphi 7 you can definitely run without a mainform if you're willing to get your hands dirty. I covered a lot of the details in another answer here.
Since Delphi 5 doesn't have the MainFormOnTaskbar property, you need to do the following in your dpr:
// Hide application's taskbar entry
WasVisible := IsWindowVisible(Application.Handle);
if WasVisible then
ShowWindow(Application.Handle, SW_HIDE);
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
if WasVisible then
ShowWindow(Application.Handle, SW_SHOW);
// Hide the hidden app window window from the Task Manager's
// "Applications" tab. Don't change Application.Title since
// it might get read elsewhere.
SetWindowText(Application.Handle, '');
That will hide the application window, and as long as you override your form's CreateParams to set Params.WndParent := 0 each of them will have a taskbar entry of their own. Application.MainForm isn't assigned, so things like the minimize override aren't an issue, but you do have to be careful about any code that assumes MainForm is valid.
You can put your modeless forms in a dll, then they act pretty much on their own. (If you do not use the Application instance of the dll while creating them (Application.CreateForm) then Application.Mainform is nil in the dll).
Of course this might not be feasible depending on what the forms might need to do.
Actually most of what you are complaining about is in fact the design of Windows rather than the VCL. See Windows Features for all the details.
The crux of the matter is the owner property, and I mean the windows owner rather than the VCL owner.
An owned window is hidden when
its owner is minimized.
If you wish to be able to minimise the main form without other windows being hidden then you need to get on top of how owned windows work.