view packaging/desktopshellrun.cpp @ 1402:1adf72328b75 tip

Added tag 1.0 for changeset ee807f64e21e
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 27 Jan 2015 15:18:32 +0100
parents 12ed0b72e9f5
children
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */

/**
 * @file tasksched_run_as.cpp
 * @brief Run a command with the Task Scheduler API
 *
 * This effectively starts a program as the user regardles of the current
 * process token.
 */
#include <windows.h>
#include "logging.h"
#include "strhelp.h"

#ifndef INITGUID
#define INITGUID
#endif

#include <initguid.h>
#include <mstask.h>
#include <wchar.h>
#include <ole2.h>
#include <shldisp.h>
#include <shobjidl.h>
#include <exdisp.h>
#include <shlguid.h>

/* Some declarations missing in mingw-w64 3.1.0 taken from msdn */

__CRT_UUID_DECL(IShellWindows, 0x85CB6900, 0x4D95, 0x11CF,
                0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);

DEFINE_GUID(IID_IShellWindows,
            0x85CB6900, 0x4D95, 0x11CF,
            0x96, 0x0C, 0x00, 0x80, 0xC7, 0xF4, 0xEE, 0x85);
DEFINE_GUID(CLSID_ShellWindows,
            0x9BA05972, 0xF6A8, 0x11CF,
            0xA4, 0x42, 0x00, 0xA0, 0xC9, 0x0A, 0x8F, 0x39);


__CRT_UUID_DECL(IShellDispatch2, 0xA4C6892C, 0x3BA9, 0x11d2,
                0x9D, 0xEA, 0x00, 0xC0, 0x4F, 0xB1, 0x61, 0x62);
__CRT_UUID_DECL(IShellFolderViewDual,  0xe7a1af80, 0x4d96,
                0x11cf, 0x96, 0x0c, 0x00, 0x80, 0xc7, 0xf4, 0xee, 0x85);

#ifndef SWC_DESKTOP /* Will probably be addedd in future mingw */
#define SWC_DESKTOP 0x00000008
/* from http://msdn.microsoft.com/en-us/library/windows/desktop/cc836581%28v=vs.85%29.aspx */
#endif

#undef INITGUID

#ifdef IS_TAG_BUILD
bool g_debug = false;
#else
bool g_debug = true;
#endif

typedef struct _stack_t
{
  struct _stack_t *next;
  char text[1];          /* This should be the length of string_size. */
} stack_t;

static HINSTANCE g_hInstance; /* Our Instance. */

#define UNUSED(x) (void)(x)

/** @brief the actual execuation call on the shell dispatcher
 *
 * @param[in] disp The shell dispatcher to use for shell execute.
 * @param[in] fName The file that should be exectued.
 * @param[in] param Optinal parameters to add.
 *
 * @returns true on success.
 */
static bool
shellexecute(IShellDispatch2 *disp, wchar_t *fName, wchar_t *param)
{
  BSTR bName = NULL,
       bParam = NULL,
       bDir = NULL,
       bOp = NULL;
  VARIANT vParams[4];
  HRESULT hr;

  if (!fName || !disp)
    {
      ERRORPRINTF ("Invalid call to shellexecute.");
      return false;
    }

  bName = SysAllocString(fName);
  bParam = SysAllocString(param ? param : L"");
  bDir = SysAllocString(L"");
  bOp = SysAllocString(L"");

  if (!bName || !bParam || !bDir || !bOp)
    {
      /* Out of memory */
      ERRORPRINTF ("Failed to allocate bstr values ");
      return false;
    }

  vParams[0].vt = VT_BSTR;
  vParams[0].bstrVal = bParam;
  vParams[1].vt = VT_BSTR;
  vParams[1].bstrVal = bDir;
  vParams[2].vt = VT_BSTR;
  vParams[2].bstrVal = bOp;
  vParams[3].vt = VT_INT;
  vParams[3].intVal = SW_SHOWNORMAL;

  hr = disp->ShellExecute(bName, vParams[0], vParams[1], vParams[2], vParams[3]);

  SysFreeString(bName);
  SysFreeString(bParam);
  SysFreeString(bOp);
  SysFreeString(bDir);

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to execute.");
      return false;
    }
  return true;
}

#ifdef __cplusplus
extern "C" {
#endif

/* Standard entry point for DLLs. */
int WINAPI
DllMain (HANDLE hinst, DWORD reason, LPVOID reserved)
{
  UNUSED(reserved);
  if (reason == DLL_PROCESS_ATTACH)
    g_hInstance = (HINSTANCE)hinst;
  return TRUE;
}

/** @brief Execute a command with the current running shell.
 *
 * This function is intended to be called when you want to
 * make sure that your application is not executed with higher
 * privileges then the normal desktop session.
 *
 * The code is based on the idea:
 * http://blogs.msdn.com/b/oldnewthing/archive/2013/11/18/10468726.aspx
 *
 * The function signature is explained by NSIS.
 */
void __declspec(dllexport) __cdecl Exec(HWND hwndParent,
                                        int string_size,
                                        char *variables,
                                        stack_t **stacktop)
{
  UNUSED(hwndParent);
  UNUSED(string_size);
  HRESULT hr;
  wchar_t *wbuf = NULL,
          *params = NULL;
  IShellWindows *shellWindows = NULL;
  IShellBrowser *shellBrowser = NULL;
  IShellView *shellView = NULL;
  IShellFolderViewDual *folderView = NULL;
  IShellDispatch2 *shellDispatch = NULL;
  IServiceProvider *serviceProv = NULL;
  HWND hwnd;
  IDispatch *disp = NULL,
             *bgDisp = NULL,
              *sDisp = NULL;
  VARIANT vEmpty = {};


  if (!stacktop || !*stacktop || !(*stacktop)->text)
    {
      ERRORPRINTF ("Invalid call to exec :");
      return;
    }

  /* Initialize com ctx */
  hr = CoInitialize(NULL);
  if(FAILED(hr))
    {
      ERRORPRINTF ("CoInitializeEx failed. error = 0x%lx.", hr);
      return;
    }

  /* Get the shell interface */
  hr = CoCreateInstance(CLSID_ShellWindows,
                        NULL, CLSCTX_LOCAL_SERVER,
                        IID_PPV_ARGS(&shellWindows));
  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get shell interface.");
      goto done;
    }

  /* Get the desktop shell window */
  hr = shellWindows->FindWindowSW(&vEmpty,
                                  &vEmpty,
                                  SWC_DESKTOP,
                                  (long*)&hwnd,
                                  SWFO_NEEDDISPATCH,
                                  &disp);
  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to find the desktop dispatcher.");
      goto done;
    }

  hr = disp->QueryInterface(IID_PPV_ARGS(&serviceProv));

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get the service provider.");
      goto done;
    }

  /* Get the shell browser */
  hr = serviceProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shellBrowser));
  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to find the top level browser.");
      goto done;
    }

  hr = shellBrowser->QueryActiveShellView(&shellView);

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to find the active view.");
      goto done;
    }

  hr = shellView->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&bgDisp));

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get the views background.");
      goto done;
    }

  hr = bgDisp->QueryInterface(IID_PPV_ARGS(&folderView));

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get the folder view.");
      goto done;
    }


  hr = folderView->get_Application(&sDisp);

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get the shell dispatch.");
      goto done;
    }

  hr = sDisp->QueryInterface(IID_PPV_ARGS(&shellDispatch));

  if (FAILED(hr))
    {
      ERRORPRINTF ("Failed to get the shell dispatch interface.");
      goto done;
    }

  /* For unicodensis this has to be utf8 to wchar */

  wbuf = acp_to_wchar((*stacktop)->text, strlen((*stacktop)->text));
  if (!wbuf)
    {
      ERRORPRINTF ("Failed to convert argument to wchar. error = 0x%lx.", hr);
      goto done;
    }

  if ((*stacktop)->next && (*stacktop)->next->text)
    {
      params = acp_to_wchar((*stacktop)->next->text, strlen((*stacktop)->next->text));
    }

  if (!shellexecute(shellDispatch, wbuf, params))
    {
      ERRORPRINTF ("Failed to execute.");
    }
  xfree (wbuf);
  xfree (params);
done:
  if (folderView)
    {
      folderView->Release();
    }
  if (disp)
    {
      disp->Release();
    }
  if (shellBrowser)
    {
      shellBrowser->Release();
    }
  if (shellWindows)
    {
      shellWindows->Release();
    }
  if (shellView)
    {
      shellView->Release();
    }
  if (sDisp)
    {
      sDisp->Release();
    }
  if (shellDispatch)
    {
      shellDispatch->Release();
    }
  if (serviceProv)
    {
      serviceProv->Release();
    }
  CoUninitialize();
  return;
}
#ifdef __cplusplus
}
#endif


http://wald.intevation.org/projects/trustbridge/