Click here to Skip to main content
Click here to Skip to main content

Create a system tray icon and a dialog for a Windows Service

By , 25 Jan 2008
 

shot1.JPG

Introduction

Generally speaking, the easiest way to implement a system tray icon with a context menu for a Windows Service is to implement a separate Shell program. But here, I am creating a Windows Service which will have its own system tray icon and dialog.

Background

A Windows Service is started before the Windows logon, so the first challenge is how to detect the Shell is ready in the Windows Service. The second challenge is how to interact with the Windows service. I solve the first issue with the System Event Notification Service (SENS). For the second, I associate the thread to the default desktop by using P/Invoke.

Steps

Step 1: Open VS2005, create a Windows Service project, and add an installer for the Windows Service.

[RunInstaller(true)]
public class SvcInstaller : Installer
{
    private static readonly string SVC_NAME = "SystemTrayIconInSvc";
    private static readonly string SVC_DESC = "This is a test";

    public SvcInstaller()
    {
        Installers.Clear();

        ServiceInstaller serviceInstaller = new ServiceInstaller();
        serviceInstaller.StartType = ServiceStartMode.Automatic;
        serviceInstaller.ServiceName = SVC_NAME;
        serviceInstaller.DisplayName = SVC_NAME;
        serviceInstaller.Description = SVC_DESC;
        serviceInstaller.ServicesDependedOn = new string[] { "SENS", "COMSysApp" };

        Installers.Add(serviceInstaller);

        ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
        processInstaller.Account = ServiceAccount.LocalSystem;
        processInstaller.Password = null;
        processInstaller.Username = null;

        Installers.Add(processInstaller);
    }


    protected override void OnAfterInstall(IDictionary savedState)
    {
        ServiceController controller = null;
        ServiceController[] controllers = ServiceController.GetServices();
        for (int i = 0; i < controllers.Length; i++)
        {
            if (controllers[i].ServiceName == SVC_NAME)
            {
                controller = controllers[i];
                break;
            }
        }
        if (controller == null)
        {
            return;
        }

        // if the service is not active, start it
        if (controller.Status != ServiceControllerStatus.Running)
        {
            string[] args = { "-install" };
            controller.Start(args);
        }
    }
}

Note: The Windows service should depend on the "SENS" and "COMSysApp" because we need to make sure the Windows service is launched after the dependency is ready.

Step 2: SENS subscription. Add a reference to "COM+ 1.0 Admin Type Library" to the project. Add a reference to "SENS Events Type Library" to the project. Add the SensAdvisor class which is used to do the subscription against the SENS interface. I wrote the SensAdvisor class based on the MSDN article, "Accessing System Power and Network Status Using SENS". Here, we are interested in the ISensLogon2 interface (you can subscribe to the ISensLogon as well).

/// <summary>
/// ISensLogon2 Event Args
/// </summary>
public class SensLogon2EventArgs : EventArgs
{
    public string Username;
    public uint SessionId;
}

/// <summary>
/// subscribe SENS notification
/// Ref MSDN:Accessing System Power and Network Status Using SENS
/// </summary>
public sealed class SensAdvisor : ISensLogon2
{
    public const string ISensLogon2_ID = "{d5978650-5b9f-11d1-8dd2-00aa004abd5e}";

    public SensAdvisor()
    {
        COMAdminCatalogClass comAdmin = new COMAdminCatalogClass();
        ICatalogCollection subCollection = 
          (ICatalogCollection)comAdmin.GetCollection("TransientSubscriptions");

        SubscribeToEvent(subCollection, "PostShell", ISensLogon2_ID);
        SubscribeToEvent(subCollection, "Logon", ISensLogon2_ID);
        SubscribeToEvent(subCollection, "Logoff", ISensLogon2_ID);
        SubscribeToEvent(subCollection, "SessionReconnect", ISensLogon2_ID);
        SubscribeToEvent(subCollection, "SessionDisconnect", ISensLogon2_ID);
    }
        
    private void SubscribeToEvent(ICatalogCollection subCollection, 
                                  string methodName, string guidString)
    {
       ICatalogObject catalogObject = (ICatalogObject)subCollection.Add();

       // Specify the parameters of the desired subscription.
       catalogObject.set_Value("EventCLSID", guidString);
       catalogObject.set_Value("Name", "Subscription to " + 
                               methodName + " event");
       catalogObject.set_Value("MethodName", methodName);
       catalogObject.set_Value("SubscriberInterface", this);
       catalogObject.set_Value("Enabled", true);
       // This setting allows subscriptions to work for non-Administrator users.
       catalogObject.set_Value("PerUser", true);  

       // Save the changes made to the transient subscription collection.
       subCollection.SaveChanges();
    }


    public delegate void PostShellEventHandler(object sender, SensLogon2EventArgs e);
    public delegate void SessionReconnectEventHandler(object sender, SensLogon2EventArgs e);
    public delegate void SessionDisconnectEventHandler(object sender, SensLogon2EventArgs e);
    public delegate void LogonEventHandler(object sender, SensLogon2EventArgs e);
    public delegate void LogoffEventHandler(object sender, SensLogon2EventArgs e);

    public event PostShellEventHandler OnShellStarted;
    public event SessionReconnectEventHandler OnSessionReconnected;
    public event SessionDisconnectEventHandler OnSessionDisconnected;
    public event LogonEventHandler OnLogon;
    public event LogoffEventHandler OnLogoff;


    public void PostShell(string bstrUserName, uint dwSessionId)
    {
        if (OnShellStarted != null)
        {
            SensLogon2EventArgs args = new SensLogon2EventArgs();
            args.Username = bstrUserName;
            args.SessionId = dwSessionId;
            OnShellStarted(this, args);
        }
    }

    public void SessionReconnect(string bstrUserName, uint dwSessionId)
    {
        if (OnSessionReconnected != null)
        {
            SensLogon2EventArgs args = new SensLogon2EventArgs();
            args.Username = bstrUserName;
            args.SessionId = dwSessionId;
            OnSessionReconnected(this, args);
        }
    }

    public void SessionDisconnect(string bstrUserName, uint dwSessionId)
    {
        if (OnSessionDisconnected != null)
        {
            SensLogon2EventArgs args = new SensLogon2EventArgs();
            args.Username = bstrUserName;
            args.SessionId = dwSessionId;
            OnSessionDisconnected(this, args);
        }
    }

    public void Logoff(string bstrUserName, uint dwSessionId)
    {
        if (OnLogoff != null)
        {
            SensLogon2EventArgs args = new SensLogon2EventArgs();
            args.Username = bstrUserName;
            args.SessionId = dwSessionId;
            OnLogoff(this, args);
        }
    }

    public void Logon(string bstrUserName, uint dwSessionId)
    {
        if (OnLogon != null)
        {
            SensLogon2EventArgs args = new SensLogon2EventArgs();
            args.Username = bstrUserName;
            args.SessionId = dwSessionId;
            OnLogon(this, args);
        }
    }  
}

Step 3: Add a wrapper class for the required APIs in the User32.dll.

/// <summary>
/// The wrapper class for User32.dll
/// </summary>
public static class User32DLL
{
    /// <summary>
    /// The GetDesktopWindow function returns a handle to the desktop window. 
    /// The desktop window covers the entire screen. 
    /// The desktop window is the area on top of which other windows are painted. 
    /// </summary>
    /// <returns>The return value is a handle to the desktop window. </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr GetDesktopWindow();


    /// <summary>
    /// Retrieves a handle to the current window station for the calling process.
    /// </summary>
    /// <returns>If the function succeeds,
    /// the return value is a handle to the window station.
    /// If the function fails, the return value is NULL.</returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr GetProcessWindowStation();


    /// <summary>
    /// Retrieves a handle to the desktop assigned to the specified thread.
    /// </summary>
    /// <param name="dwThread">[in] Handle to the thread
    ///     for which to return the desktop handle.</param>
    /// <returns>If the function succeeds, the return value is a handle to the 
    /// desktop associated with the specified thread. 
    /// If the function fails, the return value is NULL.</returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr GetThreadDesktop(uint dwThread);


    /// <summary>
    /// Opens the specified window station.
    /// </summary>
    /// <param name="lpszWinSta">Pointer to a null-terminated
    ///           string specifying the name of the window station 
    /// to be opened. Window station names are case-insensitive.
    /// This window station must belong to the current session.
    /// </param>
    /// <param name="fInherit">[in] If this value
    ///            is TRUE, processes created by this process 
    /// will inherit the handle. Otherwise, 
    /// the processes do not inherit this handle. 
    /// </param>
    /// <param name="dwDesiredAccess">[in] Access to the window station</param>
    /// <returns>If the function succeeds, the return value
    ///          is the handle to the specified window station.
    /// If the function fails, the return value is NULL.</returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr OpenWindowStation(string lpszWinSta
        , bool fInherit
        , WindowStationAccessRight dwDesiredAccess
        );


    /// <summary>
    /// Assigns the specified window station to the calling process. 
    /// This enables the process to access objects in the window
    /// station such as desktops, the clipboard, and global atoms. 
    /// All subsequent operations on the window station
    /// use the access rights granted to hWinSta.
    /// </summary>
    /// <param name="hWinSta">[in] Handle to the window
    ///         station to be assigned to the calling process</param>
    /// <returns>If the function succeeds, the return value is nonzero.
    /// If the function fails, the return value is zero. </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr SetProcessWindowStation(IntPtr hWinSta);


    /// <summary>
    /// Closes an open window station handle.
    /// </summary>
    /// <param name="hWinSta">[in] Handle
    ///         to the window station to be closed.</param>
    /// <returns>If the function succeeds, the return value is nonzero.
    /// If the function fails, the return value is zero. </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr CloseWindowStation(IntPtr hWinSta);


    /// <summary>
    /// Opens the specified desktop object.
    /// </summary>
    /// <param name="lpszDesktop">[in] Pointer to null-terminated string 
    /// specifying the name of the desktop to be opened. 
    /// Desktop names are case-insensitive.
    /// This desktop must belong to the current window station.</param>
    /// <param name="dwFlags">[in] This parameter can
    ///          be zero or DF_ALLOWOTHERACCOUNTHOOK=0x0001</param>
    /// <param name="fInherit">[in] If this value is TRUE, processes created by 
    /// this process will inherit the handle. 
    /// Otherwise, the processes do not inherit this handle. </param>
    /// <param name="dwDesiredAccess">[in] Access
    ///         to the desktop. For a list of access rights</param>
    /// <returns>If the function succeeds, the return value is a handle to the opened desktop. 
    /// When you are finished using the handle, call the CloseDesktop function to close it.
    /// If the function fails, the return value is NULL.
    /// </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr OpenDesktop(string lpszDesktop
        , OpenDesktopFlag dwFlags
        , bool fInherit
        , DesktopAccessRight dwDesiredAccess
        );


    /// <summary>
    /// Closes an open handle to a desktop object.
    /// </summary>
    /// <param name="hDesktop">[in] Handle to the desktop to be closed.</param>
    /// <returns>If the function succeeds, the return value is nonzero.
    /// If the function fails, the return value is zero. </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr CloseDesktop(IntPtr hDesktop);

    /// <summary>
    /// Assigns the specified desktop to the calling thread. 
    /// All subsequent operations on the desktop use the access rights granted to the desktop.
    /// </summary>
    /// <param name="hDesktop">[in] Handle to the desktop
    ///           to be assigned to the calling thread.</param>
    /// <returns>If the function succeeds, the return value is nonzero.
    /// If the function fails, the return value is zero. </returns>
    [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool SetThreadDesktop(IntPtr hDesktop);
}

/// <summary>
/// REF MSDN:Window Station Security and Access Rights
/// ms-help://MS.MSDN.vAug06.en/dllproc/base/window_station_security_and_access_rights.htm
/// </summary>
[FlagsAttribute]
public enum WindowStationAccessRight : uint
{
    /// <summary>All possible access rights for the window station.</summary>
    WINSTA_ALL_ACCESS = 0x37F,

    /// <summary>Required to use the clipboard.</summary>
    WINSTA_ACCESSCLIPBOARD = 0x0004,

    /// <summary>Required to manipulate global atoms.</summary>
    WINSTA_ACCESSGLOBALATOMS = 0x0020,

    /// <summary>Required to create new desktop
    /// objects on the window station.</summary>
    WINSTA_CREATEDESKTOP = 0x0008,

    /// <summary>Required to enumerate existing desktop objects.</summary>
    WINSTA_ENUMDESKTOPS = 0x0001,

    /// <summary>Required for the window station to be enumerated.</summary>
    WINSTA_ENUMERATE = 0x0100,

    /// <summary>Required to successfully call the ExitWindows or ExitWindowsEx function. 
    /// Window stations can be shared by users and this access type can prevent other users 
    /// of a window station from logging off the window station owner.</summary>
    WINSTA_EXITWINDOWS = 0x0040,

    /// <summary>Required to read the attributes of a window station object. 
    /// This attribute includes color settings 
    /// and other global window station properties.</summary>
    WINSTA_READATTRIBUTES = 0x0002,

    /// <summary>Required to access screen contents.</summary>
    WINSTA_READSCREEN = 0x0200,

    /// <summary>Required to modify the attributes of 
    /// a window station object. 
    /// The attributes include color settings 
    /// and other global window station properties.</summary>
    WINSTA_WRITEATTRIBUTES = 0x0010,
}

/// <summary>
/// OpenDesktop 2nd param
/// </summary>
public enum OpenDesktopFlag : uint
{
    /// <summary>
    /// Default value
    /// </summary>
    DF_NONE = 0x0000,

    /// <summary>
    /// Allows processes running in other accounts on the desktop
    /// to set hooks in this process.
    /// </summary>
    DF_ALLOWOTHERACCOUNTHOOK = 0x0001,
}

/// <summary>
/// REF MSDN:Desktop Security and Access Rights
/// ms-help://MS.MSDN.vAug06.en/dllproc/base/desktop_security_and_access_rights.htm
/// </summary>
[FlagsAttribute]
public enum DesktopAccessRight : uint
{
    /// <summary>Required to create a menu on the desktop. </summary>
    DESKTOP_CREATEMENU = 0x0004,

    /// <summary>Required to create a window on the desktop. </summary>
    DESKTOP_CREATEWINDOW = 0x0002,

    /// <summary>Required for the desktop to be enumerated. </summary>
    DESKTOP_ENUMERATE = 0x0040,

    /// <summary>Required to establish any of the window hooks. </summary>
    DESKTOP_HOOKCONTROL = 0x0008,

    /// <summary>Required to perform journal playback on a desktop. </summary>
    DESKTOP_JOURNALPLAYBACK = 0x0020,

    /// <summary>Required to perform journal recording on a desktop. </summary>
    DESKTOP_JOURNALRECORD = 0x0010,

    /// <summary>Required to read objects on the desktop. </summary>
    DESKTOP_READOBJECTS = 0x0001,

    /// <summary>Required to activate the desktop
    ///          using the SwitchDesktop function. </summary>
    DESKTOP_SWITCHDESKTOP = 0x0100,

    /// <summary>Required to write objects on the desktop. </summary>
    DESKTOP_WRITEOBJECTS = 0x0080,
}

Step 4: Add the Desktop class, which will help us to set the thread station.

internal class Desktop
{
    private IntPtr m_hCurWinsta = IntPtr.Zero;
    private IntPtr m_hCurDesktop = IntPtr.Zero;
    private IntPtr m_hWinsta = IntPtr.Zero;
    private IntPtr m_hDesk = IntPtr.Zero;

    /// <summary>
    /// associate the current thread to the default desktop
    /// </summary>
    /// <returns></returns>
    internal bool BeginInteraction()
    {
        EndInteraction();
        m_hCurWinsta = User32DLL.GetProcessWindowStation();
        if (m_hCurWinsta == IntPtr.Zero)
            return false;

        m_hCurDesktop = User32DLL.GetDesktopWindow();
        if (m_hCurDesktop == IntPtr.Zero)
            return false;

        m_hWinsta = User32DLL.OpenWindowStation("winsta0", false,
            WindowStationAccessRight.WINSTA_ACCESSCLIPBOARD |
            WindowStationAccessRight.WINSTA_ACCESSGLOBALATOMS |
            WindowStationAccessRight.WINSTA_CREATEDESKTOP |
            WindowStationAccessRight.WINSTA_ENUMDESKTOPS |
            WindowStationAccessRight.WINSTA_ENUMERATE |
            WindowStationAccessRight.WINSTA_EXITWINDOWS |
            WindowStationAccessRight.WINSTA_READATTRIBUTES |
            WindowStationAccessRight.WINSTA_READSCREEN |
            WindowStationAccessRight.WINSTA_WRITEATTRIBUTES
            );
        if (m_hWinsta == IntPtr.Zero)
            return false;

        User32DLL.SetProcessWindowStation(m_hWinsta);

        m_hDesk = User32DLL.OpenDesktop("default", OpenDesktopFlag.DF_NONE, false,
                DesktopAccessRight.DESKTOP_CREATEMENU |
                DesktopAccessRight.DESKTOP_CREATEWINDOW |
                DesktopAccessRight.DESKTOP_ENUMERATE |
                DesktopAccessRight.DESKTOP_HOOKCONTROL |
                DesktopAccessRight.DESKTOP_JOURNALPLAYBACK |
                DesktopAccessRight.DESKTOP_JOURNALRECORD |
                DesktopAccessRight.DESKTOP_READOBJECTS |
                DesktopAccessRight.DESKTOP_SWITCHDESKTOP |
                DesktopAccessRight.DESKTOP_WRITEOBJECTS
                );
        if (m_hDesk == IntPtr.Zero)
            return false;

        User32DLL.SetThreadDesktop(m_hDesk);

        return true;
    }

    /// <summary>
    /// restore
    /// </summary>
    internal void EndInteraction()
    {
        if (m_hCurWinsta != IntPtr.Zero)
            User32DLL.SetProcessWindowStation(m_hCurWinsta);

        if (m_hCurDesktop != IntPtr.Zero)
            User32DLL.SetThreadDesktop(m_hCurDesktop);

        if (m_hWinsta != IntPtr.Zero)
            User32DLL.CloseWindowStation(m_hWinsta);

        if (m_hDesk != IntPtr.Zero)
            User32DLL.CloseDesktop(m_hDesk);
    }
}

Step 5: Add the Form and the system tray icon class. Note that the UI is running in another thread.

public partial class SettingDlg : Form
{
    private Desktop m_Desktop = new Desktop();
    private IContainer m_Container = null;
    private NotifyIcon m_NotifyIcon = null;
    private Button btnHide;
    private ContextMenu m_ContextMenu = null;

    /// <summary>
    /// Start the UI thread
    /// </summary>
    public static SettingDlg StartUIThread()
    {
        SettingDlg dlg = new SettingDlg();

        Thread thread = new Thread(new ThreadStart(dlg.UIThread));
        thread.Start();

        return dlg;
    }

    /// <summary>
    /// UI thread
    /// </summary>
    public void UIThread()
    {
        if( !m_Desktop.BeginInteraction() )
                return;
        Application.Run(this);
    }

    protected SettingDlg()
    {
        InitializeComponent();
    }

    /// <summary>
    /// Move the window to the right-bottom corner
    /// </summary>
    /// <param name="e"></param>
    protected override void OnShown(EventArgs e)
    {
        this.Left = Screen.PrimaryScreen.WorkingArea.Left
            + Screen.PrimaryScreen.WorkingArea.Width
            - this.Width
            ;
        this.Top = Screen.PrimaryScreen.WorkingArea.Top
            + Screen.PrimaryScreen.WorkingArea.Height
            - this.Height
            ;
    }


    private void SettingDlg_Load(object sender, EventArgs e)
    {
        m_ContextMenu = new ContextMenu();
        m_ContextMenu.MenuItems.Add(new MenuItem("Open Dialog", this.OpenDialog));
        
        Icon icon = new Icon(SystemIcons.Application, 16, 16);
        m_Container = new Container();
        m_NotifyIcon = new NotifyIcon(m_Container);
        m_NotifyIcon.ContextMenu = m_ContextMenu;
        m_NotifyIcon.Icon = icon;
        m_NotifyIcon.Visible = true;

        m_NotifyIcon.ShowBalloonTip( 200
            , "SystemTrayIconInSvc"
            , "The system tray icon is implemented in the windows service itself."
            , ToolTipIcon.Info
            );
    }



    public void OpenDialog(Object sender, EventArgs e)
    {
        this.Visible = true;
        BringToFront();
    }

    protected override void OnClosed(EventArgs e)
    {
        m_NotifyIcon.Dispose();
        m_ContextMenu.Dispose();
        m_Container.Dispose();
    }

    private void btnHide_Click(object sender, EventArgs e)
    {
        this.Visible = false;
    }
}

Step 6: Integrate all of the above together.

public partial class Svc : ServiceBase
{
    private SensAdvisor m_Advisor = new SensAdvisor();
    private List<SettingDlg> m_Dlgs = new List<SettingDlg>();

    public Svc()
    {
        InitializeComponent();

        m_Advisor.OnShellStarted += this.PostShell;
    }

    internal void DebugStart()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // if the service started after the windows logon
        m_Dlgs.Add(SettingDlg.StartUIThread());
    }

    protected override void OnStop()
    {
        foreach (SettingDlg dlg in m_Dlgs)
        {
            try
            {
                dlg.Close();
                dlg.Dispose();
            }
            catch { }
        }

        m_Dlgs.Clear();
    }

    /// <summary>
    /// Called when the shell is ready
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public void PostShell(object sender, SensLogon2EventArgs e)
    {
        m_Dlgs.Add(SettingDlg.StartUIThread());
    }
}

Points

I haven't tested this from a remote desktop client because the sample just associates the UI to the default desktop, so it may not work from a remote desktop. If so, you can add a "/console" parameter when you connect to the remote desktop, and that will make the remote client use the default desktop. Note, the "/console" parameter is only supported under Windows 5.2 and above.

rr.JPG

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jerry.Wang
Team Leader
China China
Member
Jerry is from China. He was captivated by computer programming since 13 years old when first time played with Q-Basic.
 

  • Windows / Linux & C++
  • iOS & Obj-C
  • .Net & C#
  • Flex/Flash & ActionScript
  • PHP / HTML / CSS / Javascript
  • Gaming Server programming / video, audio processing / image & graphics
 
Contact: 32775973(at)qq.com
Chinese Blog: http://blog.csdn.net/wangjia184

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membervasim sajad24 Sep '12 - 23:33 
gud one Smile | :)
Questionwindows service does not start automatically for me too [modified]memberphoenix116722 Sep '12 - 23:21 
Hi Jerry.Wang, your article was too useful for me.
I have a windows service that it should check and save all login and logout of all accounts in a network.
I mean when a user logs in to his system, a record should add to Logs table that contains the user's login time and also when he logs out the logout time should save on that record.
 
I have used ISensLogon2 in my service based on your article.
 
Now when i start my service manually it works fine but when i restart my system the service does not start automatically.
I have found that the problem is in the following code line but i do not know what is the reason!?
ICatalogCollection subCollection = (ICatalogCollection)comAdmin.GetCollection("TransientSubscriptions");
 
Note: I have used Installshield to install my service instead ofInstaller Class of visual studio.
 
Can you please tell me what is my mistake? Dead | X| D'Oh! | :doh: WTF | :WTF:

modified 23 Sep '12 - 5:38.

AnswerRe: windows service does not start automatically for me toomemberJerry.Wang23 Sep '12 - 0:34 
Is SEN Service, the dependency of your service, started with the system?
GeneralRe: windows service does not start automatically for me toomemberphoenix116723 Sep '12 - 19:12 
How can i check it? I have tried to add the Interop.SensEvents.dll as a dependency to my installshield project but the dependency scanner of installshield could not diagnose this dll in my project.
However it can found Interop.COMAdmin.dll and add it successfully.
GeneralRe: windows service does not start automatically for me toomemberJerry.Wang23 Sep '12 - 19:13 
You need ensure your service has the dependency to SENS, so that your service will be started after it.
 
Also, need ensure both of them are automaticlly started in services.msc
GeneralRe: windows service does not start automatically for me too [modified]memberphoenix116723 Sep '12 - 19:22 
Thanks a lot mr. Wang, i will try to find a way to add SENS as a dependency.

modified 24 Sep '12 - 2:08.

GeneralRe: windows service does not start automatically for me toomemberphoenix116723 Sep '12 - 20:09 
I have found the following service in Services.msc:
 
Name: COM + Event system
Description: Supports System Events Notification Service (SENS) which provides automatic distribution of events to subscribe Component Object Model (COM) components.If the service is stopped SENS will close and will not be able to provide login and logoff notifications...
Status: Started
Start Type: Manual
 
Is this service my preferred service??If yes so why my service could not perform
comAdmin.GetCollection("TransientSubscriptions");
???Confused | :confused:
GeneralRe: windows service does not start automatically for me toomemberJerry.Wang23 Sep '12 - 20:14 
your service depends on SENS, which should be started before your service
GeneralRe: windows service does not start automatically for me toomemberphoenix116723 Sep '12 - 22:23 
I know but the question is that how?
 
Please guide me....
GeneralRe: windows service does not start automatically for me toomemberJerry.Wang23 Sep '12 - 22:26 
See the demo source code for how the service's dependency is added.
 
And in your installer, changing both services and ensure them starts automatically.
 
it is just a property of the service, can be done via ServiceManager or modifying Register.
 
use Google please
QuestionAdvice on how this could work with Win 7 (and Vista, I guess)memberDotMP121618 Jun '12 - 6:35 
My Service based on this example works great in XP, but I don't see the dialog on Win 7.
 
Anyone tried to get this to work on Win 7?
Questiontray icon not visible in Windows server2008membersandeepappana23 May '12 - 0:39 
i tried this code on client and server system but in server(windows server 2008 )system the tray icon was not appeard on taskbar please solve this isssue.
QuestionProblem with Windows server2008membersandeepappana23 May '12 - 0:36 
i tried this code on client and server system but in server(windows server 2008 )system the tray icon was not appeard on taskbar please solve this isssue.
QuestionCan be converted this to WPF version?memberMember 219778423 Sep '09 - 1:31 
Can be converted this to WPF version?
If this project is converted WPF version,
Can you explain how to do this?
GeneralWin2003工作不正常memberfanwenxuan1 Sep '09 - 17:22 
您好!我最近在项目中也是遇到了通过windows服务的方式来弹出窗口。运行了你的程序之后,运行正常。你的程序是在"local System"帐号下运行的。现在我想在一个管理员的帐号运行(在windows services里面设置成运行帐号为administrator),结果是弹出窗口不起作用,请教一下是什么原因?多谢!
我重启过服务,也重启过电脑,都不起作用
GeneralRe: Win2003工作不正常memberJerry.Wang1 Sep '09 - 18:29 
1. The demo is developed under Windows 2003
2. Make sure the server is started on your Win2K3, if not , check the Event Log
3. It u use a user account to run the server, you need modify the Group Polices to make the account can be used for service
GeneralRe: Win2003工作不正常memberfanwenxuan1 Sep '09 - 19:08 
Thanks for your reply.But my system is win 2003 ,and I run the services as administrator,who has complete access to my PC. Any other reasons?
Thanks.
GeneralRe: Win2003工作不正常memberJerry.Wang23 Sep '09 - 15:44 
The point 3, did u try it???
GeneralRe: Win2003工作不正常memberfanwenxuan12 Oct '09 - 3:06 
感谢你的回复!
我通过其他的方法实现我的需求。不过我确信:如果不是以local System account的帐号运行服务,是没有办法实现弹出窗口的效果的。
GeneralMy vote of 1memberStephen C. Martin23 Jul '09 - 11:37 
This will not work in Vista and later, it is a serious security breach in the OSes under which it will work and it solves an essentially non-existent problem.
GeneralGreat workmembertorial9 Apr '09 - 4:59 
Thanks for sharing your research - I definitely needed it!
GeneralExample not working on Windows 2003 servermemberDerec Roofie14 Nov '08 - 0:21 
I try your example on WinXP and it's OK.
But on Win2003 ballon help, notifyIcon and settings form are not shown at all.
 
Is there any security settings I should change on win2003 to allow service to interact with desktop/GUI except check box "allow interact .." in service's porperties pane
 
Thanks
GeneralRe: Example not working on Windows 2003 servermemberJerry.Wang16 Nov '08 - 14:40 
I develop this program in Win2K3(SP2), and it should work.
 
try to trace in Win2K3 to find out where it failed
GeneralRe: Example not working on Windows 2003 servermemberDerec Roofie16 Nov '08 - 20:48 
Thanks for response. I have no VS on Win2K3 and cannot trace.
It is something with security settings on Win2K3 in general, but I cannot figured out what exactly.
I can’t even get simple message box!
GeneralRe: Example not working on Windows 2003 servermemberJerry.Wang16 Nov '08 - 21:05 
If you use RemoteDesktopTop to connect to the server, remember add the "/console" parameter when connecting.
 
What is the account for this service? is the server in a domain area?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 25 Jan 2008
Article Copyright 2008 by Jerry.Wang
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid