Усовершенствовать запрет на запуск копии.
От: MAMOHT  
Дата: 10.12.14 07:49
Оценка:
Добрый день, уважаемые.

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

            bool createdNew;
            string mutName = "TrendView";
            var mut = new System.Threading.Mutex(true, mutName, out createdNew);
            if (!createdNew)
            {
                //тут нужно передать параметры в уже работающую программу.  
                Shutdown();
            }


Какой механизм такое позволяет. Параметры — это string и int;

P.S. Если это важно, то WPF.

Спасибо.
Re: Усовершенствовать запрет на запуск копии.
От: icWasya  
Дата: 10.12.14 08:15
Оценка:
Здравствуйте, MAMOHT, Вы писали:

MAM>Добрый день, уважаемые.


MAM>Встала вот такая проблема:

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

MAM>
MAM>            bool createdNew;
MAM>            string mutName = "TrendView";
MAM>            var mut = new System.Threading.Mutex(true, mutName, out createdNew);
MAM>            if (!createdNew)
MAM>            {
MAM>                //тут нужно передать параметры в уже работающую программу.  
MAM>                Shutdown();
MAM>            }
MAM>


MAM>Какой механизм такое позволяет. Параметры — это string и int;


MAM>P.S. Если это важно, то WPF.


MAM>Спасибо.


Запаковать в массив байт.
Затем использовать WM_COPYDATA
Re: Усовершенствовать запрет на запуск копии.
От: Sinatr Германия  
Дата: 10.12.14 08:29
Оценка:
Здравствуйте, MAMOHT, Вы писали:

MAM>Необходимо сделать так, чтобы при повторном запуске, копия передавала новые параметры в уже запущенное приложение и завершало работу.


Смотрите в сторону IPC.

Схематически (winforms, делал лет 10 назад, лень разбираться):

— попробовать присвоить Mutex, получилось — сервер, не получилось — клиент.
— сервер подписывается на
        protected override void WndProc(ref Message m)
        {
            // get data
            Filter.GetData(ref m, this);
            base.WndProc(ref m);
        }
где
        public static void GetData(ref Message m, FormMain form)
        {
            // if not registered yet
            if (_message == 0)
                _message = RegisterWindowMessage("SomeUniqueMessageString");
            // if registered message incoming
            if (m.Msg == _message)
            {
                // generate file name
                string fileName = "Lalala" + m.WParam.ToString() + m.LParam.ToString();
                // get data
                IntPtr fileMap = OpenFileMapping(FILE_MAP_READ, false, fileName);
                IntPtr buf = MapViewOfFile(fileMap, FILE_MAP_READ, 0, 0, 0);
                // read
                XmlDocument document = new XmlDocument();
                document.LoadXml(Marshal.PtrToStringAnsi(buf));
                ...
                // finalize
                UnmapViewOfFile(buf);
                CloseHandle(fileMap);
            }
        }
- клиент шлет как-то так
                public static void SendData(string file)
                {
                    // find window
                    hWnd = FindWindow(null, "MyAppIsTheBest");
                    if (hWnd != IntPtr.Zero)
                    {
                        // if not registered yet
                        if (_message == 0)
                            _message = RegisterWindowMessage("SomeUniqueMessageString");
                        // generate filename
                        Random random = new Random();
                        IntPtr lparam = (IntPtr)random.NextDouble();
                        IntPtr wparam = (IntPtr)random.NextDouble();
                        string fileName = "Lalala" + wparam.ToString() + lparam.ToString();
                        // open file and map it
                        FileStream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                        IntPtr fileMap = CreateFileMapping(stream.Handle, IntPtr.Zero, PAGE_READONLY, 0, 0, fileName);
                        // send notification message
                        SendMessage(hWnd, _message, wparam, lparam);
                        // close
                        UnmapViewOfFile(fileMap);
                        CloseHandle(fileMap);
                        stream.Close();
                        // delete file
                        while (File.Exists(file))
                        {
                            try { File.Delete(file); }
                            catch { }
                        }
                  }

Вроде бы как клиент создает файл (содержащий данные для передачи), мапит его и шлет мессагу (содержащую имя файла) серверу. Сервер тоже мапит, считывает, обрабатывает. Все DllImport искать на PInvoke.
---
ПроГLамеры объединяйтесь..
Re: Усовершенствовать запрет на запуск копии.
От: Rinbe Россия  
Дата: 10.12.14 09:06
Оценка:
Здравствуйте, MAMOHT, Вы писали:

MAM>Добрый день, уважаемые.


MAM>Встала вот такая проблема:

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

MAM>
MAM>            bool createdNew;
MAM>            string mutName = "TrendView";
MAM>            var mut = new System.Threading.Mutex(true, mutName, out createdNew);
MAM>            if (!createdNew)
MAM>            {
MAM>                //тут нужно передать параметры в уже работающую программу.  
MAM>                Shutdown();
MAM>            }
MAM>


MAM>Какой механизм такое позволяет. Параметры — это string и int;


MAM>P.S. Если это важно, то WPF.


MAM>Спасибо.


Я через файл как то делал. Приложение при запуске проверяет есть ли уже запущенный экземпляр, если есть,
то создает файл с параметрами запуска в определенной папке, другой экземпляр получает уведомление об этом у
FileSystemWatcher и считывает параметры.
Re[2]: Усовершенствовать запрет на запуск копии.
От: GlebZ Россия  
Дата: 10.12.14 10:51
Оценка: +1
Здравствуйте, Rinbe, Вы писали:

R>Я через файл как то делал. Приложение при запуске проверяет есть ли уже запущенный экземпляр, если есть,

R>то создает файл с параметрами запуска в определенной папке, другой экземпляр получает уведомление об этом у
R>FileSystemWatcher и считывает параметры.
Единственная поправка — не стоит делать через FileSystemWatcher. При большой нагрузке на диск имеет свойство пропадать события (Microsoft не гарантирует). Проще через именованный Event
Re[3]: Усовершенствовать запрет на запуск копии.
От: Rinbe Россия  
Дата: 10.12.14 11:49
Оценка: -1
Здравствуйте, GlebZ, Вы писали:

GZ>Здравствуйте, Rinbe, Вы писали:


R>>Я через файл как то делал. Приложение при запуске проверяет есть ли уже запущенный экземпляр, если есть,

R>>то создает файл с параметрами запуска в определенной папке, другой экземпляр получает уведомление об этом у
R>>FileSystemWatcher и считывает параметры.
GZ>Единственная поправка — не стоит делать через FileSystemWatcher. При большой нагрузке на диск имеет свойство пропадать события (Microsoft не гарантирует). Проще через именованный Event

Да такое может быть но на практике на обычном детскопе не наблюдалось.
Re: Усовершенствовать запрет на запуск копии.
От: koodeer  
Дата: 10.12.14 12:02
Оценка: +2
Здравствуйте, MAMOHT, Вы писали:

MAM>Есть приложение, которое показывает некий график. Параметры ему передаются в командной строке.

MAM>Необходимо сделать так, чтобы при повторном запуске, копия передавала новые параметры в уже запущенное приложение и завершало работу.

Я думаю, проще всего сделать это с помощью пайпов — pipes.
Re[2]: Усовершенствовать запрет на запуск копии.
От: hardcase Пират http://nemerle.org
Дата: 10.12.14 12:12
Оценка:
Здравствуйте, koodeer, Вы писали:

K>Я думаю, проще всего сделать это с помощью пайпов — pipes.


Они же заменят мутекс\семафор.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: Усовершенствовать запрет на запуск копии.
От: GlebZ Россия  
Дата: 10.12.14 13:31
Оценка:
Здравствуйте, hardcase, Вы писали:

K>>Я думаю, проще всего сделать это с помощью пайпов — pipes.

H>Они же заменят мутекс\семафор.
Только писанины много, да и сама концепция пайпов несколько необычная, со своими прибаутками. WCF поднять быстрее.
Re[4]: Усовершенствовать запрет на запуск копии.
От: andrey82  
Дата: 10.12.14 14:45
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Только писанины много, да и сама концепция пайпов несколько необычная, со своими прибаутками. WCF поднять быстрее.


А какой WCF binding для такого сценария лучше всего подойдет (чтобы не требовал настроек системы и раздачи каких-то особенных прав процессам/файлам и т.п.) ?
Re[5]: Усовершенствовать запрет на запуск копии.
От: RushDevion Россия  
Дата: 10.12.14 15:16
Оценка: +1
A>А какой WCF binding для такого сценария лучше всего подойдет (чтобы не требовал настроек системы и раздачи каких-то особенных прав процессам/файлам и т.п.) ?
NamedPipeBinding?
Re[2]: Усовершенствовать запрет на запуск копии.
От: Философ Ад http://vk.com/id10256428
Дата: 10.12.14 15:46
Оценка:
привязываться к заголовку окна недопустимо — нифига не уникальный объект
Всё сказанное выше — личное мнение, если не указано обратное.
Re: Усовершенствовать запрет на запуск копии.
От: Danchik Украина  
Дата: 11.12.14 14:49
Оценка: :))
Здравствуйте, MAMOHT, Вы писали:

[Skip]

MAM>Спасибо.


Поигрался чуток, вот что получилось. Не WPF но как то попроще

В своем проекте, заходим в NuGet Package Console и ставим пакет NetMq

Install-Package NetMq


Заходим в файл App.xaml.cs и дополняем класс App:

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private const string Address = "ipc:///tmp/feeds/MyApplication";
        private NetMQContext _ctx;
        private ResponseSocket _server;
        private volatile bool _shouldStop = false;
        private Thread _thread;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            _ctx = NetMQContext.Create();

            _server = _ctx.CreateResponseSocket();
            _thread = null;
            try
            {
                _server.Bind(Address);

                _thread = new Thread(MonitorOthers);
                _thread.Start();

                Debug.WriteLine("I'm FIRST");
            }
            catch (NetMQException)
            {
                // already started
                using (var client = _ctx.CreateRequestSocket())
                {
                    client.Connect(Address);
                    client.Send("Process " + Process.GetCurrentProcess().Id + " - Do something");
                    var response = client.ReceiveString();

                    Debug.WriteLine("Other client already started! - " + response);
                    Debug.WriteLine("Exiting...");
                    
                    Current.Shutdown();
                }
            }
        }

        private void MonitorOthers()
        {
            while (!_shouldStop)
            {
                var message = _server.ReceiveString();
                if (!string.IsNullOrEmpty(message))
                {
                    Debug.WriteLine(string.Format("Secondary: {0}", message));
                    _server.Send("Got it - " + Process.GetCurrentProcess().Id);
                }
            }
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);

            if (_thread != null)
            {
                _shouldStop = true;
                _thread.Join();
                _thread = null;
            }

            using (_server)
                _server = null;
            using (_ctx)
                _ctx = null;
        }
    }


Вот и все.
Re: Усовершенствовать запрет на запуск копии.
От: Peshuha Россия  
Дата: 19.12.14 12:17
Оценка:
Здравствуйте, MAMOHT, Вы писали:

сделайте внешнее сообщение юзерское.. и шлите вашему процессу.. Можно также по каналу (pipe)
... Мы не привыкли отступать! И расколоть его поможет киножурнал "Хочу Все Знать"! ;)
Re: Усовершенствовать запрет на запуск копии.
От: Vladek Россия Github
Дата: 26.12.14 23:14
Оценка:
Здравствуйте, MAMOHT, Вы писали:

MAM>Встала вот такая проблема:

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

Меня не покидает ощущение, что я уже выкладывал этот код.

  SingleInstance.cs
namespace WpfApp.Configuration
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Deployment.Application;
    using System.IO;
    using System.Linq;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Ipc;
    using System.Runtime.Serialization.Formatters;
    using System.Threading;
    using System.Windows;
    using System.Windows.Threading;

    public interface ISingleInstanceApp
    {
        void ActivateInstance(IList<string> args);
    }

    /// <summary>
    /// This class checks to make sure that only one instance of
    /// this application is running at a time.
    /// </summary>
    public static class SingleInstance<TApplication>
        where TApplication : Application, ISingleInstanceApp
    {
        /// <summary>
        /// Suffix to the channel name.
        /// </summary>
        private const string ChannelNameSuffix = "SingeInstanceIpcChannel";

        /// <summary>
        /// String delimiter used in channel names.
        /// </summary>
        private const string Delimiter = ":";

        /// <summary>
        /// IPC protocol used (string).
        /// </summary>
        private const string IpcProtocol = "ipc://";

        /// <summary>
        /// Remote service name.
        /// </summary>
        private const string RemoteServiceName = "SingleInstanceApplicationService";

        /// <summary>
        /// IPC channel for communications.
        /// </summary>
        private static IpcServerChannel _channel;

        private static IList<string> _commandLineArgs;

        /// <summary>
        /// Application mutex.
        /// </summary>
        private static Mutex _singleInstanceMutex;

        public static IList<string> CommandLineArgs
        {
            get
            {
                return _commandLineArgs;
            }
        }

        /// <summary>
        /// Cleans up single-instance code, clearing shared resources, mutexes, etc.
        /// </summary>
        public static void Cleanup()
        {
            if (_singleInstanceMutex != null)
            {
                _singleInstanceMutex.Close();
                _singleInstanceMutex = null;
            }

            if (_channel != null)
            {
                ChannelServices.UnregisterChannel(_channel);
                _channel = null;
            }
        }

        /// <summary>
        /// Checks if the instance of the application attempting to start is the first instance.
        /// If not, activates the first instance.
        /// </summary>
        /// <returns>True if this is the first instance of the application.</returns>
        public static bool InitializeAsFirstInstance(string uniqueName)
        {
            _commandLineArgs = GetCommandLineArgs();

            // Build unique application Id and the IPC channel name.
            string applicationIdentifier = uniqueName + Environment.UserName;

            string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix);

            // Create mutex based on unique application Id to check if this is the first instance of the application.
            bool firstInstance;
            _singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance);
            if (firstInstance)
            {
                CreateRemoteService(channelName);
            }
            else
            {
                SignalFirstInstance(channelName, _commandLineArgs);
            }

            return firstInstance;
        }

        #region Private Methods

        /// <summary>
        /// Activates the first instance of the application with arguments from a second instance.
        /// </summary>
        /// <param name="args">List of arguments to supply the first instance of the application.</param>
        private static void ActivateFirstInstance(IList<string> args)
        {
            // Set main window state and process command line args
            if (Application.Current == null)
            {
                return;
            }

            ((TApplication)Application.Current).ActivateInstance(args);
        }

        /// <summary>
        /// Callback for activating first instance of the application.
        /// </summary>
        /// <param name="arg">Callback argument.</param>
        /// <returns>Always null.</returns>
        private static object ActivateFirstInstanceCallback(object arg)
        {
            // Get command line args to be passed to first instance
            IList<string> args = arg as IList<string>;
            ActivateFirstInstance(args);
            return null;
        }

        /// <summary>
        /// Creates a remote service for communication.
        /// </summary>
        /// <param name="channelName">Application's IPC channel name.</param>
        private static void CreateRemoteService(string channelName)
        {
            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
            serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
            IDictionary props = new Dictionary<string, string>();

            props["name"] = channelName;
            props["portName"] = channelName;
            props["exclusiveAddressUse"] = "false";

            // Create the IPC Server channel with the channel properties
            _channel = new IpcServerChannel(props, serverProvider);

            // Register the channel with the channel services
            ChannelServices.RegisterChannel(_channel, true);

            // Expose the remote service with the REMOTE_SERVICE_NAME
            IpcRemoteService remoteService = new IpcRemoteService();
            RemotingServices.Marshal(remoteService, RemoteServiceName);
        }

        /// <summary>
        /// Gets command line arguments - for ClickOnce deployed applications,
        /// command line arguments may not be passed directly, they have to be retrieved.
        /// </summary>
        /// <returns>List of command line argument strings.</returns>
        private static IList<string> GetCommandLineArgs()
        {
            IEnumerable<string> args = null;
            if (AppDomain.CurrentDomain.ActivationContext == null)
            {
                // The application was not ClickOnce deployed, get arguments from standard APIs
                // Skip the executable file name
                args = Environment.GetCommandLineArgs().Skip(1);
            }
            else if (ApplicationDeployment.IsNetworkDeployed)
            {
                // The application was ClickOnce deployed
                // ClickOnce deployed apps cannot receive traditional command line arguments

                if (AppDomain.CurrentDomain.SetupInformation.ActivationArguments != null)
                {
                    args = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;
                }
            }

            if (args == null)
            {
                args = Enumerable.Empty<string>();
            }

            return args.ToArray();
        }

        /// <summary>
        /// Creates a client channel and obtains a reference to the remoting service exposed by the server -
        /// in this case, the remoting service exposed by the first instance. Calls a function of the remoting service
        /// class from the second instance to the first and cause it to activate itself.
        /// </summary>
        /// <param name="channelName">Application's IPC channel name.</param>
        /// <param name="args">
        /// Command line arguments for the second instance, passed to the first instance to take appropriate action.
        /// </param>
        private static void SignalFirstInstance(string channelName, IList<string> args)
        {
            IpcClientChannel secondInstanceChannel = new IpcClientChannel();
            ChannelServices.RegisterChannel(secondInstanceChannel, true);

            string remotingServiceUrl = IpcProtocol + channelName + "/" + RemoteServiceName;

            // Obtain a reference to the remoting service exposed by the server i.e the first instance of the application
            IpcRemoteService firstInstanceRemoteServiceReference = (IpcRemoteService)RemotingServices.Connect(typeof(IpcRemoteService), remotingServiceUrl);

            // Check that the remote service exists, in some cases the first instance may not yet have created one, in which case
            // the second instance should just exit
            if (firstInstanceRemoteServiceReference != null)
            {
                // Invoke a method of the remote service exposed by the first instance passing on the command line
                // arguments and causing the first instance to activate itself
                firstInstanceRemoteServiceReference.InvokeFirstInstance(args);
            }
        }

        #endregion Private Methods

        #region Private Classes

        /// <summary>
        /// Remoting service class which is exposed by the server i.e the first instance and called by the second instance
        /// to cause the first instance to activate itself.
        /// </summary>
        private class IpcRemoteService : MarshalByRefObject
        {
            /// <summary>
            /// Remoting Object's ease expires after every 5 minutes by default. We need to override the InitializeLifetimeService class
            /// to ensure that lease never expires.
            /// </summary>
            /// <returns>Always null.</returns>
            public override object InitializeLifetimeService()
            {
                return null;
            }

            /// <summary>
            /// Activates the first instance of the application.
            /// </summary>
            public void InvokeFirstInstance(IList<string> args)
            {
                if (Application.Current != null)
                {
                    // Do an asynchronous call to ActivateFirstInstance function
                    Application.Current.Dispatcher.BeginInvoke(
                        DispatcherPriority.Normal, new DispatcherOperationCallback(SingleInstance<TApplication>.ActivateFirstInstanceCallback), args);
                }
            }
        }

        #endregion Private Classes
    }
}


Как использовать в WPF:
namespace WpfApp
{
    // ...

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application, ISingleInstanceApp
    {
        private const string InstanceKey = "WpfApp_InstanceKey";

        [STAThread]
        public static void Main()
        {
            if (SingleInstance<App>.InitializeAsFirstInstance(InstanceKey))
            {
                var application = new App();

                application.InitializeComponent();
                application.Run();

                // Allow single instance code to perform cleanup operations
                SingleInstance<App>.Cleanup();
            }
        }

        void ISingleInstanceApp.ActivateInstance(IList<string> args)
        {
            // Someone tried to run the app again
            // 'args' has the command line arguments
        }

        // ...
    }
}


Код был найден где-то на просторах сети и усовершенствован для корректной работы в окружении ClickOnce. Работает!
Re[3]: Усовершенствовать запрет на запуск копии.
От: breee breee  
Дата: 04.01.15 22:26
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Единственная поправка — не стоит делать через FileSystemWatcher. При большой нагрузке на диск имеет свойство пропадать события (Microsoft не гарантирует).


А откуда инфа, что Microsoft не гарантирует?
Re[4]: Усовершенствовать запрет на запуск копии.
От: Draqon  
Дата: 05.01.15 09:20
Оценка:
BB>А откуда инфа, что Microsoft не гарантирует?

Из MSDN. FileSystemWatcher использует ReadDirectoryChangesW, а про нее в MSDN написано что она может терять события.
Re[5]: Усовершенствовать запрет на запуск копии.
От: breee breee  
Дата: 05.01.15 18:35
Оценка:
Здравствуйте, Draqon, Вы писали:

BB>>А откуда инфа, что Microsoft не гарантирует?


D>Из MSDN. FileSystemWatcher использует ReadDirectoryChangesW, а про нее в MSDN написано что она может терять события.


В изначальном посте говорилось о потерях при "большой нагрузке на диск", а в MSDN вижу только про потери при переполнении буфера. Поэтому, если создать файл в отдельном каталоге и использовать фильтр FILE_NOTIFY_CHANGE_LAST_WRITE, то потерь вроде не должно быть

Другое дело, что нет гарантий, что при приходе события "пишущий" процесс закончил работу с файлом — зафлашил весь текст и закрыл его. Поэтому я бы предпочел писать в реестр.
Отредактировано 05.01.2015 18:52 breee breee . Предыдущая версия . Еще …
Отредактировано 05.01.2015 18:51 breee breee . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.