Запуск приложения из службы, работающей как система, которая может взаимодействовать с пользователем - PullRequest
2 голосов
/ 25 ноября 2011

В настоящее время у меня есть одно приложение, которое нужно запустить из службы Windows, которую я кодирую в .net 3.5.Это приложение в настоящее время работает как пользователь, запустивший службу, в моем случае это пользователь SYSTEM.При запуске от имени пользователя SYSTEM приложение не отображается на рабочем столе пользователя.Мысли?совет?

//constructor
    private Process ETCHNotify = new Process();

//StartService()     
    ETCHNotify.StartInfo.FileName = baseDir + "\\EtchNotify.exe";
    ETCHNotify.StartInfo.UseShellExecute = false;

//BackgroundWorkerThread_DoWork()
    if (!systemData.GetUserName().Equals(""))
    {
        // start ETCHNotify
        try {
            ETCHNotify.Start();
        }
            catch (Exception ex)
        {
            systemData.Run("ERR: Notify can't start: " + ex.Message);
        }
    }

Я выполняю попытку / отлов только в том случае, если написанная мной функция GetUserName () (которая определяет имя пользователя, запускающего explorer.exe) снова не равна нулю

Еще раз: желаемая функциональность заключается в том, что это запускает ETCHNotify в состоянии, которое позволяет ему взаимодействовать с текущим вошедшим в систему пользователем, как определено GetUserName ()

Ответы [ 4 ]

4 голосов
/ 25 ноября 2011

Коллаж из некоторого поста, найденного вокруг (, и , )

Обратите внимание, что в Windows Vista службам строго запрещено напрямую взаимодействовать спользователь :

Важное замечание: Службы не могут напрямую взаимодействовать с пользователем начиная с Windows Vista.Поэтому методы, упомянутые в разделе «Использование интерактивного сервиса», не должны использоваться в новом коде.

Эта «особенность» не работает, и общепринятая мудрость подсказывает, что вы не должны полагаться наэто все равно.Сервисы не предназначены для предоставления пользовательского интерфейса или прямого взаимодействия с пользователем.Microsoft предостерегает, что эту функцию следует избегать с первых дней существования Windows NT из-за возможных угроз безопасности.

Однако существуют некоторые возможные обходные пути, если вам абсолютно необходима эта функциональность.Но я настоятельно призываю вас внимательно рассмотреть его необходимость и изучить альтернативные варианты дизайна для вашего сервиса.

Используйте WTSEnumerateSessions , чтобы найти правильный рабочий стол, затем CreateProcessAsUser , чтобы запустить приложениена этом рабочем столе (вы передаете ему дескриптор рабочего стола как часть структуры STARTUPINFO ) правильно.

Однако я настоятельно рекомендую не делать этого.В некоторых средах, таких как серверы терминалов со многими активными пользователями, определить, какой рабочий стол является «активным», нелегко и даже невозможно.

Более традиционным подходом является установкаярлык для небольшого клиентского приложения для вашей службы в глобальной группе запуска.Затем это приложение будет запускаться вместе с каждым сеансом пользователя и может использоваться для запуска других приложений (если это необходимо) без каких-либо манипуляций с учетными данными пользователя, сеансами и / или рабочими столами.

1 голос
/ 30 января 2014

Я не собирался отвечать на этот вопрос, так как вы уже ответили на него, (а это, ну, что? Прошло 2,5 года! ») Но есть ВСЕГДА те люди, которые ищут эту же тему и читают ответы ...

Для того, чтобы мой сервис мог взаимодействовать с рабочим столом, не важно, ЧТО рабочий стол, ни МНОГО рабочих столов, ни сервис, даже запущенный на ОДНОМ ЖЕ КОМПЬЮТЕРЕ в качестве настольного приложения !! Ничто из этого не имеет отношения к тому, что я получил здесь ... Я не буду утомлять вас деталями, я просто дам вам мясо и картошку, а вы и дайте мне знать, если вы хотите увидеть больше ...

Хорошо. Первым делом я создал рекламный сервис . Это поток, который запускает служба, открывает UDP-сокет для прослушивания трансляций в сети. Затем, используя тот же фрагмент кода, я поделился им с клиентским приложением, но оно вызывает Advertise.CLIENT , а не Advertise.SERVER ... КЛИЕНТ открывает порт Я ожидаю, что служба будет включена, и рассылает сообщение «Здравствуйте ... Есть ли кто-нибудь там?», Спрашивая, слушают ли они ЛЮБЫЕ серверы, и если да, ответьте на ЭТОТ IP-адрес с вашего компьютера. имя, IP-адрес и номер порта, где я могу найти .NET Remoting Services ... "Затем он ожидает небольшой промежуток времени ожидания, собирает полученные ответы, а если их больше одного, он представляет пользователю с диалоговым окном и списком сервисов, которые ответили ... Затем клиент выбирает один или, если ответил только ОДИН, он вызовет Connect ( (TServerResponse) res); для этого необходимо подключиться На этом этапе сервер использует службы удаленного взаимодействия с WellKnownClientType и WellKnownServerType, чтобы выставить себя ...

Я не думаю, что вы слишком заинтересованы в моем "локаторе автосервиса", потому что многие люди недовольны UDP, особенно когда ваше приложение начинает вещание в больших сетях. Итак, я предполагаю, что вас больше заинтересует мой RemotingHelper, который подключит клиента к серверу. Это выглядит так:

    public static Object GetObject(Type type) 
    {
        try {
            if(_wellKnownTypes == null) {
                InitTypeCache();
            }
            WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type];

            if(entr == null) {
                throw new RemotingException("Type not found!");
            }
            return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl);
        } catch(System.Net.Sockets.SocketException sex) {
            DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject().  Error: {0}.", sex.Message);
            Disconnect();
            if(Connect()) {
                return GetObject(type);
            }
        }
        return null;
    }

    private static void InitTypeCache() 
    {
        if(m_AdvertiseServer == null) {
            throw new RemotingException("AdvertisementServer cannot be null when connecting to a server.");
        }

        _wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>();

        Dictionary<string, object> channelProperties = new Dictionary<string, object>();
        channelProperties["port"] = 0;
        channelProperties["name"] = m_AdvertiseServer.ChannelName;

        Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>();
        binFormatterProperties["typeFilterLevel"] = "Full";

        if(Environment.UserInteractive) {
            BinaryServerFormatterSinkProvider binFormatterProvider = new BinaryServerFormatterSinkProvider(binFormatterProperties, null); 
            _serverChannel = new TcpServerChannel(channelProperties, binFormatterProvider);
            // LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this
            //      channel registered in this AppDomain.
            ChannelServices.RegisterChannel(_serverChannel, false);
        }

        System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus)));
        RegisterType(typeof(IPawnStatServiceStatus),m_AdvertiseServer.RunningStatusURL.ToString());
        System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService)));
        RegisterType(typeof(IPawnStatService),      m_AdvertiseServer.RunningServerURL.ToString());
        System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration)));
        RegisterType(typeof(IServiceConfiguration), m_AdvertiseServer.RunningConfigURL.ToString());
    }

    [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)]
    public static void RegisterType(Type type, string serviceUrl)
    {
        WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl);
        if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) {
            RemotingConfiguration.RegisterWellKnownClientType(clientType);
        }
        _wellKnownTypes[type] = clientType;
    }

    public static bool Connect()
    {
        // Init the Advertisement Service, and Locate any listening services out there...
        m_AdvertiseServer.InitClient();
        if(m_AdvertiseServer.LocateServices(iTimeout)) {
            if(!Connected) {
                bConnected = true;
            }
        } else {
            bConnected = false;
        }
        return Connected;
    }

    public static void Disconnect()
    {
        if(_wellKnownTypes != null) {
            _wellKnownTypes.Clear();
        }
        _wellKnownTypes = null;
        if(_serverChannel != null) {
            if(Environment.UserInteractive) {
                // LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel...
                ChannelServices.UnregisterChannel(_serverChannel);
                // LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered!
                _serverChannel = null;
            }
        }
        bConnected = false;
    }
}

Итак, это основа моего удаленного кода, и он позволил мне написать клиента, который не должен был знать, где установлены службы или сколько служб запущено в сети. Это позволило мне общаться с ним по сети или на локальной машине. И это не было проблемой, если бы два или более человек работали с приложением, однако, это возможно. Теперь у меня есть некоторый сложный код обратного вызова, в котором я регистрирую события для прохождения через канал удаленного взаимодействия, поэтому мне нужно иметь код, который проверяет, подключен ли клиент, прежде чем отправлять уведомление клиенту о том, что что-то произошло , Кроме того, если вы работаете более чем с одним пользователем, вы можете не использовать объекты Singleton. Это было хорошо для меня, потому что сервер СОБСТВЕННЫЙ объектов, и они являются тем, что сервер говорит, что они есть. Итак, мой объект STATS, например, является Singleton. Нет причин создавать его экземпляр для КАЖДОГО соединения, когда все будут видеть одни и те же данные, верно?

Я могу предоставить больше кусков кода, если это необходимо. Это, конечно, один крошечный кусочек общей картины того, что делает эту работу ... Не говоря уже о поставщиках подписки и все такое.

Для полноты картины я включаю фрагмент кода, чтобы поддерживать подключение службы в течение всего процесса.

public override object InitializeLifetimeService() 
{
    ILease lease = (ILease)base.InitializeLifetimeService();

    if(lease.CurrentState == LeaseState.Initial) {
        lease.InitialLeaseTime = TimeSpan.FromHours(24);
        lease.SponsorshipTimeout = TimeSpan.FromSeconds(30);
        lease.RenewOnCallTime = TimeSpan.FromHours(1);
    }
    return lease;
}

#region ISponsor Members

[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
public TimeSpan Renewal(ILease lease) 
{
    return TimeSpan.FromHours(12);
}
#endregion

Если вы включите интерфейс ISponsor как часть вашего серверного объекта, вы можете реализовать приведенный выше код.

Надеюсь, некоторые из них полезны.

0 голосов
/ 29 ноября 2011

В конечном итоге, чтобы решить эту проблему, я воспользовался советом @marco и постами, которые он упомянул. Я создал сервис, который будет полностью независим от приложения в трее, которое взаимодействует с пользователем. Однако я установил приложение Tray с помощью методов запуска реестра вместе со службой. Установщик службы теперь установит приложение, которое также взаимодействует с пользователем ... Это был самый безопасный и наиболее полный метод.

спасибо за вашу помощь всем.

0 голосов
/ 25 ноября 2011

Когда вы регистрируете свой сервис, вы можете указать, что он разрешает взаимодействие с рабочим столом. Вы можете прочитать эту старую ссылку http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx

Кроме того, не забывайте, что несколько пользователей могут одновременно войти в систему.

Очевидно, что в Windows Vista и более новых версиях взаимодействие с рабочим столом стало более сложным. Прочитайте это для потенциального решения: http://www.codeproject.com/KB/cs/ServiceDesktopInteraction.aspx

...