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.
Related
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.
There's a well-known problem that Skype on Windows 8 takes up 100% of one CPU core on some users' PCs. Including mine! There's a workaround courtesy of techfreak in Skype Community:
Download and run the latest version of process explorer. (http://download.sysinternals.com/files/ProcessExplorer.zip)
With Skype running search for Skype.exe in the list of active programs and double click on it.
Go to the threads tab and Suspend or Kill the Skype thread that is consuming the highest resources when IDLE. (like 50%+ CPU)
I'm getting annoyed with manually doing this after every reboot, so I'd like to automate the steps above, to write a simple C++ or C# "Skype launcher" program that does the following:
launch SKYPE.EXE
wake up every 1 second and look to see if one particular Skype thread is taking up over 98% of the CPU cycles in the process
if found, suspend that thread and exit the launcher process
otherwise loop up to 10 times until the bad thread is found.
After a quick Google search I got intimidated by the Win32 thread-enumeration APIs, and this "find and kill/suspend evil thread" problem seems to be fairly generic, so I'm wondering if there's an existing sample out there that I could re-purpose. Any pointers?
After much more googling and some dead ends with powershell (too many security hassles, too confusing for a newbie) and WMI (harder than needed), I finally found a great C# sample on MSDN Forums that will enumerate and suspend threads. This was easy to adapt to first check CPU time of each thread before suspending the culprit.
Here's code. Just compile and drop into your startup menu and Skype will no longer heat your office!
// code adapted from
// http://social.msdn.microsoft.com/Forums/en-US/d51efcf0-7653-403e-95b6-bf5fb97bf16c/suspend-thread-of-a-process
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.ComponentModel;
namespace SkypeLauncher
{
class Program
{
static void Main(string[] args)
{
Process[] procs = Process.GetProcessesByName("skype");
if (procs.Length == 0)
{
Console.WriteLine("Skype not loaded. Launching. ");
Process.Start(Environment.ExpandEnvironmentVariables(#"%PROGRAMFILES(X86)%\Skype\Phone\Skype.exe"));
Thread.Sleep(8000); // wait to allow skype to start up & get into steady state
}
// wait to allow skype to start up & get into steady state, where "steady state" means
// a lot of threads created
Process proc = null;
for (int i = 0; i < 50; i++)
{
procs = Process.GetProcessesByName("skype");
if (procs != null)
{
proc = procs[0];
if (proc.Threads.Count > 10)
break;
}
Thread.Sleep(1000); // wait to allow skype to start up & get into steady state
}
// try multiple times; if not hanging after a while, give up. It must not be hanging!
for (int i = 0; i < 50; i++)
{
// must reload process to get updated thread time info
procs = Process.GetProcessesByName("skype");
if (procs.Length == 0)
{
Console.WriteLine("Skype not loaded. Exiting. ");
return;
}
proc = procs[0];
// avoid case where exception thrown if thread is no longer around when looking at its CPU time, or
// any other reason why we can't read the time
var safeTotalProcessorTime = new Func<ProcessThread, double> (t =>
{
try { return t.TotalProcessorTime.TotalMilliseconds; }
catch (InvalidOperationException) { return 0; }
}
);
var threads = (from t in proc.Threads.OfType<ProcessThread>()
orderby safeTotalProcessorTime(t) descending
select new
{
t.Id,
t.ThreadState,
TotalProcessorTime = safeTotalProcessorTime(t),
}
).ToList();
var totalCpuMsecs = threads.Sum(t => t.TotalProcessorTime);
var topThread = threads[0];
var nextThread = threads[1];
var topThreadCpuMsecs = topThread.TotalProcessorTime;
var topThreadRatio = topThreadCpuMsecs / nextThread.TotalProcessorTime;
// suspend skype thread that's taken a lot of CPU time and
// and it has lots more CPU than any other thread.
// in other words, it's been ill-behaved for a long time!
// it's possible that this may sometimes suspend the wrong thread,
// but I haven't seen it break yet.
if (topThreadCpuMsecs > 10000 && topThreadRatio > 5)
{
Console.WriteLine("{0} bad thread. {0:N0} msecs CPU, {1:N1}x CPU than next top thread.",
topThread.ThreadState == System.Diagnostics.ThreadState.Wait ? "Already suspended" : "Suspending",
topThreadCpuMsecs,
topThreadRatio);
Thread.Sleep(1000);
IntPtr handle = IntPtr.Zero;
try
{
//Get the thread handle & suspend the thread
handle = OpenThread(2, false, topThread.Id);
var success = SuspendThread(handle);
if (success == -1)
{
Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
Console.WriteLine(ex.Message);
}
Console.WriteLine("Exiting");
Thread.Sleep(1000);
return;
}
finally
{
if (handle != IntPtr.Zero)
CloseHandle(handle);
};
}
Console.WriteLine("Top thread: {0:N0} msecs CPU, {1:N1}x CPU than next top thread. Waiting.",
topThreadCpuMsecs,
topThreadRatio);
Thread.Sleep(2000); // wait between tries
}
Console.WriteLine("No skype thread is ill-behaved enough. Giving up.");
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern
IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)]bool bInheritHandle, int dwThreadId);
}
}
We have a third party winforms software that we need to run as a batch
I need to monitor if a certain form for a certain process (we run several processes at the same time) is shown.
I have used this method to get all window handles for a process
public IEnumerable<int> EnumerateProcessWindowHandles(int processId)
{
var handles = new List<IntPtr>();
try
{
foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
Win32.EnumThreadWindows(thread.Id,
(hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
}
catch(Exception e) {}
return handles.Select(h => (int)h);
}
Then this method to get the window caption from the hwnd
public string GetTitle(int hwnd)
{
int length = Win32.SendMessage((IntPtr)hwnd, Win32.WM_GETTEXTLENGTH, 0, IntPtr.Zero);
var sb = new StringBuilder(length + 1);
Win32.SendMessage((IntPtr)hwnd, Win32.WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
Each second I poll the process with above methods, but sometimes it fails to detected a window shown. The window in question is opened more than a second so its not the pol frequency.
Is there a more reliable way of getting callbacks when a window is closed/opened?
Probably the cleanest way to listen to window creation and destruction is using a CBT hook. Listen for HCBT_CREATEWND and HCBT_DESTROYWND. This MSDN article, Windows Hooks in the .NET Framework, covers the subject from a .net perspective.
maybe that can help
[1] http://spradip.wordpress.com/category/programming-c/page/2/
and this any ms C++ example
[2] http://msdn.microsoft.com/en-us/library/windows/desktop/ms686701(v=vs.85).aspx
I'm currently writing a software in Visual Studio 2012 for communication with RFID-cards.
I got a DLL written in Delphi to handle the communication with the card reader.
The problem is: My software is running fine on machines, that have VS2012 installed. On other systems it freezes itself or the whole system.
I tried it on Win XP / 7 / 8 with x32 and x64 configuration.
I'm using .NET 4.0.
After connecting to the reader, the software starts a backgroundWorker, which polls (at 200ms rate) the reader with a command to inventory cards in the readers RF-field. The crash usally happens ca. 10 to 20 seconds after the reader connect. Here is the code:
[DllImport("tempConnect.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int inventory(int maxlen, [In] ref int count,
IntPtr UIDs, UInt32 HFOffTime);
public String getCardID()
{
if (isConnectet())
{
IntPtr UIDs = IntPtr.Zero;
int len = 2 * 8;
Byte[] zero = new Byte[len];
UIDs = Marshal.AllocHGlobal(len);
Thread.Sleep(50);
Marshal.Copy(zero, 0, UIDs, len);
int count = 0;
int erg;
String ret;
try
{
erg = inventory(len, ref count, UIDs, 50);
}
catch (ExternalException) // this doesn't catch anything (iI have set <legacyCorruptedStateExceptionsPolicy enabled="true"/>)
{
return "\0";
}
finally
{
ret = Marshal.PtrToStringAnsi(UIDs, len);
IntPtr rslt = LocalFree(UIDs);
GC.Collect();
}
if (erg == 0)
return ret;
else
return zero.ToString();
}
else
return "\0";
}
The DLL is written in Delphi, the code DLL command is:
function inventory (maxlen: Integer; var count: Integer;
UIDs: PByteArray; HFOffTime: Cardinal = 50): Integer; STDCALL;
I think there may be a memory leak somewhere, but I have no idea how to find it...
EDIT:
I added some ideas (explicit GC.Collect(), try-catch-finally) to my code above, but it still doesnt work.
Here is the code, that calls getCardID():
The action, that runs every 200ms:
if (!bgw_inventory.IsBusy)
bgw_inventory.RunWorkerAsync();
Async backgroundWorker does:
private void bgw_inventory_DoWork(object sender, DoWorkEventArgs e)
{
if (bgw_inventory.CancellationPending)
{
e.Cancel = true;
return;
}
else
{
String UID = reader.getCardID();
if (bgw_inventory.CancellationPending)
{
e.Cancel = true;
return;
}
if (UID.Length == 16 && UID.IndexOf("\0") == -1)
{
setCardId(UID);
if (!allCards.ContainsKey(UID))
{
allCards.Add(UID, new Card(UID));
}
if (readCardActive || deActivateCardActive || activateCardActive)
{
if (lastActionCard != UID)
actionCard = UID;
else
setWorkingStatus("OK", Color.FromArgb(203, 218, 138));
}
}
else
{
setCardId("none");
if (readCardActive || deActivateCardActive || activateCardActive)
setWorkingStatus("waiting for next card", Color.Yellow);
}
}
}
EDIT
Till now I have made some little reworks (updates above) at the code. Now only the App. crashes with 0xC00000FD (Stack overflow) at "tempConnect.dll". This does not happen on Systems with VS2012 installed or if I use the DLL with native Delphi!
Do anyone have any other ideas ?
EDIT
Now I made the DLL logging it's stacksize and found something weird:
If it's called and polled from my C# Programm, the stacksize is changing continuously up and down.
If i do the same from a natural Deplhi Program the stacksize is constant!
So I'll do further investigations, but I have no really idea, what I have to search for...
I'm a little concerned about how're using that Marshal object. As you fear with the memory leak, it seems to be allocating memory quite often but I don't see it ever explicitly releasing it. The garbage collector should (operative word) be taking care of that, but you say yourself you have some unmanaged code in the mix. It is difficult with the posted information to tell where the unmanaged code begins.
Check out this question for some good techniques to finding memory leaks in .NET itself - this will give you a ton of information on how memory is being used in the managed end of your code (that is, the part you can directly control). Use the Windows Performance Monitor with breakpoints to keep an eye on the overall health of the system. If .NET appears to be behaving, but WPM is showing some sharp spikes, it's probably in the unmanaged code. You can't really control anything but your usage there, so it would probably be time to go back to the documentation at that point.
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;
}