Starting a process fails in OnStart code block of a service - c#

I only would like to call
Process.Start("notepad.exe");
when the service starts; But it doesn't work at all. No notepad is called right after I chose to start the service in the windows service manager.
Thank you a lot.
[UPDATE]
I made it work after putting a check mark in logon tab to allow interactive desktop. But I don't know what does this really mean ? How can I schedule to run a task on any computer if it always asks me to accept to view the message in the Interactive Desktop Detection panel ?

A Windows service is different from a standard process and by default it can't interact with the user desktop(this is a rule of the Windows OS), so to launch a process and allow it to interact with the user desktop you have to flag the Interact with desktop option ...
Bear in mind that starting from Windows Vista services are running under session 0 and every time that the service try to start the process a panel is shown to the user the let the user choose if he wants to run the process or not; to overcome this limitation (panel that ask for confirmation) the only way is to launch the process from the service directly with the CreateProcessAsUser function of the Windows API ...
Take a look at this function that i have developed some times ago, that make use of the CreateProcessAsUser API, and start a process from a service without asking anything even in Vista/7:
/// <summary>
/// LaunchProcess As User Overloaded for Window Mode
/// </summary>
/// <param name="cmdLine"></param>
/// <param name="token"></param>
/// <param name="envBlock"></param>
/// <param name="WindowMode"></param>
/// <returns></returns>
private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock,uint WindowMode)
{
bool result = false;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = #"WinSta0\Default"; //Default Vista/7 Desktop Session
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
//Check the Startup Mode of the Process
if (WindowMode == 1)
si.wShowWindow = SW_SHOW;
else if (WindowMode == 2)
{ //Do Nothing
}
else if (WindowMode == 3)
si.wShowWindow = 0; //Hide Window
else if (WindowMode == 4)
si.wShowWindow = 3; //Maximize Window
else if (WindowMode == 5)
si.wShowWindow = 6; //Minimize Window
else
si.wShowWindow = SW_SHOW;
//Set other si properties as required.
result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out pi);
if (result == false)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
Debug.WriteLine(message);
}
return result;
}

Related

Enumerating windows like the Windows taskbar does it [duplicate]

The documentation on EnumWindows underscores:
Note For Windows 8 and later, EnumWindows enumerates only top-level windows of desktop apps.
What is the difference between "desktop apps" and "non desktop apps"?
Is this related to metro apps?
I ask because EnumWindows is behaving somewhat different in Win10 compared with Win7.
Another solution is to use the undocumented api from win32u.dll, it has the prototype:
NTSTATUS WINAPI NtUserBuildHwndList
(
HDESK in_hDesk,
HWND in_hWndNext,
BOOL in_EnumChildren,
BOOL in_RemoveImmersive,
DWORD in_ThreadID,
UINT in_Max,
HWND *out_List,
UINT *out_Cnt
);
Pass it in a HWND list with Max entries, set all other parameters to zero, the output Cnt gives the number of returned entries. If the resultcode is STATUS_BUFFER_TOO_SMALL then reallocate the list with more entries and try again.
Compared to pre-Win10 versions a parameter RemoveImmersive is added. If TRUE then the same list is returned as EnumWindows (without immersive windows). If FALSE then the full list is returned.
The first entry of the list is 0x00000001 as a handle and must be ignored.
The advantage of this api is that the is no posibility of changing of the window list during calls to FindWIndowEx (a lock is set during building of the list)
The EnumWindows, EnumDesktopWindows, EnumChildWindows, FindWindow, FindWindowEx all use this api.
Hereby a request to Microsoft to add a public api EnumWindowsEx or EnumAllWindows so developers have a safe method to enumerate all windows. I understand they added the filter to EnumWindows to fix custom tasklists out there which display visible but cloaked immersive/metro/uwp windows. But a method should be supported for developers to get the full list.
UPDATE: Example on how to use this api, InitWin32uDLL does a runtime load of win32u.dll, and lib_NtUserBuildHwndListW10 is the GetProcAddress pointer
/********************************************************/
/* enumerate all top level windows including metro apps */
/********************************************************/
BOOL Gui_RealEnumWindows(WNDENUMPROC in_Proc, LPARAM in_Param)
{
/* locals */
INT lv_Cnt;
HWND lv_hWnd;
BOOL lv_Result;
HWND lv_hFirstWnd;
HWND lv_hDeskWnd;
HWND *lv_List;
// only needed in Win8 or later
if (gv_SysInfo.Basic.OsVersionNr < OSVER_WIN8)
return EnumWindows(in_Proc, in_Param);
// no error yet
lv_Result = TRUE;
// first try api to get full window list including immersive/metro apps
lv_List = _Gui_BuildWindowList(0, 0, 0, 0, 0, &lv_Cnt);
// success?
if (lv_List)
{
// loop through list
while (lv_Cnt-- > 0 && lv_Result)
{
// get handle
lv_hWnd = lv_List[lv_Cnt];
// filter out the invalid entry (0x00000001) then call the callback
if (IsWindow(lv_hWnd))
lv_Result = in_Proc(lv_hWnd, in_Param);
}
// free the list
MemFree(lv_List);
}
else
{
// get desktop window, this is equivalent to specifying NULL as hwndParent
lv_hDeskWnd = GetDesktopWindow();
// fallback to using FindWindowEx, get first top-level window
lv_hFirstWnd = FindWindowEx(lv_hDeskWnd, 0, 0, 0);
// init the enumeration
lv_Cnt = 0;
lv_hWnd = lv_hFirstWnd;
// loop through windows found
// - since 2012 the EnumWindows API in windows has a problem (on purpose by MS)
// that it does not return all windows (no metro apps, no start menu etc)
// - luckally the FindWindowEx() still is clean and working
while (lv_hWnd && lv_Result)
{
// call the callback
lv_Result = in_Proc(lv_hWnd, in_Param);
// get next window
lv_hWnd = FindWindowEx(lv_hDeskWnd, lv_hWnd, 0, 0);
// protect against changes in window hierachy during enumeration
if (lv_hWnd == lv_hFirstWnd || lv_Cnt++ > 10000)
break;
}
}
// return the result
return lv_Result;
}
HWND *_Gui_BuildWindowList
(
HDESK in_hDesk,
HWND in_hWnd,
BOOL in_EnumChildren,
BOOL in_RemoveImmersive,
UINT in_ThreadID,
INT *out_Cnt
)
{
/* locals */
UINT lv_Max;
UINT lv_Cnt;
UINT lv_NtStatus;
HWND *lv_List;
// is api not supported?
if (!InitWin32uDLL())
return NULL;
// initial size of list
lv_Max = 512;
// retry to get list
for (;;)
{
// allocate list
if ((lv_List = (HWND*)MemAlloc(lv_Max*sizeof(HWND))) == NULL)
break;
// call the api
lv_NtStatus = lib_NtUserBuildHwndListW10(
in_hDesk, in_hWnd,
in_EnumChildren, in_RemoveImmersive, in_ThreadID,
lv_Max, lv_List, &lv_Cnt);
// success?
if (lv_NtStatus == NOERROR)
break;
// free allocated list
MemFree(lv_List);
// clear
lv_List = NULL;
// other error then buffersize? or no increase in size?
if (lv_NtStatus != STATUS_BUFFER_TOO_SMALL || lv_Cnt <= lv_Max)
break;
// update max plus some extra to take changes in number of windows into account
lv_Max = lv_Cnt + 16;
}
// return the count
*out_Cnt = lv_Cnt;
// return the list, or NULL when failed
return lv_List;
}
You are correct. EnumWindows will only find windows that belong to programs that aren't Modern (Metro) apps. It will get windows that belong to traditional (desktop) programs. FindWindowEx, according to several sources, does work all kinds of windows, including those from Modern apps.

EnumWindows function in Win10 enumerates only desktop apps

The documentation on EnumWindows underscores:
Note For Windows 8 and later, EnumWindows enumerates only top-level windows of desktop apps.
What is the difference between "desktop apps" and "non desktop apps"?
Is this related to metro apps?
I ask because EnumWindows is behaving somewhat different in Win10 compared with Win7.
Another solution is to use the undocumented api from win32u.dll, it has the prototype:
NTSTATUS WINAPI NtUserBuildHwndList
(
HDESK in_hDesk,
HWND in_hWndNext,
BOOL in_EnumChildren,
BOOL in_RemoveImmersive,
DWORD in_ThreadID,
UINT in_Max,
HWND *out_List,
UINT *out_Cnt
);
Pass it in a HWND list with Max entries, set all other parameters to zero, the output Cnt gives the number of returned entries. If the resultcode is STATUS_BUFFER_TOO_SMALL then reallocate the list with more entries and try again.
Compared to pre-Win10 versions a parameter RemoveImmersive is added. If TRUE then the same list is returned as EnumWindows (without immersive windows). If FALSE then the full list is returned.
The first entry of the list is 0x00000001 as a handle and must be ignored.
The advantage of this api is that the is no posibility of changing of the window list during calls to FindWIndowEx (a lock is set during building of the list)
The EnumWindows, EnumDesktopWindows, EnumChildWindows, FindWindow, FindWindowEx all use this api.
Hereby a request to Microsoft to add a public api EnumWindowsEx or EnumAllWindows so developers have a safe method to enumerate all windows. I understand they added the filter to EnumWindows to fix custom tasklists out there which display visible but cloaked immersive/metro/uwp windows. But a method should be supported for developers to get the full list.
UPDATE: Example on how to use this api, InitWin32uDLL does a runtime load of win32u.dll, and lib_NtUserBuildHwndListW10 is the GetProcAddress pointer
/********************************************************/
/* enumerate all top level windows including metro apps */
/********************************************************/
BOOL Gui_RealEnumWindows(WNDENUMPROC in_Proc, LPARAM in_Param)
{
/* locals */
INT lv_Cnt;
HWND lv_hWnd;
BOOL lv_Result;
HWND lv_hFirstWnd;
HWND lv_hDeskWnd;
HWND *lv_List;
// only needed in Win8 or later
if (gv_SysInfo.Basic.OsVersionNr < OSVER_WIN8)
return EnumWindows(in_Proc, in_Param);
// no error yet
lv_Result = TRUE;
// first try api to get full window list including immersive/metro apps
lv_List = _Gui_BuildWindowList(0, 0, 0, 0, 0, &lv_Cnt);
// success?
if (lv_List)
{
// loop through list
while (lv_Cnt-- > 0 && lv_Result)
{
// get handle
lv_hWnd = lv_List[lv_Cnt];
// filter out the invalid entry (0x00000001) then call the callback
if (IsWindow(lv_hWnd))
lv_Result = in_Proc(lv_hWnd, in_Param);
}
// free the list
MemFree(lv_List);
}
else
{
// get desktop window, this is equivalent to specifying NULL as hwndParent
lv_hDeskWnd = GetDesktopWindow();
// fallback to using FindWindowEx, get first top-level window
lv_hFirstWnd = FindWindowEx(lv_hDeskWnd, 0, 0, 0);
// init the enumeration
lv_Cnt = 0;
lv_hWnd = lv_hFirstWnd;
// loop through windows found
// - since 2012 the EnumWindows API in windows has a problem (on purpose by MS)
// that it does not return all windows (no metro apps, no start menu etc)
// - luckally the FindWindowEx() still is clean and working
while (lv_hWnd && lv_Result)
{
// call the callback
lv_Result = in_Proc(lv_hWnd, in_Param);
// get next window
lv_hWnd = FindWindowEx(lv_hDeskWnd, lv_hWnd, 0, 0);
// protect against changes in window hierachy during enumeration
if (lv_hWnd == lv_hFirstWnd || lv_Cnt++ > 10000)
break;
}
}
// return the result
return lv_Result;
}
HWND *_Gui_BuildWindowList
(
HDESK in_hDesk,
HWND in_hWnd,
BOOL in_EnumChildren,
BOOL in_RemoveImmersive,
UINT in_ThreadID,
INT *out_Cnt
)
{
/* locals */
UINT lv_Max;
UINT lv_Cnt;
UINT lv_NtStatus;
HWND *lv_List;
// is api not supported?
if (!InitWin32uDLL())
return NULL;
// initial size of list
lv_Max = 512;
// retry to get list
for (;;)
{
// allocate list
if ((lv_List = (HWND*)MemAlloc(lv_Max*sizeof(HWND))) == NULL)
break;
// call the api
lv_NtStatus = lib_NtUserBuildHwndListW10(
in_hDesk, in_hWnd,
in_EnumChildren, in_RemoveImmersive, in_ThreadID,
lv_Max, lv_List, &lv_Cnt);
// success?
if (lv_NtStatus == NOERROR)
break;
// free allocated list
MemFree(lv_List);
// clear
lv_List = NULL;
// other error then buffersize? or no increase in size?
if (lv_NtStatus != STATUS_BUFFER_TOO_SMALL || lv_Cnt <= lv_Max)
break;
// update max plus some extra to take changes in number of windows into account
lv_Max = lv_Cnt + 16;
}
// return the count
*out_Cnt = lv_Cnt;
// return the list, or NULL when failed
return lv_List;
}
You are correct. EnumWindows will only find windows that belong to programs that aren't Modern (Metro) apps. It will get windows that belong to traditional (desktop) programs. FindWindowEx, according to several sources, does work all kinds of windows, including those from Modern apps.

Single Instance Application in C++/CLI using Mutex

I am developing a tray icon based application in C++ CLI. I am using Mutex to ensure single instance of my application running at a time. But each time a new instance starts, the current instance's window should go active.
I am sending a message to the window using PostMessage(Pinvoke). But after 3 or 4 successive run, my application crashes.
Any ideas why that happen. please help!!
The code I have written in the main() function is,
Mutex ^mutex = gcnew Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
if (mutex->WaitOne(TimeSpan::Zero, true))
{
// New Instance. Proceed......................
}
else// An instance is already running. Activate it and return
{
// send our Win32 message to make the currently running instance
// jump on top of all the other windows
try
{
HWND hWindow = FindWindow( nullptr, "MyWindow" );
if(hWindow)
PostMessage(hWindow, WM_ACTIVATE_APP, nullptr,nullptr);
}
catch(Exception^ Ex)
{
}
return -1;
}
Thanks & Regards,
Rohini
Try this instead of PostMessage():
ShowWindowAsync(hWindow, 1); // SW_SHOWNORMAL
SetForegroundWindow(hWindow);

Windows App Certification Kit fails: Multi user session test

I'm trying to pull a WinForms application through the Windows App Certification Kit and gets a fail on this test:
<TEST INDEX="17" NAME="Multi user session test" DESCRIPTION="Multi User checks Application invocation in multiple sessions." EXECUTIONTIME="00h:00m:24s.22ms">
<RESULT><![CDATA[FAIL]]></RESULT>
<MESSAGES />
I guess that this is because I only allow one instance of the application to run, like this:
using ( var p = System.Diagnostics.Process.GetCurrentProcess() )
if ( System.Diagnostics.Process.GetProcessesByName( p.ProcessName ).Length > 1 )
{
MessageBox.Show(
"An instance of xxx is already running!",
Title,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation );
return;
}
It is a tray-application activated by a hot-key combination, registered with this function:
[DllImport( "user32", EntryPoint = "RegisterHotKey", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true )]
public static extern int RegisterHotkey( IntPtr hWnd, int id, int fsModifiers, int vk );
So I guess I have two questions:
1) How can I correctly prevent multiple sessions being run within the same user session but allow several instances across multiple user sessions?
2) Will I be able to register the same hot-key in different user sessions? Or must I somehow unregister and re-register the hot-key when user sessions are switched?
TIA
You can achieve the same effect using a Mutex. See MSDN for details, but the short version is that any mutex created with a name starting with "Local\" will be per-session. Enter a mutex named "Local\MyAppName" and only one instance of your app can run per-session.
Hotkeys are registered per-session and it will not be a problem to register the same hotkey in multiple sessions.
Example Use (from Run single instance of an application using Mutex):
bool ownsMutex = false;
// NOTE: Local is the default namespace, so the name could be shortened to myApp
Mutex myMutex = new Mutex(false, #"Local\myApp");
try
{
ownsMutex = myMutex.WaitOne(0)
}
catch (AbandonedMutexException)
{
ownsMutex = true;
}
if (!ownsMutex)
{
MessageBox.Show("myApp is already running!", "Multiple Instances");
return;
}
else
{
try
{
Application.Run(new Form1());
}
finally
{
myMutex.ReleaseMutex();
}
}

Multiple app domains on a single process winform application

I am creating app domain in my winform application using this code in program.cs file
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
[STAThread]
static void Main(string[] args)
{
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
using (var mutex = new Mutex(false, mutexId))
{
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
mutex.SetAccessControl(securitySettings);
if (mutex.WaitOne(TimeSpan.Zero, true) || (args.Length > 0 && string.Compare(args[0], "secondary", true) == 0))
{
ErrorHandler errorHandler = new ErrorHandler();
DffEnvironment.Default.AppErrorHandler = errorHandler;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(errorHandler.Application_ErrorHandler);
MainForm mainForm = new MainForm();
DffEnvironment.Default.MainForm = mainForm;
if (args.Length > 0)
{
MessageBox.Show(" CurrentDomain" + AppDomain.CurrentDomain.FriendlyName);
}
Application.Run(mainForm);
}
else
{
// send our Win32 message to make the currently running instance
// Add new app domain
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
}
}
}
and in my MainForm (Form) I override the code WndProc method and wrote this
static int procNumber=0;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == NativeMethods.WM_SHOWME)
{
try
{
procNumber++;
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomainApplication" + procNumber.ToString(), null, setupInfo);
string[] arguments = { "secondary" };
appDomain.ExecuteAssembly("MyAppDomainApplication.exe", null, arguments);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
base.WndProc(ref m);
}
It is working fine, it creates another app domain in the same process already running when I try to open my application.
My first question is can I create app domain of the same process in already running from other user, for example
John is working on this app and having two app domains and one process. And Steve logged in on the same machine and tried to open this application and application should not create process and it should add new app domain in already running process by John.
I detected the process running in another user by prefixing the name of the mutex with "Global\". as mentioned here
and second question is here when I edit the following code in program.cs
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
to
try
{
procNumber++;
AppDomain appDomain = AppDomain.CreateDomain("MyAppDomainApplication" + procNumber.ToString(), null, setupInfo);
string[] arguments = { "secondary" };
appDomain.ExecuteAssembly("MyAppDomainApplication.exe", null, arguments);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
It creates another process why it is not working in Program.cs file, why should I have to send message to my Form and do the same thing in WndProc method
I'll answer to your second question.
Application domains are isolated environments inside process. Here you have two processes and they both have their own application domains. You must send a message from one process to another if you want to command the other process to create a new application domain. That is why you have to send the message.
I also suspect that the code does not work in the way you have intended. The ExecuteAssembly() is run in the same thread as the main user interface. If the executed assembly begins a new message loop, your call stack will grow after every WM_SHOWME message and you will eventually get stack overflow exception.
In that case your call stack will look more or less like this:
at Application.Run()
at Main()
at AppDomain.ExecuteAssembly()
...
at Application.Run()
at Main()
at AppDomain.ExecuteAssembly()
at Application.Run()
at Main()

Categories