Monthly Archives: June 2014

Getting GDI object count per process in Powershell

Ever wanted to query the GDI object count in PowerShell per process?

Here’s how:

"Number of GUI handles per process"
$sig = @'
[DllImport("User32.dll")]
public static extern int GetGuiResources(IntPtr hProcess, int uiFlags);
'@

Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32

$processes = [System.Diagnostics.Process]::GetProcesses()
[int]$gdiHandleCount = 0
ForEach ($p in $processes)
{
    try{
        $gdiHandles = [Win32.NativeMethods]::GetGuiResources($p.Handle, 0)
        $gdiHandleCount += $gdiHandles
        $p.Name + " : " + $gdiHandles.ToString()   
    }
    catch {
        #"Error accessing " + $p.Name
    }
}
"Total number of GDI handles " + $gdiHandleCount.ToString()

Alternatively you can use Process Explorer from Microsoft (originally sys-internals) if it is installed.

How to build an C# Application that can relaunch itself in Admin mode – without UAC prompt

Lets start by emphasizing that this is NOT a way to create a hack. In order for this to work you (the user) needs to be an Administrator and access to be able to launch applications in ‘Admin mode’ anyway. The first time when the application needs to set up this functionality it needs to run in Admin mode as well (for which you’ll have to be prompted by UAC). Only after this and for the future the application in ‘Non-admin’ mode can then relaunch itself in ‘Admin mode’ without showing the UAC prompt.

The ‘trick’ (not hack) is to use the built-in Windows Task scheduler that can launch applications with the ‘highest’ priority setting. Take note that this also stops you from launching the app with any command line parameters – but there are other ways an application can ‘communicate’ with itself/another instance of itself e.g. by saving its own settings before launching the second instance and the second instance reading those settings again.

So how can a (C#) application integrate this easily? My first idea was to call the Task scheduler (Schtasks.exe) manually with the required parameters to create a task of launching the app again later in admin mode. The problem part is finding out if the task already exists or not. Then I came across an article that showed how the COM+ library for the Task Scheduler can be used from .Net which make it a whole lot easier to interact.

Task Scheduler COM+ library

In your application’s references add a reference to ‘TaskScheduler 1.1 Type Library’ (Interop.TaskScheduler.dll) usually located on ‘c:\Windows\SysWow64\taskschd.dll (on 64-bit at least). Then you can add the following type of code in your project:

Code

To test if task exists or is running already:


private string myLaunchTaskName = "RunMeAsAdmin";

private bool LaunchTaskExist()
{
  try
  {
    TaskScheduler.TaskScheduler ts = new TaskScheduler.TaskScheduler();
    ts.Connect(null, null, null, null);
    if (ts.Connected)
    {
      TaskScheduler.ITaskFolder root = ts.GetFolder("\\");
      TaskScheduler.IRegisteredTask task = root.GetTask(myLaunchTaskName);
      if (task != null)
        return true;
    }
  }
  catch { }
  return false;
}
private bool TaskAlreadyRunning()
{
  try
  {
    TaskScheduler.TaskScheduler ts = new TaskScheduler.TaskScheduler();
    ts.Connect(null, null, null, null);
    if (ts.Connected)
    {
      foreach (TaskScheduler.IRunningTask td in ts.GetRunningTasks(0))
      {
        if (td.Name == myLaunchTaskName)
          return true;
      }
    }
  }
  catch { }
  return false;
}

To create the task the first time:

function CreateSelfLaunchTask()
{
  try
  {
    if (AdminModeTools.IsInAdminMode()) //Different library to test if app is in Admin mode
    {
      TaskScheduler.TaskScheduler ts = new TaskScheduler.TaskScheduler();
      ts.Connect(null, null, null, null);
      if (ts.Connected)
      {
        TaskScheduler.ITaskDefinition task = ts.NewTask(0);
        task.RegistrationInfo.Author = "Me";
        task.RegistrationInfo.Description = myLaunchTaskName;
        task.Principal.RunLevel = TaskScheduler._TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
        task.Settings.MultipleInstances = TaskScheduler._TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW;
        TaskScheduler.ITimeTrigger trigger = (TaskScheduler.ITimeTrigger)task.Triggers.Create(TaskScheduler._TASK_TRIGGER_TYPE2.TASK_TRIGGER_TIME);
        trigger.Id = "NoTime";
        trigger.StartBoundary = "2000-01-01T12:00:00";
        trigger.StartBoundary = "2000-01-01T12:00:00";
        TaskScheduler.IExecAction action = (TaskScheduler.IExecAction)task.Actions.Create(TaskScheduler._TASK_ACTION_TYPE.TASK_ACTION_EXEC);
        action.Id = "Run exe";
        action.Path = System.Reflection.Assembly.GetExecutingAssembly().Location;
        action.WorkingDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

        TaskScheduler.ITaskFolder root = ts.GetFolder("\\");
        TaskScheduler.IRegisteredTask regTask = root.RegisterTaskDefinition(
          myLaunchTaskName,
          task,
          (int)TaskScheduler._TASK_CREATION.TASK_CREATE_OR_UPDATE,
          null,
          null,
          TaskScheduler._TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN);
      }
    }
    else
    {
      MessageBox.Show("To create the task you must start this program in 'Admin' mode.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
  }
  catch(Exception ex)
  {
    MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
}

Then to call the task:

function void RunAppAsAdmin()
{
  try
  {
    if (LaunchTaskExist())
    {
      if (!TaskAlreadyRunning())
      {
        TaskScheduler.TaskScheduler ts = new TaskScheduler.TaskScheduler();
        ts.Connect(null, null, null, null);
        if (ts.Connected)
        {
          TaskScheduler.ITaskFolder root = ts.GetFolder("\\");
          TaskScheduler.IRegisteredTask task = root.GetTask(myLaunchTaskName);
          TaskScheduler.IRunningTask runTask = task.Run(null);
        }
      }
      else
      {
        MessageBox.Show("The task is already running!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }
    else
    {
      MessageBox.Show("The task does not exist yet!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
}

If for some reason you need to delete the task you can use this:

private void DeleteRunAsAdminTask()
{
    try
    {
        if (AdminModeTools.IsInAdminMode())
        {
            if (LaunchTaskExist())
            {
                TaskScheduler.TaskScheduler ts = new TaskScheduler.TaskScheduler();
                ts.Connect(null, null, null, null);
                if (ts.Connected)
                {
                    TaskScheduler.ITaskFolder root = ts.GetFolder("\\");
                    TaskScheduler.IRegisteredTask task = root.GetTask(myLaunchTaskName);
                    if (task != null)
                    {
                        root.DeleteTask(myLaunchTaskName, 0);
                    }
                }
            }
            else
            {
                MessageBox.Show("The task does not exist!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        else
        {
            MessageBox.Show("To delete the task you must start this program in 'Admin' mode.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

With all these ‘bits’ you can build an application that can launch itself in ‘Admin’ mode. Have fun kidz but not too much!

I’ve tested this on Windows Vista, 7, 2008/R2 and 8. I don’t have access to any XP machines anymore but I doubt it will work on XP (or 2003)… duhh.. Just remembered XP and 2003 does not have ‘Admin’ mode anyway so this does not apply… silly me.

Update: I now have one of my utilities actually using this code: Service Monitor