Q: Start a program hidden
Answer:
To start a program hidden, you must edit the WinMain function and hide the program's main form and the
program's taskbar icon.
Step 1: Choose View|Project Source from the C++Builder menu so you
can edit the WinMain function. Hide the program's taskbar icon by
calling ShowWindow for the application's window handle. Set ShowMainForm to
false to keep the main form from appearing on the screen.
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->ShowMainForm = false;
ShowWindow(Application->Handle, SW_HIDE);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
Step 2: Execute these two code statements when you want to show the
application. Note that Application->MainForm->Visible=true can be
shortened to just Visible=true if the code is located in a method
of the main form's class.
ShowWindow(Application->Handle, SW_SHOW);
Application->MainForm->Visible = true;
Final Note (C++Builder 1.0 only): In C++Builder 1.0, the constructor for the global
TApplication object called the CreateHandle method. CreateHandle executed API
functions to create a window handle for the application. Creating the window handle resulted in a taskbar icon
appearing on the screen. This icon would appear before WinMain ran. This icon would appear to flash if
WinMain was trying to create a program that started in a hidden state. The rest of this FAQ explains how to
eliminate the taskbar icon flash that occurred in C++Builder 1.0 programs. C++Builder 3.0 contained a fix for the
shortcoming in TApplication. If you use C++Builder 3.0, ignore what follows.
Because of the way the VCL works, the application's icon will flash briefly on the
taskbar before the ShowWindow call executes inside of WinMain.
The VCL creates the global application object before WinMain is called. The
TApplication constructor executes a function called CreateHandle that creates
a window handle and places an icon on the taskbar. The only way to keep the
icon from flashing is to provide a different version of
CreateHandle. This isn't too hard if you have the professional
or client/server versions of C++Builder. You can simply copy
\SOURCE\VCL\FORMS.PAS to your project directory, add this file to your
project, and fix the flaws in CreateHandle. CreateHandle contains two statements
that force an icon to appear on the taskbar.
The first is the CreateWindow call, which looks like this:
FHandle := CreateWindow(WindowClass.lpszClassName, PChar(FTitle),
WS_POPUP or WS_CAPTION or WS_VISIBLE or WS_CLIPSIBLINGS or
WS_SYSMENU or WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN) div 2,
GetSystemMetrics(SM_CYSCREEN) div 2,
0, 0, 0, 0, HInstance, nil);
The second call is the call to ShowWinNoAnimate. ShowWinNoAnimate is a
VCL function whose arguments reflect a ShowWindow API call.
ShowWinNoAnimate(FHandle, SW_RESTORE);
In the CreateWindow call, the WS_VISIBLE style causes the taskbar icon
to appear. You can fix this by simply deleting the WS_VISIBLE style.
The ShowWinNoAnimate call ends up passing SW_RESTORE to the API ShowWindow
function. SW_RESTORE shows and restores a window, and showing the form
places the icon on the taskbar. You can prevent this by changing
SW_RESTORE to SW_HIDE, or by commenting out the call altogether. The
new version of CreateHandle should look like this:
procedure TApplication.CreateHandle;
var
TempClass: TWndClass;
SysMenu: HMenu;
begin
if not FHandleCreated then
begin
FObjectInstance := MakeObjectInstance(WndProc);
if not GetClassInfo(HInstance, WindowClass.lpszClassName, TempClass)
then
begin
WindowClass.hInstance := HInstance;
if Windows.RegisterClass(WindowClass) = 0 then
raise EOutOfResources.CreateRes(SWindowClass);
end;
FHandle := CreateWindow(WindowClass.lpszClassName, PChar(FTitle),
WS_POPUP or WS_CAPTION or WS_CLIPSIBLINGS or
WS_SYSMENU or WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN) div 2,
GetSystemMetrics(SM_CYSCREEN) div 2,
0, 0, 0, 0, HInstance, nil);
FTitle := '';
FHandleCreated := True;
ShowWinNoAnimate(FHandle, SW_HIDE);
SetWindowLong(FHandle, GWL_WNDPROC, Longint(FObjectInstance));
if NewStyleControls then
SendMessage(FHandle, WM_SETICON, 1, GetIconHandle);
SysMenu := GetSystemMenu(FHandle, False);
DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
if NewStyleControls then DeleteMenu(SysMenu, SC_MOVE, MF_BYCOMMAND);
end;
end;
Important: Make sure you modify the version of FORMS.PAS that you
copied to your project directory and added to your project. Do not
modify the file in \SOURCE\VCL.
So what if you have the standard version of C++Builder and don't have access
to the VCL source? You can code your own version of TApplication::CreateHandle
in your project source file using C++. The linker will utilize your updated
version of the function instead of the function in the VCL library files. The
C++ function should look like this (insert this just before the WinMain
function).
void __fastcall TApplication::CreateHandle(void)
{
WNDCLASS WindowClass ;
WindowClass.style=0;
WindowClass.lpfnWndProc=DefWindowProc;
WindowClass.cbClsExtra=0;
WindowClass.cbWndExtra=0;
WindowClass.hInstance =0;
WindowClass.hIcon= 0;
WindowClass.hCursor= 0;
WindowClass.hbrBackground= 0;
WindowClass.lpszMenuName = NULL;
WindowClass.lpszClassName= "TApplication";
TWndClass TempClass;
HMENU SysMenu;
if(!FHandleCreated)
{
FObjectInstance = MakeObjectInstance(WndProc);
if (!GetClassInfo(HInstance, WindowClass.lpszClassName, &TempClass))
{
WindowClass.hInstance = HInstance;
if (RegisterClass(&WindowClass) == 0)
throw EOutOfResources("Error registering window class");
}
FHandle = CreateWindow(WindowClass.lpszClassName, FTitle.c_str(),
WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS |
WS_SYSMENU | WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN) / 2,
GetSystemMetrics(SM_CYSCREEN) / 2,
0, 0, 0, 0, HInstance, NULL);
FTitle = "";
FHandleCreated = True;
// ShowWinNoAnimate(FHandle, SW_HIDE);
SetWindowLong(FHandle, GWL_WNDPROC, Longint(FObjectInstance));
if (NewStyleControls)
SendMessage(FHandle, WM_SETICON, 1, (LPARAM)GetIconHandle());
SysMenu = GetSystemMenu(FHandle, False);
DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
if (NewStyleControls)
DeleteMenu(SysMenu, SC_MOVE, MF_BYCOMMAND);
}
}
|