For simple interactions, a service can display a message window on a user session through the Wtssendmessage function. For some complex UI interactions, you must call CreateProcessAsUser or other methods (WCF,. NET remoting, and so on, to create an application interface on a desktop user.
wtssendmessage function
If the service simply sends a message window to the desktop user session, it can be implemented using the Wtssendmessage function. First, add a Interop.cs class to the previous downloaded code and add the following code to the class:
Copy Code code as follows:
public static IntPtr wts_current_server_handle = IntPtr.Zero;
public static void ShowMessageBox (String message, string title)
{
int resp = 0;
Wtssendmessage (
Wts_current_server_handle,
Wtsgetactiveconsolesessionid (),
Title, title. Length,
message, message. Length,
0, 0, out resp, false);
}
[DllImport ("kernel32.dll", SetLastError = True)]
public static extern int Wtsgetactiveconsolesessionid ();
[DllImport ("Wtsapi32.dll", SetLastError = True)]
public static extern bool Wtssendmessage (
IntPtr Hserver,
int SessionId,
String Ptitle,
int Titlelength,
String PMessage,
int Messagelength,
int Style,
int Timeout,
out int Presponse,
BOOL bwait);
The wtssendmessage is invoked in the ShowMessageBox function to send an information window so that we can use it in the service's OnStart function, open the Service1.cs and add the following code:
Copy Code code as follows:
protected override void OnStart (string[) args)
{
Interop.showmessagebox ("This a message from Alertservice.", "Alertservice");
}
After compiling the program, restart the Alertservice service in Service Manager, and from the following figure you can see that the message window is displayed on the current user's desktop, not session 0.
CreateProcessAsUser function
If you want to create a complex UI program interface through the service to the desktop user session, you need to use the CreateProcessAsUser function to create a new process for the user to run the program. Open the Interop class to continue adding the following code:
Copy Code code as follows:
public static void CreateProcess (String app, string path)
{
BOOL result;
IntPtr Htoken = WindowsIdentity.GetCurrent (). Token;
IntPtr Hdupedtoken = IntPtr.Zero;
process_information pi = new process_information ();
Security_attributes sa = new security_attributes ();
Sa. Length = marshal.sizeof (SA);
Startupinfo si = new Startupinfo ();
SI.CB = marshal.sizeof (SI);
int dwsessionid = Wtsgetactiveconsolesessionid ();
result = Wtsqueryusertoken (Dwsessionid, out htoken);
if (!result)
{
ShowMessageBox ("Wtsqueryusertoken failed", "alertservice message");
}
result = Duplicatetokenex (
Htoken,
Generic_all_access,
Ref SA,
(int) Security_impersonation_level. Securityidentification,
(int) Token_type. Tokenprimary,
Ref Hdupedtoken
);
if (!result)
{
ShowMessageBox ("Duplicatetokenex failed", "alertservice message");
}
IntPtr lpenvironment = IntPtr.Zero;
result = Createenvironmentblock (out lpenvironment, Hdupedtoken, false);
if (!result)
{
ShowMessageBox ("Createenvironmentblock failed", "alertservice message");
}
result = CreateProcessAsUser (
Hdupedtoken,
App
String.Empty,
Ref SA, ref SA,
False, 0, IntPtr.Zero,
Path, ref si, ref pi);
if (!result)
{
int error = Marshal.GetLastWin32Error ();
String message = String.Format ("CreateProcessAsUser error: {0}", error);
ShowMessageBox (Message, "Alertservice message");
}
if (pi.hprocess!= IntPtr.Zero)
CloseHandle (pi.hprocess);
if (Pi.hthread!= IntPtr.Zero)
CloseHandle (Pi.hthread);
if (Hdupedtoken!= IntPtr.Zero)
CloseHandle (Hdupedtoken);
}
[StructLayout (LayoutKind.Sequential)]
public struct STARTUPINFO
{
Public Int32 CB;
public string lpreserved;
public string lpdesktop;
public string Lptitle;
Public Int32 DwX;
Public Int32 Dwy;
Public Int32 dwxsize;
Public Int32 Dwxcountchars;
Public Int32 Dwycountchars;
Public Int32 Dwfillattribute;
Public Int32 dwflags;
Public Int16 Wshowwindow;
Public Int16 CbReserved2;
Public IntPtr LpReserved2;
Public IntPtr hStdInput;
Public IntPtr Hstdoutput;
Public IntPtr Hstderror;
}
[StructLayout (LayoutKind.Sequential)]
public struct process_information
{
Public IntPtr hprocess;
Public IntPtr Hthread;
Public Int32 Dwprocessid;
Public Int32 dwThreadID;
}
[StructLayout (LayoutKind.Sequential)]
public struct security_attributes
{
Public Int32 Length;
Public IntPtr Lpsecuritydescriptor;
public bool bInheritHandle;
}
public enum Security_impersonation_level
{
Securityanonymous,
Securityidentification,
Securityimpersonation,
SecurityDelegation
}
public enum Token_type
{
Tokenprimary = 1,
Tokenimpersonation
}
public const int generic_all_access = 0x10000000;
[DllImport ("kernel32.dll", SetLastError = True,
CharSet = CharSet.Auto, callingconvention = Callingconvention.stdcall)]
public static extern bool CloseHandle (INTPTR handle);
[DllImport ("advapi32.dll", SetLastError = True,
CharSet = CharSet.Ansi, callingconvention = Callingconvention.stdcall)]
public static extern bool CreateProcessAsUser (
IntPtr Htoken,
String Lpapplicationname,
String lpCommandLine,
Ref Security_attributes Lpprocessattributes,
Ref Security_attributes Lpthreadattributes,
BOOL bInheritHandle,
Int32 dwCreationFlags,
IntPtr Lpenvrionment,
String Lpcurrentdirectory,
Ref Startupinfo Lpstartupinfo,
Ref process_information lpprocessinformation);
[DllImport ("advapi32.dll", SetLastError = True)]
public static extern bool Duplicatetokenex (
IntPtr Hexistingtoken,
Int32 dwDesiredAccess,
Ref Security_attributes Lpthreadattributes,
Int32 ImpersonationLevel,
Int32 Dwtokentype,
Ref INTPTR Phnewtoken);
[DllImport ("Wtsapi32.dll", Setlasterror=true)]
public static extern bool Wtsqueryusertoken (
Int32 SessionId,
Out IntPtr Token);
[DllImport ("Userenv.dll", SetLastError = True)]
static extern bool Createenvironmentblock (
Out IntPtr Lpenvironment,
IntPtr Htoken,
BOOL Binherit);
The use of Duplicatetokenex, Wtsqueryusertoken, and Createenvironmentblock functions is also involved in the CreateProcess function, and interested friends can learn from MSDN. Once the CreateProcess function is created, you can actually invoke the application through it, and go back to Service1.cs to modify the OnStart let's open a CMD window. The following code:
Copy Code code as follows:
protected override void OnStart (string[) args)
{
Interop.createprocess ("cmd.exe", @ "C:\Windows\System32\");
}
Recompile the program and start the Alertservice service to see the following image interface. So far, we can solve the session 0 isolation problem with some simple methods. You can also use WCF and other technologies to complete a number of more complex communication methods across the session, to achieve in Windows 7 and Vista system services and desktop users of the interactive operation.
Resources
1. Wtssendmessage Function
http://msdn.microsoft.com/en-us/library/aa383842 (vs.85). aspx
2. CreateProcessAsUser Function
http://msdn.microsoft.com/en-us/library/ms682429 (v=vs.85). aspx
3. Wtssendmessage (WTSAPI32)
Http://www.pinvoke.net/default.aspx/wtsapi32/WTSSendMessage.html
4. Wtsqueryusertoken Function
http://msdn.microsoft.com/en-us/library/aa383840 (vs.85). aspx
5. http://www.pinvoke.net/
Code Download Alertservice2_jb51.rar
Author: Li Jinghan (Gnie)
Source: {Gnietech} (http://www.cnblogs.com/gnielee/)