Ho costruito un'applicazione per Windows Embedded utilizzando WinForms.
Allo start dell'applicazioen ho bisogno di sincronizzare la data in modalità offline e quindi con un form faccio mettere all'utente la data corrente e poi utilizzo la SetLocalTime (tramite PInvoke) per aggiornare la data di sistema.
La chiamata ritorna una eccezione con codice 5 (Access Denied).
Siccome l'utente corrente non è amministratore locale della macchina uso prima l'AdjustTokenPrivileges per dare al processo il permesso SE_SYSTEMTIME_NAME e poi l'Impersonation per avere i diritti da amministratore.
C'è qualcuno che sa da cosa può dipendere?
Questo è il codice C# della Classe Impersonation:
class Impersonation
{
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
// creates duplicate token handle
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
private IntPtr pExistingTokenHandle = new IntPtr(0);
private IntPtr pDuplicateTokenHandle = new IntPtr(0);
public Impersonation()
{
// initialize tokens
pExistingTokenHandle = new IntPtr(0);
pDuplicateTokenHandle = new IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
}
public WindowsImpersonationContext ImpersonateUser(string sUsername, string sDomain, string sPassword)
{
if (sDomain == "")
sDomain = System.Environment.MachineName;
string sResult = null;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
// get handle to token
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref pExistingTokenHandle);
// did impersonation fail?
if (false == bImpersonated)
{
int nErrorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(nErrorCode);
}
// Get identity before impersonation
bool bRetVal = DuplicateToken(pExistingTokenHandle,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (false == bRetVal)
{
int nErrorCode = Marshal.GetLastWin32Error();
// close existing handle
CloseHandle(pExistingTokenHandle);
// show the reason why DuplicateToken failed
MessageBox.Show(sResult, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
else
{
// create new identity using new primary token
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
return impersonatedUser;
}
}
public void CloseHandles()
{
if (pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if (pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
// group type enum
public enum SECURITY_IMPERSONATION_LEVEL : int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
Questo è il codice della mia classe SystemDateTimeUtils:
class SystemDateTimeUtils
{
#region pinvokes
[DllImport("kernel32.dll")]
private static extern bool SetLocalTime(ref SYSTEMTIME time);
[DllImport("kernel32.dll")]
private static extern bool SetSystemTime(ref SYSTEMTIME systime);
[DllImport("kernel32.dll")]
private static extern int GetLastError();
[DllImport("kernel32.dll")]
private static extern void GetLocalTime(out SYSTEMTIME time);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool OpenProcessToken(int ProcessHandle,
int DesiredAccess, ref int TokenHandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetCurrentProcess();
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName,
[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool AdjustTokenPrivileges(int TokenHandle, int DisableAllPrivileges,
[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES NewState,
int BufferLength,
[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES PreviousState, ref int ReturnLength);
#endregion
#region Constants
private const int ANYSIZE_ARRAY = 1;
private const string SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege";
private const int SE_PRIVILEGE_ENABLED = 0x00000002;
private const int TOKEN_QUERY = 0x0008;
private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
#endregion
#region SetLocalTime
public static bool SetLocalTime(DateTime systemTime, out int errorCode)
{
AdjustPrivileges();
SYSTEMTIME time = GetSystemTime(ref systemTime);
WindowsImpersonationContext newUser = null;
bool result = false;
Impersonation imp = new Impersonation();
try
{
newUser = imp.ImpersonateUser(<my_username>, ".", <my_password>);
result = SetLocalTime(ref time);
if (result)
result = SetLocalTime(ref time);
if (!result)
errorCode = GetLastError();
else
errorCode = 0;
}
finally
{
imp.CloseHandles();
if (newUser != null)
newUser.Undo();
}
return result;
}
#endregion
#region GetSystemTime
private static SYSTEMTIME GetSystemTime(ref DateTime systemTime)
{
SYSTEMTIME time = new SYSTEMTIME();
time.wDay = (ushort)systemTime.Day;
time.wDayOfWeek = (ushort)(systemTime.DayOfWeek);
time.wHour = (ushort)systemTime.Hour;
time.wMilliseconds = (ushort)systemTime.Millisecond;
time.wMinute = (ushort)systemTime.Minute;
time.wMonth = (ushort)systemTime.Month;
time.wSecond = (ushort)systemTime.Second;
time.wYear = (ushort)systemTime.Year;
return time;
}
#endregion
public static bool AdjustPrivileges()
{
TOKEN_PRIVILEGES tkNew = new TOKEN_PRIVILEGES();
tkNew.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];
TOKEN_PRIVILEGES tkOld = new TOKEN_PRIVILEGES();
tkOld.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];
LUID luid = new LUID();
int token = -1;
int oldluidSize = 0;
if (LookupPrivilegeValue(null, SE_SYSTEMTIME_NAME, ref luid))
{
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token))
{
tkNew.PrivilegeCount = 1;
tkNew.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tkNew.Privileges[0].Luid = luid;
int luidSize = Marshal.SizeOf(typeof(TOKEN_PRIVILEGES));
if (AdjustTokenPrivileges(token, 0, ref tkNew, luidSize, ref tkOld, ref oldluidSize))
{
return true;
}
}
}
return false;
}
#region Strutture
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct SYSTEMTIME
{
public ushort wYear, wMonth, wDayOfWeek, wDay,
wHour, wMinute, wSecond, wMilliseconds;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public int LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = ANYSIZE_ARRAY)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
#endregion
}
e questo è il codice della chiamata:
int errorCode = 0;
SystemDateTimeUtils.SetLocalTime(currentDate, out errorCode);
if (errorCode != 0)
MessageBox.Show("Error setting local system time to desired value: " +
"error code " + errorCode, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);