Is it possible to apply (and remove) Windows group policy settings using .NET?
I am working on an application that needs to temporarily put a machine into a restricted, kiosk-like state. One of the things I need to control is access to USB drives which I believe I can do through group policy. I'd like my app to set the policy when it starts and revert the change when it exits... is this something I can do through .NET framework calls?
These are my primary requirements:
Apply group policy settings when my console app is started.
Identify when a user action is denied by the policy and log it.
Logging to the system security log is acceptable.
Revert my policy changes when my app stops.
Try using IGroupPolicyObject
bool SetGroupPolicy(HKEY hKey, LPCTSTR subKey, LPCTSTR valueName, DWORD dwType, const BYTE* szkeyValue, DWORD dwkeyValue)
{
CoInitialize(NULL);
HKEY ghKey, ghSubKey, hSubKey;
LPDWORD flag = NULL;
IGroupPolicyObject *pGPO = NULL;
HRESULT hr = CoCreateInstance(CLSID_GroupPolicyObject, NULL, CLSCTX_ALL, IID_IGroupPolicyObject, (LPVOID*)&pGPO);
if(!SUCCEEDED(hr))
{
MessageBox(NULL, L"Failed to initialize GPO", L"", S_OK);
}
if (RegCreateKeyEx(hKey, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hSubKey, flag) != ERROR_SUCCESS)
{
return false;
CoUninitialize();
}
if(dwType == REG_SZ)
{
if(RegSetValueEx(hSubKey, valueName, 0, dwType, szkeyValue, strlen((char*)szkeyValue) + 1) != ERROR_SUCCESS)
{
RegCloseKey(hSubKey);
CoUninitialize();
return false;
}
}
else if(dwType == REG_DWORD)
{
if(RegSetValueEx(hSubKey, valueName, 0, dwType, (BYTE*)&dwkeyValue, sizeof(dwkeyValue)) != ERROR_SUCCESS)
{
RegCloseKey(hSubKey);
CoUninitialize();
return false;
}
}
if(!SUCCEEDED(hr))
{
MessageBox(NULL, L"Failed to initialize GPO", L"", S_OK);
CoUninitialize();
return false;
}
if(pGPO->OpenLocalMachineGPO(GPO_OPEN_LOAD_REGISTRY) != S_OK)
{
MessageBox(NULL, L"Failed to get the GPO mapping", L"", S_OK);
CoUninitialize();
return false;
}
if(pGPO->GetRegistryKey(GPO_SECTION_USER,&ghKey) != S_OK)
{
MessageBox(NULL, L"Failed to get the root key", L"", S_OK);
CoUninitialize();
return false;
}
if(RegCreateKeyEx(ghKey, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &ghSubKey, flag) != ERROR_SUCCESS)
{
RegCloseKey(ghKey);
MessageBox(NULL, L"Cannot create key", L"", S_OK);
CoUninitialize();
return false;
}
if(dwType == REG_SZ)
{
if(RegSetValueEx(ghSubKey, valueName, 0, dwType, szkeyValue, strlen((char*)szkeyValue) + 1) != ERROR_SUCCESS)
{
RegCloseKey(ghKey);
RegCloseKey(ghSubKey);
MessageBox(NULL, L"Cannot create sub key", L"", S_OK);
CoUninitialize();
return false;
}
}
else if(dwType == REG_DWORD)
{
if(RegSetValueEx(ghSubKey, valueName, 0, dwType, (BYTE*)&dwkeyValue, sizeof(dwkeyValue)) != ERROR_SUCCESS)
{
RegCloseKey(ghKey);
RegCloseKey(ghSubKey);
MessageBox(NULL, L"Cannot set value", L"", S_OK);
CoUninitialize();
return false;
}
}
if(pGPO->Save(false, true, const_cast<GUID*>(&EXTENSION_GUID), const_cast<GUID*>(&CLSID_GPESnapIn)) != S_OK)
{
RegCloseKey(ghKey);
RegCloseKey(ghSubKey);
MessageBox(NULL, L"Save failed", L"", S_OK);
CoUninitialize();
return false;
}
pGPO->Release();
RegCloseKey(ghKey);
RegCloseKey(ghSubKey);
CoUninitialize();
return true;
}
You can call this function like this..
// Remove the Log Off in start menu
SetGroupPolicy(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
L"StartMenuLogOff", REG_DWORD, NULL, 1);
NOTE: I use two GroupPolicy assembly references:
C:\Windows\assembly\GAC_MSIL\Microsoft.GroupPolicy.Management\2.0.0.0__31bf3856ad364e35\Microsoft.GroupPolicy.Management.dll
and
C:\Windows\assembly\GAC_32\Microsoft.GroupPolicy.Management.Interop\2.0.0.0__31bf3856ad364e35\Microsoft.GroupPolicy.Management.Interop.dll
This framework 2.0, so there are mixed code, and you must use app.config: http://msmvps.com/blogs/rfennell/archive/2010/03/27/mixed-mode-assembly-is-built-against-version-v2-0-50727-error-using-net-4-development-web-server.aspx
I made it like that.
using System.Collections.ObjectModel;
using Microsoft.GroupPolicy;
using Microsoft.Win32;
/// <summary>
/// Change user's registry policy
/// </summary>
/// <param name="gpoName">The name of Group Policy Object(DisplayName)</param>
/// <param name="keyPath">Is KeyPath(like string path=#"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer")</param>
/// <param name="typeOfKey">DWord, ExpandString,... e.t.c </param>
/// <param name="parameterName">Name of parameter</param>
/// <param name="value">Value</param>
/// <returns>result: true\false</returns>
public bool ChangePolicyUser(string gpoName, string keyPath, RegistryValueKind typeOfKey, string parameterName, object value)
{
try
{
RegistrySetting newSetting = new PolicyRegistrySetting();
newSetting.Hive = RegistryHive.CurrentUser;
newSetting.KeyPath = keyPath;
bool contains = false;
//newSetting.SetValue(parameterName, value, typeOfKey);
switch (typeOfKey)
{
case RegistryValueKind.String:
newSetting.SetValue(parameterName, (string)value, typeOfKey);
break;
case RegistryValueKind.ExpandString:
newSetting.SetValue(parameterName, (string)value, typeOfKey);
break;
case RegistryValueKind.DWord:
newSetting.SetValue(parameterName, (Int32)value);
break;
case RegistryValueKind.QWord:
newSetting.SetValue(parameterName, (Int64)value);
break;
case RegistryValueKind.Binary:
newSetting.SetValue(parameterName, (byte[])value);
break;
case RegistryValueKind.MultiString:
newSetting.SetValue(parameterName, (string[])value, typeOfKey);
break;
}
Gpo gpoTarget = _gpDomain.GetGpo(gpoName);
RegistryPolicy registry = gpoTarget.User.Policy.GetRegistry(false);
try
{
ReadOnlyCollection<RegistryItem> items = gpoTarget.User.Policy.GetRegistry(false).Read(newSetting.Hive, keyPath);
foreach (RegistryItem item in items)
{
if (((RegistrySetting) item).ValueName == parameterName)
{
contains = true;
}
}
registry.Write((PolicyRegistrySetting) newSetting, !contains);
registry.Save(false);
return true;
}
catch (ArgumentException)
{
registry.Write((PolicyRegistrySetting)newSetting, contains);
registry.Save(true);
return true;
}
}
catch (Exception)
{
return false;
}
}
Related
I have a service that is responsible for starting/monitoring an interactive process in user session once user logins and console session connects.
Service is set to start automatically so its up and running before user login.
All work fine on first login and i get the user process started/restarted correctly.
What happens is that if user signs out and re logins service is no longer able to start a user process correctly.
CreateProcessAsUser returns no error but as soon as user process is started its exiting with -1073741502 (0xC0000142) exit code.
If i restart the service then its again able to launch user process without any errors.
I can post full source of how the service creates user process if required.
Edit
try
{
//WE ALREADY HAVE A CLIENT ATTACHED , DONT START A NEW ONE
if (ClientProcessId != null)
return;
var ACTIVE_CONSOLE_SESSION = ListSessions()
.Where(SESSION => SESSION.State == WTS_CONNECTSTATE_CLASS.WTSActive)
.FirstOrDefault();
if (ACTIVE_CONSOLE_SESSION == null)
return;
CONSOLE_SESSION_ID = (uint)ACTIVE_CONSOLE_SESSION.Id;
IntPtr USER_TOKEN = IntPtr.Zero;
IntPtr ENVIRONMENT = IntPtr.Zero;
IntPtr LINKED_TOKEN = IntPtr.Zero;
try
{
try
{
if (!Wtsapi32.WTSQueryUserToken(CONSOLE_SESSION_ID.Value, out USER_TOKEN))
throw new Win32Exception();
}
catch (Win32Exception wex)
{
EntryPoint.TryWriteToCacheLog($"{nameof(Wtsapi32.WTSQueryUserToken)} : console session id {CONSOLE_SESSION_ID} error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
throw;
}
try
{
if (!Userenv.CreateEnvironmentBlock(out ENVIRONMENT, USER_TOKEN, true))
throw new Win32Exception();
}
catch (Win32Exception wex)
{
EntryPoint.TryWriteToCacheLog($"{nameof(Userenv.CreateEnvironmentBlock)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
throw;
}
try
{
LINKED_TOKEN = CoreProcess.GetLinkedTokeIfRequiered(USER_TOKEN);
}
catch (Win32Exception wex)
{
EntryPoint.TryWriteToCacheLog($"{nameof(CoreProcess.GetLinkedTokeIfRequiered)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
throw;
}
//GET PROCESS PATHS
string FILE_NAME = EntryPoint.PROCESS_FULL_FILE_NAME;
string WORKING_DIRECTORY = EntryPoint.PROCESS_DIRECTORY;
//GET CURRENT COMMAND LINE ARGUMENTS
var CMD_ARGS = Environment.GetCommandLineArgs();
//FIRST ARGUMENT WILL ALWAYS HAVE FULL PROCESS PATH,OTHER ARGUMENTS ARE OPTIONAL
var ARGUMENTS_STRING = CMD_ARGS
.Skip(1)
.DefaultIfEmpty()
.Aggregate((first, next) => ' ' + first + ' ' + next);
var ARGUMENTS = new StringBuilder(ARGUMENTS_STRING);
var START_INFO = new STARTUPINFO();
START_INFO.cb = Marshal.SizeOf(START_INFO);
START_INFO.lpDesktop = #"winsta0\default";
var PROCESS_INFO = new PROCESS_INFORMATION();
uint dwCreationFlags = NORMAL_PRIORITY_CLASS | (int)(PROCESS_CREATE_FLAG.CREATE_NEW_CONSOLE | PROCESS_CREATE_FLAG.CREATE_UNICODE_ENVIRONMENT);
try
{
if (!AdvApi32.CreateProcessAsUser(LINKED_TOKEN,
FILE_NAME,
ARGUMENTS,
IntPtr.Zero,
IntPtr.Zero,
true,
dwCreationFlags,
ENVIRONMENT,
WORKING_DIRECTORY,
ref START_INFO,
out PROCESS_INFO))
throw new Win32Exception();
if (PROCESS_INFO.hThread != IntPtr.Zero)
{
ClientProcessId = PROCESS_INFO.dwProcessId;
ClientSessionId = CONSOLE_SESSION_ID;
EntryPoint.TryWriteToCacheLog($"{nameof(AdvApi32.CreateProcessAsUser)} : Created porocess {ClientProcessId} in session {CONSOLE_SESSION_ID}.");
}
}
catch (Win32Exception wex)
{
EntryPoint.TryWriteToCacheLog($"{nameof(AdvApi32.CreateProcessAsUser)} : error {wex.ErrorCode} , native error {wex.NativeErrorCode}");
throw;
}
}
catch (Win32Exception wex)
{
switch (wex.NativeErrorCode)
{
case 5:
case 1008:
tryCount++;
if (tryCount >= START_RETRIES)
throw;
Thread.Sleep(RETRY_WAIT_SPAN);
if (DisableCallBacks)
return;
CreateProcess(tryCount);
break;
default:
throw;
}
}
catch
{
throw;
}
finally
{
Userenv.DestroyEnvironmentBlock(ENVIRONMENT);
Kernel32.CloseHandle(USER_TOKEN);
if (USER_TOKEN != LINKED_TOKEN)
Kernel32.CloseHandle(LINKED_TOKEN);
}
}
catch (Exception ex)
{
EntryPoint.TryWriteToCacheLog($"{nameof(CreateProcess)} failed after {tryCount} retries, console seesion id {(CONSOLE_SESSION_ID != null ? CONSOLE_SESSION_ID.ToString() : "Unobtained")}.", ex.ToString());
}
finally
{
Monitor.Exit(CREATE_LOCK);
}
public static TOKEN_ELEVATION_TYPE GetTokenElevationLevel(IntPtr hToken)
{
int TOKEN_INFO_LENGTH = Marshal.SizeOf(typeof(int));
IntPtr TOKEN_INFO_POINTER = Marshal.AllocHGlobal(TOKEN_INFO_LENGTH);
try
{
if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevationType, TOKEN_INFO_POINTER, TOKEN_INFO_LENGTH, out TOKEN_INFO_LENGTH))
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
return (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(TOKEN_INFO_POINTER);
}
catch
{
throw;
}
finally
{
if (TOKEN_INFO_POINTER != IntPtr.Zero)
Marshal.FreeHGlobal(TOKEN_INFO_POINTER);
}
}
public static IntPtr GetLinkedTokeIfRequiered(IntPtr hToken)
{
var TOKEN_ELEVATION = GetTokenElevationLevel(hToken);
if (TOKEN_ELEVATION != TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited)
return hToken;
int TOKEN_INFO_LENGHT = Marshal.SizeOf(typeof(IntPtr));
IntPtr LINKED_TOKEN_INFO = Marshal.AllocHGlobal(TOKEN_INFO_LENGHT);
try
{
if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, LINKED_TOKEN_INFO, TOKEN_INFO_LENGHT, out TOKEN_INFO_LENGHT))
throw new Win32Exception();
return Marshal.ReadIntPtr(LINKED_TOKEN_INFO);
}
finally
{
if (LINKED_TOKEN_INFO != IntPtr.Zero)
Marshal.Release(LINKED_TOKEN_INFO);
}
}
Thanks you for posting the rest of the code. I see you are launching the process elevated. I added this to my test service and it still works correctly.
But I think the problem might be this line in GetLinkedTokeIfRequiered():
Marshal.Release(LINKED_TOKEN_INFO);
That should obviously be:
Marshal.FreeHGlobal(LINKED_TOKEN_INFO);
Fix that and it might just work. As things are, I'm amazed it's not crashing.
Not easy for me, digging through this. C# interop not my strong suit.
For the OP's benefit, complete source code of my test service, written in C++, which works:
#include <windows.h>
#include <wtsapi32.h>
#include <userenv.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "wtsapi32.lib")
#pragma comment (lib, "userenv.lib")
#pragma comment (lib, "advapi32.lib")
DWORD report_error (const char *operation)
{
DWORD err = GetLastError ();
return err;
}
// Launch notepad as currently logged-on user
DWORD LaunchProcess (DWORD SessionId, const char **failed_operation)
{
HANDLE hToken;
BOOL ok = WTSQueryUserToken (SessionId, &hToken);
if (!ok)
return report_error (*failed_operation = "WTSQueryUserToken");
void *environment = NULL;
ok = CreateEnvironmentBlock (&environment, hToken, TRUE);
if (!ok)
{
CloseHandle (hToken);
return report_error (*failed_operation = "CreateEnvironmentBlock");
}
TOKEN_LINKED_TOKEN lto;
DWORD nbytes;
ok = GetTokenInformation (hToken, TokenLinkedToken, <o, sizeof (lto), &nbytes);
if (ok)
{
CloseHandle (hToken);
hToken = lto.LinkedToken;
}
STARTUPINFO si = { sizeof (si) } ;
PROCESS_INFORMATION pi = { } ;
si.lpDesktop = "winsta0\\default";
// Do NOT want to inherit handles here, surely
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | /* CREATE_NEW_CONSOLE | */ CREATE_UNICODE_ENVIRONMENT;
ok = CreateProcessAsUser (hToken, "c:\\windows\\system32\\notepad.exe", NULL, NULL, NULL, FALSE,
dwCreationFlags, environment, NULL, &si, &pi);
DestroyEnvironmentBlock (environment);
CloseHandle (hToken);
if (!ok)
return report_error (*failed_operation = "CreateProcessAsUser");
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
}
// Determine the session ID of the currently logged-on user
DWORD GetCurrentSessionId ()
{
WTS_SESSION_INFO *pSessionInfo;
DWORD n_sessions = 0;
BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
if (!ok)
return 0;
DWORD SessionId = 0;
for (DWORD i = 0; i < n_sessions; ++i)
{
if (pSessionInfo [i].State == WTSActive)
{
SessionId = pSessionInfo [i].SessionId;
break;
}
}
WTSFreeMemory (pSessionInfo);
return SessionId;
}
#define SERVICE_NAME __T ("demo_service")
bool quit;
// CtrlHandler callback
DWORD WINAPI CtrlHandler (DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
if (dwControl == SERVICE_CONTROL_STOP)
quit = true;
return NO_ERROR;
}
// SvcMain callback
VOID WINAPI SvcMain (DWORD dwArgc, LPTSTR *lpszArgv)
{
// Register for callbacks
SERVICE_STATUS_HANDLE sh = RegisterServiceCtrlHandlerEx (SERVICE_NAME, CtrlHandler, NULL);
// Tell the SCM that we are up and running
SERVICE_STATUS ss = { };
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
SetServiceStatus (sh, &ss);
TCHAR buf [256];
const TCHAR *title = __T ("(c) 2018 Contoso Corporation");
while (!quit)
{
DWORD response = IDOK;
DWORD SessionId = GetCurrentSessionId ();
if (SessionId == 0)
{
Sleep (2000);
continue;
}
// Pop-up a message on the screen of the currently logged-on user (session 1)
_stprintf (buf, __T ("Ready to launch..., SessionId = %d"), SessionId);
WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (TCHAR *) title, _tcslen (title),
buf, _tcslen (buf), MB_OKCANCEL, 0, &response, TRUE);
if (response == IDCANCEL)
break;
const char *failed_operation = "";
DWORD dwResult = LaunchProcess (SessionId, &failed_operation);
// Report results
_stprintf (buf, __T ("LaunchProcess returned %lx from %s"), dwResult, failed_operation);
WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (char *) title, _tcslen (title),
buf, _tcslen (buf), MB_OK, 0, &response, TRUE);
FILE *logfile = fopen ("g:\\temp\\service.log", "at");
if (logfile)
{
fprintf (logfile, "%s\n", buf);
fclose (logfile);
}
}
// Tell the SCM we are going away and exit
ss.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (sh, &ss);
}
// main
int main (void)
{
SERVICE_TABLE_ENTRY DispatchTable [] =
{
{ SERVICE_NAME, SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped.
// The process should simply terminate when the call returns.
StartServiceCtrlDispatcher (DispatchTable);
return 0;
}
Error is STATUS_DLL_INIT_FAILED which means a dynamically loaded DLL is missing. Maybe you specify the wrong working directory and some call to LoadLibrary("lib_with_no_path.dll") fails?
You should be able to see which DLL is missing if you look in the Event Viewer.
I have written a dll which call windows spooler api to get printer name and printer driver name, it works fine in console application. However, I got nothing when I call the dll in windows services application.
Here is my dll source code(i.e. printerUtility):
PRINTER_ENUM_LOCAL = 0x00000002,
PRINTER_ENUM_CONNECTIONS = 0x00000004,
public ArrayList getAllLocalPrinterInfo(LogWriter logger)
{
ArrayList printerInfoList = getAllLocalPrinterInfoV2(PrinterEnumFlags.PRINTER_ENUM_LOCAL | PrinterEnumFlags.PRINTER_ENUM_CONNECTIONS);
foreach (PrinterInfoV2 printerInfo in printerInfoList)
{
logger.write("In dll Printer Name pointer:" + printerInfo.pPrinterName+"\nIn dll Printer Name:" + Marshal.PtrToStringAnsi(printerInfo.pPrinterName));
logger.write("In dll Driver Name pointer:" + printerInfo.pDriverName+"\nIn dll Driver Name:" + Marshal.PtrToStringAnsi(printerInfo.pDriverName));
}
//return allLocalPrinterInfo;
return printerInfoList;
}
public ArrayList getAllLocalPrinterInfoV2(PrinterEnumFlags Flags)
{
return enumPrinters(Flags, 2);
}
private ArrayList enumPrinters(PrinterEnumFlags Flags, UInt32 level)
{
bool result;
ArrayList printerInfo = new ArrayList();
UInt32 returned, needed = 0;
UInt32 flags = Convert.ToUInt32(Flags);
//find required size for the buffer
result = EnumPrinters(flags, null, level, IntPtr.Zero, 0, out needed, out returned);
if (Marshal.GetLastWin32Error() != Convert.ToUInt32(CredUIReturnCodes.ERROR_INSUFFICIENT_BUFFER))
{
throw new Exception("EnumPrinters 1 failure, error code=" + Marshal.GetLastWin32Error());
}
else
{
IntPtr buffer = Marshal.AllocHGlobal((int)needed);
result = EnumPrinters(flags, null, level, buffer, needed, out needed, out returned);
if (result)
{
if ((level > 0) || (level < 5))
{
Type type = typeof(PrinterInfoV1);
switch (level)
{
case 1:
type = typeof(PrinterInfoV1);
break;
case 2:
type = typeof(PrinterInfoV2);
break;
case 3:
type = typeof(PrinterInfoV3);
break;
case 4:
type = typeof(PrinterInfoV4);
break;
}
int offset = buffer.ToInt32();
int increment = Marshal.SizeOf(type);
for (int i = 0; i < returned; i++)
{
printerInfo.Add(Marshal.PtrToStructure(new IntPtr(offset), type));
offset += increment;
}
Marshal.FreeHGlobal(buffer);
return printerInfo;
}
else
{
Marshal.FreeHGlobal(buffer);
throw new Exception("The value of level is out of range");
}
}
else
{
Marshal.FreeHGlobal(buffer);
throw new Exception("EnumPrinters 2 failure, error code=" + Marshal.GetLastWin32Error());
}
}
}
[DllImport("winspool.drv", SetLastError = true)]
static extern bool EnumPrinters([InAttribute()] UInt32 Flags,
[InAttribute()] string pPrinterName,
[InAttribute()] UInt32 Level,
[OutAttribute()] IntPtr pPrinter,
[InAttribute()] UInt32 cbBuf,
[OutAttribute()] out UInt32 pcbNeeded,
[OutAttribute()] out UInt32 pcReturned);
The LogWriter Source code:
public class LogWriter
{
EventLog eventLog = null;
public LogWriter(System.Diagnostics.EventLog elog)
{
this.eventLog = elog;
}
public void write(String msg)
{
if (eventLog != null)
{
eventLog.WriteEntry(msg);
}
}
}
Here is services source code:
protected override void OnStart(string[] args)
{
serviceStatus.dwWaitHint = 100000;
serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
printerMonitorList = new ArrayList();
try
{
ArrayList printerInfoList = printerUtility.getAllLocalPrinterInfo(logger);
foreach (PrinterInfoV2 printerInfo in printerInfoList)
{
logger.write("In service Printer Name pointer:" + printerInfo.pPrinterName+"\nIn service Printer Name:" + Marshal.PtrToStringAnsi(printerInfo.pPrinterName));
logger.write("In service Driver Name pointer:" + printerInfo.pDriverName+"\nIn service Driver Name:" + Marshal.PtrToStringAnsi(printerInfo.pDriverName));
}
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
logger.write(this.ServiceName + " Has Been Started");
}
catch (Exception err)
{
logger.write("Print Alert Service cannot be started:"+err.Message);
}
In event viewer, I got an empty string for both printer name and printer driver name.
However, I found that the dll return a pointer for both printer name and printer driver name. Therefore, I suspect whether the Marshal.PtrToStringAnsi function not working in window services environment.
In C# I would like test the availability of a network path, what authentication needed. I am using the following function:
private bool TestDrive(string path,string userName,string domain, string password)
{
if (userName == "")
return true;
//else authentication needed
try
{
using (new CImpersonator(userName, domain, password))
{
using (FileStream fs = File.Create(path+"\\1.txt"))
{ }
File.Delete(path + "\\1.txt");
}
return true;
}
catch (Exception ex)
{
return false;
}
}
public CImpersonator(
string userName,
string domainName,
string password,
bool checkUserName = true)
{
//Check if user exists
if (checkUserName && !CCheckUsername.UserExists(userName))
return;
this.ImpersonateValidUser(userName, domainName, password);
}
private void ImpersonateValidUser(
string userName,
string domain,
string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (RevertToSelf())
{
if (LogonUser(
userName,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref token) != 0)
{
if (DuplicateToken(token, (int)System.Management.ImpersonationLevel.Impersonate, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
_impersonationContext = tempWindowsIdentity.Impersonate();
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = (IntPtr)System.Diagnostics.Process.GetCurrentProcess().Id;
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ALL_ACCESS, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
retVal = LookupPrivilegeValue(null, SE_SYSTEMTIME_NAME, ref tp.Luid);
retVal = AdjustTokenPrivileges(token, false, ref tp, Marshal.SizeOf(typeof(TokPriv1Luid)), IntPtr.Zero, IntPtr.Zero);
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
}
}
The problem is that when I use this for test a network path in an another domain, then the TestDrive function returns false. Of course the domain, username and password are good, the user rights are good.
It is strange that the function works good, when I test it in the same domain.
Thank you for all ideas!
I would first try the following:
Folder.Exists(String Path);
This will however only work on a Path where you have read access, so if the network path isn't mapped with the credentials this method will return false, but you can use the above method and if it returns false, map the network path (this question answersthe how-to on that) with those different credentials
and then perform the:
Folder.Exists(String Path);
once more. Afterwards you can disconnect the network drive again.
I am trying to have my program determine if it has permission to delete a registry key in C#. I have been trying this code which is returning true when in fact I don't have the right permissions for the registry key.
public static bool CanDeleteKey(RegistryKey key)
{
try
{
if (key.SubKeyCount > 0)
{
bool ret = false;
foreach (string subKey in key.GetSubKeyNames())
{
ret = CanDeleteKey(key.OpenSubKey(subKey));
if (!ret)
break;
}
return ret;
}
else
{
RegistryPermission r = new
RegistryPermission(RegistryPermissionAccess.AllAccess, key.ToString());
r.Demand();
return true;
}
}
catch (SecurityException)
{
return false;
}
}
The registry key that I am passing to this function is HKEY_CLASSES_ROOT\eHomeSchedulerService.TVThumbnailCache. It should be re-cursing to the sub key CLSID and returning false because the Full Control permission is only set for TrustedInstaller.
Here are the permissions for HKEY_CLASSES_ROOT\eHomeSchedulerService.TVThumbnailCache\CLSID from regedit:
I should note that I am running the code with Administrative privileges. Also, I know I can just use a try-catch block when I am deleting the registry key but I would like to know if I can delete it beforehand.
I was able to do some digging on Google and I came up with this code which seems to do the trick:
/// Checks if we have permission to delete a registry key
/// </summary>
/// <param name="key">Registry key</param>
/// <returns>True if we can delete it</returns>
public static bool CanDeleteKey(RegistryKey key)
{
try
{
if (key.SubKeyCount > 0)
{
bool ret = false;
foreach (string subKey in key.GetSubKeyNames())
{
ret = CanDeleteKey(key.OpenSubKey(subKey));
if (!ret)
break;
}
return ret;
}
else
{
System.Security.AccessControl.RegistrySecurity regSecurity = key.GetAccessControl();
foreach (System.Security.AccessControl.AuthorizationRule rule in regSecurity.GetAccessRules(true, false, typeof(System.Security.Principal.NTAccount)))
{
if ((System.Security.AccessControl.RegistryRights.Delete & ((System.Security.AccessControl.RegistryAccessRule)(rule)).RegistryRights) != System.Security.AccessControl.RegistryRights.Delete)
{
return false;
}
}
return true;
}
}
catch (SecurityException)
{
return false;
}
}
Having read permissions allows you to open the key. Try using:
key.DeleteSubKey(subkey)
Is there a way to execute the command for a progid verb without having to go digging in the registry and doing string manipulation?
Using ShObjIdl.idl I can run the following command to get the ProgId for the default browser:
var reg = new ShellObjects.ApplicationAssociationRegistration();
string progID;
reg.QueryCurrentDefault("http", ShellObjects.ASSOCIATIONTYPE.AT_URLPROTOCOL, ShellObjects.ASSOCIATIONLEVEL.AL_EFFECTIVE, out progID);
This gives me "ChromeHTML.FHXQEQDDJYXVQSFWM2SVMV5GNA". In the registry I can see this progid has the following shell/open/command:
"C:\Users\Paul\AppData\Local\Google\Chrome\Application\chrome.exe" -- "%1"
Is there an API that I can pass the ProgId to, along with the verb and argument and it will run it?
One route I went down is using ShellExecuteEx:
var shellExecuteInfo = new SHELLEXECUTEINFO();
shellExecuteInfo.cbSize = Marshal.SizeOf(shellExecuteInfo);
shellExecuteInfo.fMask = SEE_MASK_CLASSNAME;
shellExecuteInfo.hwnd = IntPtr.Zero;
shellExecuteInfo.lpVerb = "open";
shellExecuteInfo.lpFile = "google.com";
shellExecuteInfo.nShow = SW_SHOWNORMAL;
shellExecuteInfo.lpClass = "http";
ShellExecuteEx(ref shellExecuteInfo);
However this fails with a 'Windows cannot find' error due to Window's doing checking on lpFile which I don't want to happen as it isn't relevant for a URL (from: http://blogs.msdn.com/b/oldnewthing/archive/2010/07/01/10033224.aspx )
This is the solution I have come up with:
private static void Main(string[] args)
{
if (!OpenUrlInDefaultBrowser("google.com"))
Console.WriteLine("An error happened");
}
[DllImport("Shlwapi.dll")]
private static extern int AssocQueryString(ASSOCF flags, ASSOCSTR str, string pszAssoc, string pszExtra, StringBuilder pszOut, ref uint pcchOut);
private enum ASSOCF
{
ASSOCF_NONE = 0x00000000
}
private enum ASSOCSTR
{
ASSOCSTR_COMMAND = 1
}
[DllImport("Shell32.dll", CharSet=CharSet.Auto)]
private static extern int SHEvaluateSystemCommandTemplate(string pszCmdTemplate, out string ppszApplication, out string ppszCommandLine, out string ppszParameters);
private static bool OpenUrlInDefaultBrowser(string url)
{
string browserProgId;
if (!GetDefaultBrowserProgId(out browserProgId))
return false;
string browserCommandTemplate;
if (!GetCommandTemplate(browserProgId, out browserCommandTemplate))
return false;
string browserExecutable;
string parameters;
if (!EvaluateCommandTemplate(browserCommandTemplate, out browserExecutable, out parameters))
return false;
parameters = ReplaceSubstitutionParameters(parameters, url);
try
{
Process.Start(browserExecutable, parameters);
}
catch (InvalidOperationException) { return false; }
catch (Win32Exception) { return false; }
catch (FileNotFoundException) { return false; }
return true;
}
private static bool GetDefaultBrowserProgId(out string defaultBrowserProgId)
{
try
{
// midl "C:\Program Files (x86)\Windows Kits\8.0\Include\um\ShObjIdl.idl"
// tlbimp ShObjIdl.tlb
var applicationAssociationRegistration = new ApplicationAssociationRegistration();
applicationAssociationRegistration.QueryCurrentDefault("http", ShellObjects.ASSOCIATIONTYPE.AT_URLPROTOCOL, ShellObjects.ASSOCIATIONLEVEL.AL_EFFECTIVE, out defaultBrowserProgId);
}
catch (COMException)
{
defaultBrowserProgId = null;
return false;
}
return !string.IsNullOrEmpty(defaultBrowserProgId);
}
private static bool GetCommandTemplate(string defaultBrowserProgId, out string commandTemplate)
{
var commandTemplateBufferSize = 0U;
AssocQueryString(ASSOCF.ASSOCF_NONE, ASSOCSTR.ASSOCSTR_COMMAND, defaultBrowserProgId, "open", null, ref commandTemplateBufferSize);
var commandTemplateStringBuilder = new StringBuilder((int)commandTemplateBufferSize);
var hresult = AssocQueryString(ASSOCF.ASSOCF_NONE, ASSOCSTR.ASSOCSTR_COMMAND, defaultBrowserProgId, "open", commandTemplateStringBuilder, ref commandTemplateBufferSize);
commandTemplate = commandTemplateStringBuilder.ToString();
return hresult == 0 && !string.IsNullOrEmpty(commandTemplate);
}
private static bool EvaluateCommandTemplate(string commandTemplate, out string application, out string parameters)
{
string commandLine;
var hresult = SHEvaluateSystemCommandTemplate(commandTemplate, out application, out commandLine, out parameters);
return hresult == 0 && !string.IsNullOrEmpty(application) && !string.IsNullOrEmpty(parameters);
}
private static string ReplaceSubstitutionParameters(string parameters, string replacement)
{
// Not perfect but good enough for this purpose
return parameters.Replace("%L", replacement)
.Replace("%l", replacement)
.Replace("%1", replacement);
}
The explicit class does not remove the requirement that lpFile refer to a valid resource (file or URL). The class specifies how the resource should be executed (rather than inferring the class from the file type or URL protocol), but you still have to pass a valid resource. google.com is treated as a file name since it is not a URL, and the file does not exist, so you get the "not found" error.
The general case of what you're trying to do is more complicated than just extracting a command line, because most browsers use DDE rather than command lines as their primary invoke. (The command line is a fallback when DDE fails.)
But if you really want to execute a command line, you can use AssocQueryString to get the ASSOCSTR_COMMAND, and then perform the insertion via SHEvaluateSystemCommandTemplate to get the command line to execute.
The fundamental error here is that you omitted http:// from the FileName. Add that and all will be well.
shellExecuteInfo.lpFile = "http://google.com";
You don't need to set lpClass at all. The fact that lpFile begins with http:// determines the class.
Rather than calling ShellExecuteEx yourself, you may as well the Process to do it for you:
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = #"http://google.com";
psi.UseShellExecute = true;
Process.Start(psi);
Or even:
Process.Start(#"http://google.com");
My code that include check to prevent from some common errors... Hope it helps :-)
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace HQ.Util.Unmanaged
{
/// <summary>
/// Usage: string executablePath = FileAssociation.GetExecFileAssociatedToExtension(pathExtension, "open");
/// Usage: string command FileAssociation.GetExecCommandAssociatedToExtension(pathExtension, "open");
/// </summary>
public static class FileAssociation
{
/// <summary>
///
/// </summary>
/// <param name="ext"></param>
/// <param name="verb"></param>
/// <returns>Return null if not found</returns>
public static string GetExecCommandAssociatedToExtension(string ext, string verb = null)
{
if (ext[0] != '.')
{
ext = "." + ext;
}
string executablePath = FileExtentionInfo(AssocStr.Command, ext, verb);
// Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher
if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) &&
!executablePath.ToLower().EndsWith(".dll"))
{
if (executablePath.ToLower().EndsWith("openwith.exe"))
{
return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file
}
return executablePath;
}
return executablePath;
}
/// <summary>
///
/// </summary>
/// <param name="ext"></param>
/// <param name="verb"></param>
/// <returns>Return null if not found</returns>
public static string GetExecFileAssociatedToExtension(string ext, string verb = null)
{
if (ext[0] != '.')
{
ext = "." + ext;
}
string executablePath = FileExtentionInfo(AssocStr.Executable, ext, verb); // Will only work for 'open' verb
if (string.IsNullOrEmpty(executablePath))
{
executablePath = FileExtentionInfo(AssocStr.Command, ext, verb); // required to find command of any other verb than 'open'
// Extract only the path
if (!string.IsNullOrEmpty(executablePath) && executablePath.Length > 1)
{
if (executablePath[0] == '"')
{
executablePath = executablePath.Split('\"')[1];
}
else if (executablePath[0] == '\'')
{
executablePath = executablePath.Split('\'')[1];
}
}
}
// Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher
if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) &&
!executablePath.ToLower().EndsWith(".dll"))
{
if (executablePath.ToLower().EndsWith("openwith.exe"))
{
return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file
}
return executablePath;
}
return executablePath;
}
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut);
private static string FileExtentionInfo(AssocStr assocStr, string doctype, string verb)
{
uint pcchOut = 0;
AssocQueryString(AssocF.Verify, assocStr, doctype, verb, null, ref pcchOut);
Debug.Assert(pcchOut != 0);
if (pcchOut == 0)
{
return "";
}
StringBuilder pszOut = new StringBuilder((int)pcchOut);
AssocQueryString(AssocF.Verify, assocStr, doctype, verb, pszOut, ref pcchOut);
return pszOut.ToString();
}
[Flags]
public enum AssocF
{
Init_NoRemapCLSID = 0x1,
Init_ByExeName = 0x2,
Open_ByExeName = 0x2,
Init_DefaultToStar = 0x4,
Init_DefaultToFolder = 0x8,
NoUserSettings = 0x10,
NoTruncate = 0x20,
Verify = 0x40,
RemapRunDll = 0x80,
NoFixUps = 0x100,
IgnoreBaseClass = 0x200
}
public enum AssocStr
{
Command = 1,
Executable,
FriendlyDocName,
FriendlyAppName,
NoOpen,
ShellNewValue,
DDECommand,
DDEIfExec,
DDEApplication,
DDETopic
}
}
}