windowless shell application?

G

Guest

Hi all:

I am designing a "generic" shell component that can launch a user
application specified in an init file, or fall back to cmd if the application
cannot be found/launched.

I have the launcher application written now (using VC++) but am wondering
what the best way is to have this launcher application be "windowless." If I
build the launcher as a console app, an unatractive console window flashes up
at boot time. If I build the launcher as a win32 app, I still get an odd,
invisible "GDI+ window" in focus at launch.

Suggestions?

thanks,
--FritzM.
 
G

Guest

I have a shell component that does not act like a launcher, but it doesn't
look like a window. I used VC++ .NET, Dialog-based MFC App, the dialog has
no title bar and no border. Is that what you're looking for?

MJ
 
C

Coderer

I think it's best to have a Win32 app (or .NET, if you like) and play
with the window properties.

If you're using a Win32 app, you don't have to "launch" (DoModal) the
window -- the "default" Win32 dialog-based VC++ app will create a
<projectname>.cpp file that creates an instance of your dialog class,
then shows it with DoModal(). Just comment out that line and add an
infinite loop, or your application code, after. That should run it
with no output window.

Likewise if you're using a .NET Form, you don't have to call
Application::Run(new <formname>()). At least, I don't think you have
to... anyway, I haven't tried it, but I don't think it should present a
problem.

I can't promise either of those would do *exactly* what you want, but
it's a start. Running windowless shouldn't be that hard.
 
G

Guest

Are you using MFC project to do this? If so, don't. Write a native Win32
app that just spins in WinMain doing whatever you want it to do. As long as
you don't call CreateWindow() API you won't have any windows.

-harrier
 
G

Guest

Yeah, that's precisely what I am trying to do. But the "GDI+ Window"
appears anyway, even though I am not calling CreateWindow explicitly anywhere
in my code.

The entire shell app is one short .cpp. I'll post it here in case anybody
would like to comment on it.

thanks much,
--FritzM.

=================================================

//-------------------------------------------------------------
//
// File: Launcher.cpp
//
// Copyright 2005 (c) by Euphonix, Inc.
//
// Description:
//
// Simple shell application for XPE. Reads a registry entry
// (set via target designer) to find an init file. Reads the
// init file to find an app to launch and working dir to use.
// Launches the app and waits for the launched process to
// terminate.
//
// If failure occurs anywhere along the way, launches an XP
// command window as a failsafe.
//
// Author: Fritz Mueller
//
// Creation Date: 8/3/05
//
// Implementation Notes:
//
//-------------------------------------------------------------

#define WINVER 0x0501
#include <afxwin.h>

#include <tchar.h>
#include <sstream>

using namespace std;

typedef basic_ostringstream<TCHAR> tostringstream;
typedef basic_string<TCHAR> tstring;

class LauncherErr
{
public:
LauncherErr(DWORD iCode, const tstring &iDesc) : mCode(iCode), mDesc(iDesc)
{}
DWORD mCode;
tstring mDesc;
};


DWORD SpawnProcess(
const tstring &cmdline,
const tstring &workingdir,
DWORD creationFlags)
{
DWORD result;
BOOL success;

STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi;

success = CreateProcess(
NULL, (LPTSTR)cmdline.c_str(), NULL, NULL, FALSE, creationFlags, NULL,
workingdir.empty() ? NULL : workingdir.c_str(), &si, &pi
);

if (!success) throw LauncherErr(GetLastError(), _T("Could not create
process."));

result = WaitForSingleObject(pi.hProcess, INFINITE);
if (result == WAIT_FAILED) throw LauncherErr(GetLastError(), _T("Process
wait failed."));

DWORD exitcode;
success = GetExitCodeProcess(pi.hProcess, &exitcode);
if (!success) throw LauncherErr(GetLastError(), _T("Could not retrieve
process exit code."));

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

return exitcode;
}


int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
DWORD result;

try {

HKEY key;
result = RegOpenKey(HKEY_LOCAL_MACHINE,
_T("Software\\Euphonix\\LauncherShell\\"), &key);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not open launcher registry key."));

DWORD valuetype;
TCHAR initpath[MAX_PATH];
DWORD initpathsize = sizeof(initpath);
result = RegQueryValueEx(key, _T("InitFile"), NULL, &valuetype,
(LPBYTE)initpath, &initpathsize);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not retrieve init file registry value."));

TCHAR cmdline[MAX_PATH];
DWORD cmdlinesize = MAX_PATH;
cmdlinesize = GetPrivateProfileString(_T("application"), _T("cmdline"),
NULL, cmdline, cmdlinesize, initpath);
if (cmdlinesize == 0) throw LauncherErr(GetLastError(), _T("Could not
retrieve command."));

TCHAR workingdir[MAX_PATH];
DWORD workingdirsize = MAX_PATH;
workingdirsize = GetPrivateProfileString(_T("application"),
_T("workingdir"), _T(""), workingdir, workingdirsize, initpath);

result = SpawnProcess(cmdline, workingdir, DETACHED_PROCESS);
}

catch(const LauncherErr &err) {
tostringstream str;
str << _T("cmd /k echo Euphonix Launcher Error (") << err.mCode << _T("):
") << err.mDesc << endl;
result = SpawnProcess(str.str(), _T(""), CREATE_NEW_CONSOLE);
}

return (result == ERROR_SUCCESS) ? 0 : -1;
}
 
K

KM

Fritz,

I didn't get that quite - do you use MFC or not?
With frameworks like MFC some windows are created even without a direct call to CreateWindow from your own code (or even if you
don't use CWnd class).

Anyway, please post you code here so that we know what we are all talking about here.

--
Regards,
KM

Yeah, that's precisely what I am trying to do. But the "GDI+ Window"
appears anyway, even though I am not calling CreateWindow explicitly anywhere
in my code.

The entire shell app is one short .cpp. I'll post it here in case anybody
would like to comment on it.

thanks much,
--FritzM.

=================================================

//-------------------------------------------------------------
//
// File: Launcher.cpp
//
// Copyright 2005 (c) by Euphonix, Inc.
//
// Description:
//
// Simple shell application for XPE. Reads a registry entry
// (set via target designer) to find an init file. Reads the
// init file to find an app to launch and working dir to use.
// Launches the app and waits for the launched process to
// terminate.
//
// If failure occurs anywhere along the way, launches an XP
// command window as a failsafe.
//
// Author: Fritz Mueller
//
// Creation Date: 8/3/05
//
// Implementation Notes:
//
//-------------------------------------------------------------

#define WINVER 0x0501
#include <afxwin.h>

#include <tchar.h>
#include <sstream>

using namespace std;

typedef basic_ostringstream<TCHAR> tostringstream;
typedef basic_string<TCHAR> tstring;

class LauncherErr
{
public:
LauncherErr(DWORD iCode, const tstring &iDesc) : mCode(iCode), mDesc(iDesc)
{}
DWORD mCode;
tstring mDesc;
};


DWORD SpawnProcess(
const tstring &cmdline,
const tstring &workingdir,
DWORD creationFlags)
{
DWORD result;
BOOL success;

STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi;

success = CreateProcess(
NULL, (LPTSTR)cmdline.c_str(), NULL, NULL, FALSE, creationFlags, NULL,
workingdir.empty() ? NULL : workingdir.c_str(), &si, &pi
);

if (!success) throw LauncherErr(GetLastError(), _T("Could not create
process."));

result = WaitForSingleObject(pi.hProcess, INFINITE);
if (result == WAIT_FAILED) throw LauncherErr(GetLastError(), _T("Process
wait failed."));

DWORD exitcode;
success = GetExitCodeProcess(pi.hProcess, &exitcode);
if (!success) throw LauncherErr(GetLastError(), _T("Could not retrieve
process exit code."));

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

return exitcode;
}


int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
DWORD result;

try {

HKEY key;
result = RegOpenKey(HKEY_LOCAL_MACHINE,
_T("Software\\Euphonix\\LauncherShell\\"), &key);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not open launcher registry key."));

DWORD valuetype;
TCHAR initpath[MAX_PATH];
DWORD initpathsize = sizeof(initpath);
result = RegQueryValueEx(key, _T("InitFile"), NULL, &valuetype,
(LPBYTE)initpath, &initpathsize);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not retrieve init file registry value."));

TCHAR cmdline[MAX_PATH];
DWORD cmdlinesize = MAX_PATH;
cmdlinesize = GetPrivateProfileString(_T("application"), _T("cmdline"),
NULL, cmdline, cmdlinesize, initpath);
if (cmdlinesize == 0) throw LauncherErr(GetLastError(), _T("Could not
retrieve command."));

TCHAR workingdir[MAX_PATH];
DWORD workingdirsize = MAX_PATH;
workingdirsize = GetPrivateProfileString(_T("application"),
_T("workingdir"), _T(""), workingdir, workingdirsize, initpath);

result = SpawnProcess(cmdline, workingdir, DETACHED_PROCESS);
}

catch(const LauncherErr &err) {
tostringstream str;
str << _T("cmd /k echo Euphonix Launcher Error (") << err.mCode << _T("):
") << err.mDesc << endl;
result = SpawnProcess(str.str(), _T(""), CREATE_NEW_CONSOLE);
}

return (result == ERROR_SUCCESS) ? 0 : -1;
}
 
G

Guest

KM is right. There might be a side effect of using or including MFC library
(#include <afxwin.h>). Unless you need it, get rid of it. If there's a
specific header(s) you need #include it rather than <afxwin.h>. You may be
getting more than you bargin for otherwise.

There a couple things with your code in SpawnProcess.

First, you need to initialize the cb member of STARTUPINFO or risk breaking
with the next OS. si.cb = sizeof(STARTUPINFO);

Secondly, you might consider playing with the CREATE_NO_WINDOW flag in
CreateProcess' dwCreationFlags parameter. Read the docs in MSDN for
restrictions.

KM said:
Fritz,

I didn't get that quite - do you use MFC or not?
With frameworks like MFC some windows are created even without a direct call to CreateWindow from your own code (or even if you
don't use CWnd class).

Anyway, please post you code here so that we know what we are all talking about here.

--
Regards,
KM

Are you using MFC project to do this? If so, don't. Write a native Win32
app that just spins in WinMain doing whatever you want it to do.
As long as you don't call CreateWindow() API you won't have any
windows.

Yeah, that's precisely what I am trying to do. But the "GDI+ Window"
appears anyway, even though I am not calling CreateWindow explicitly anywhere
in my code.

The entire shell app is one short .cpp. I'll post it here in case anybody
would like to comment on it.

thanks much,
--FritzM.

=================================================

//-------------------------------------------------------------
//
// File: Launcher.cpp
//
// Copyright 2005 (c) by Euphonix, Inc.
//
// Description:
//
// Simple shell application for XPE. Reads a registry entry
// (set via target designer) to find an init file. Reads the
// init file to find an app to launch and working dir to use.
// Launches the app and waits for the launched process to
// terminate.
//
// If failure occurs anywhere along the way, launches an XP
// command window as a failsafe.
//
// Author: Fritz Mueller
//
// Creation Date: 8/3/05
//
// Implementation Notes:
//
//-------------------------------------------------------------

#define WINVER 0x0501
#include <afxwin.h>

#include <tchar.h>
#include <sstream>

using namespace std;

typedef basic_ostringstream<TCHAR> tostringstream;
typedef basic_string<TCHAR> tstring;

class LauncherErr
{
public:
LauncherErr(DWORD iCode, const tstring &iDesc) : mCode(iCode), mDesc(iDesc)
{}
DWORD mCode;
tstring mDesc;
};


DWORD SpawnProcess(
const tstring &cmdline,
const tstring &workingdir,
DWORD creationFlags)
{
DWORD result;
BOOL success;

STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi;

success = CreateProcess(
NULL, (LPTSTR)cmdline.c_str(), NULL, NULL, FALSE, creationFlags, NULL,
workingdir.empty() ? NULL : workingdir.c_str(), &si, &pi
);

if (!success) throw LauncherErr(GetLastError(), _T("Could not create
process."));

result = WaitForSingleObject(pi.hProcess, INFINITE);
if (result == WAIT_FAILED) throw LauncherErr(GetLastError(), _T("Process
wait failed."));

DWORD exitcode;
success = GetExitCodeProcess(pi.hProcess, &exitcode);
if (!success) throw LauncherErr(GetLastError(), _T("Could not retrieve
process exit code."));

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

return exitcode;
}


int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
DWORD result;

try {

HKEY key;
result = RegOpenKey(HKEY_LOCAL_MACHINE,
_T("Software\\Euphonix\\LauncherShell\\"), &key);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not open launcher registry key."));

DWORD valuetype;
TCHAR initpath[MAX_PATH];
DWORD initpathsize = sizeof(initpath);
result = RegQueryValueEx(key, _T("InitFile"), NULL, &valuetype,
(LPBYTE)initpath, &initpathsize);
if (result != ERROR_SUCCESS) throw LauncherErr(GetLastError(), _T("Could
not retrieve init file registry value."));

TCHAR cmdline[MAX_PATH];
DWORD cmdlinesize = MAX_PATH;
cmdlinesize = GetPrivateProfileString(_T("application"), _T("cmdline"),
NULL, cmdline, cmdlinesize, initpath);
if (cmdlinesize == 0) throw LauncherErr(GetLastError(), _T("Could not
retrieve command."));

TCHAR workingdir[MAX_PATH];
DWORD workingdirsize = MAX_PATH;
workingdirsize = GetPrivateProfileString(_T("application"),
_T("workingdir"), _T(""), workingdir, workingdirsize, initpath);

result = SpawnProcess(cmdline, workingdir, DETACHED_PROCESS);
}

catch(const LauncherErr &err) {
tostringstream str;
str << _T("cmd /k echo Euphonix Launcher Error (") << err.mCode << _T("):
") << err.mDesc << endl;
result = SpawnProcess(str.str(), _T(""), CREATE_NEW_CONSOLE);
}

return (result == ERROR_SUCCESS) ? 0 : -1;
}
 
G

Guest

Thanks, guys.

Some further experimentation has shown that the GDI+ window seems to be
being created by the *launched* application, and not by the launcher shell
itself (if I have the launcher shell launch some other application, like
notepad.exe, then the GDI+ window does not appear.)

So, it looks like the launcher is working as expected (will fix the si.cb
thing, though) and I'll have to pursue the problem with the downstream
application. Here are the puzzling things:

- if same downstream application is launched from cmd.exe instead of the
launcher shell, no GDI+ window appears.

- sometimes, GDI+ window dissapears as soon as you alt-tab away from it,
but sometimes not.

thanks,
--FritzM.
 
K

KM

Fritz,

When you say "launched application", are you referring to a particular application or *any* application? I doubt that it is the
latter.
If otherwise, please give us more details about the launched application.

Also, why don't you try using MS Spy++ at run time? You can find out pretty much all info needed to know what creates the window.
Another way (may be even easier from your shell app code) - to use API to spy on the processes in your runtime.
 
G

Guest

Yeah, so:

Launcher.exe (custom shell component, for which I posted source) launches
whatever is specified in an accompanying INI file. Usually, this is our main
application, which is indeed a big MFC app that uses GDI+. There's a tweaky
old custom MFC framework in the main application that I inherited, so there's
plenty for me to pick through.

If I change the INI file so that launcher.exe launches some other win32
app (say, notepad.exe) then the phantom GDI+ window does not appear. I will
next try a very simple MFC dialog app w/ a GDI+ call instead of our main app,
and see what that does. If I can duplicate it with a simplel piece of code,
then I'll post that.

I'll also take a look with spy++ and see what I can see.

thanks again,
--FritzM.
 
K

KM

Fritz,

Interesting.. My OE is behaving strangely today. I actually didn't see the source code you posted. (your message was cut by my news
client :-( )
I was a bit surprised by the yours and harrier's comments on that :) My replies were too generic.

Now I went to Google archive and I saw the code and mentioned SpawnProcess/STARTUPINFO.

As to the Spy++.. You don't have to "spy" on your XPe runtime. You can definitely explore an app on XP Pro.
 
G

Guest

Okay, I finally had a chance to look at this some more today. Ran spyxx with
my app under both XP pro and embedded, and found out that the GDI+ hook
window is created in either case.

The difference is that sometimes, under XPE, the window has WS_VISIBLE style
(but under XP pro it never does.)

Sometimes, alt-tabbing around under XPE will provoke the GDI+ window to
"hide" itself, as well.

Perhaps jiggering with the process creation flags used by the launcher app
can fix this. Anybody know what flags windows "typically" uses when
launching a windows subsystem app from either the explorer or cmd?

--FritzM.
 
G

Guest

Ah, got it!

The launched application just needs to call SetForegroundWindow on its main
window.

Thanks for your help, all.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top