Как правильно создать приложение WPF с одним экземпляром?

Я считаю, что картирование изображений работает очень хорошо. Если у вас есть верхние или нижние колонтитулы, которые являются образами, убедитесь, что вы применили bgcolor = "заполнить пробел", потому что Outlook в большинстве случаев не загрузит изображение, и вы останетесь с прозрачным заголовком. Если вы, по крайней мере, обозначаете цвет, который работает со всеми ощущениями электронной почты, он будет менее шоком для пользователя. Никогда не пытайтесь использовать листы для стилизации. Или CSS вообще! Просто избегайте этого.

В зависимости от того, копируете ли вы контент из слова или совместно с Google Doc (команда + F), найдите все (') и (") и замените их в своем программном обеспечении для редактирования (особенно dreemweaver) потому что они будут отображаться как код, и это просто не хорошо.

ALT - ваш лучший друг. Используйте тег ALT, чтобы добавить текст ко всем вашим изображениям. Так как вероятность того, что они не будут загружаться правильно. И этот текст ALT - это то, что заставляет людей щелкнуть по кнопке (см. Изображения). Также определите ваши изображения Ширина, Высота и сделайте границы 0, чтобы не было странных линий вокруг вашего изображения.

Рассмотрите возможность редактирования всех изображений в Photoshop с 15px-границами с каждой стороны (сделать прозрачный фон и сохранять как PNG 24). Иногда клиенты электронной почты не читают стили заполнения, которые вы применяете к изображениям, чтобы избежать любого странного форматирования!

Также я нашел строку под ссылками, особенно раздражающую, поэтому, если вы примените & lt; style = "text-decoration: none; цвет: # какой цвет вы хотите здесь! "> он удалит линию и даст вам желаемый вид.

Существует много, что может действительно испортить внешний вид.

616
задан Simon_Weaver 18 October 2018 в 21:36
поделиться

11 ответов

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

Первый, это не требует зависимости от Microsoft. Блок VisualBasic. Если бы мой проект уже имел зависимость от того блока, то я, вероятно, рекомендовал бы использовать подход показанный в другом ответе . Но как это, я не использую Microsoft. Блок VisualBasic, и я не добавил бы ненужную зависимость к своему проекту.

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

<час>

ОБНОВЛЕНИЕ

С 01.08.2014, статья, которую я связал с вышеупомянутым, все еще активна, но блог не был обновлен в некоторое время. Это заставляет меня волноваться, что в конечном счете это могло бы исчезнуть, и с ним, защищенное решение. Я воспроизвожу содержание статьи здесь для потомства. Слова принадлежат только владельцу блога в Исправность Бесплатное Кодирование .

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

Ранее у меня было использование Система. Диагностика. Процесс для поиска экземпляра моего myapp.exe в списке процессов. В то время как это работает, это навлекает много издержек, и я хотел что-то инструмент для очистки.

Знание, что я мог использовать взаимное исключение для этого (но никогда не сделавший его прежде) я намеревался сокращать свой код и упрощать мою жизнь.

В классе моего приложения, основного, я создал помехи, именованные Взаимное исключение :

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

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

Взаимное исключение. WaitOne имеет перегрузку, которая указывает количество времени для нас для ожидания. Так как мы на самом деле не желаем к синхронизации нашего кода (более справедливая проверка, если это используется в настоящее время), мы используем перегрузку с двумя параметрами: Взаимное исключение. WaitOne (Тайм-аут промежутка, bool exitContext) . Ожидайте каждый возвращает true, если он может войти, и ложь, если это не было. В этом случае мы не хотим ожидать вообще; Если наше взаимное исключение используется, пропустите его, и движение, таким образом, мы передаем в TimeSpan. Нуль (ожидают 0 миллисекунд), и установил exitContext на истинный, таким образом, мы можем выйти из контекста синхронизации, прежде чем мы попробуем к aquire блокировку на нем. Используя это, мы переносим наше Приложение. Выполненный код в чем-то вроде этого:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

Так, если наше приложение работает, WaitOne возвратит false, и мы получим окно сообщения.

Вместо того, чтобы показать окно сообщения, я решил использовать немного Win32, чтобы уведомить мой рабочий экземпляр, что кто-то забыл, что это уже работало (путем приведения себя к вершине всех других окон). Для достижения этого, я использовал постсообщение для широковещательной передачи пользовательского сообщения к каждому окну (пользовательское сообщение было зарегистрировано в [1 113] RegisterWindowMessage моим запущенным приложением, что означает, что только мое приложение знает то, что это), затем мои вторые выходы экземпляра. Экземпляр запущенного приложения получил бы то уведомление и обработал бы его. Чтобы сделать это, я переопределил WndProc в моей основной форме и прислушался к своему пользовательскому уведомлению. Когда я получил то уведомление, я установил свойство TopMost формы на истинный для перевода в рабочее состояние его на вершине.

Вот то, с чем я закончил:

  • Program.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Fo rm1.cs (неравнодушная передняя сторона)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}
499
ответ дан DaveInCaz 19 October 2018 в 07:36
поделиться
  • 1
    Хорошо, спасибо, таким образом, заключение, если нет никакой причины с помощью насмешек, можно сделать их статичными, иначе не делайте их статичными. право? – michel.iamit 20 March 2009 в 07:34

Вот мои 2 цента

 static class Program
    {
        [STAThread]
        static void Main()
        {
            bool createdNew;
            using (new Mutex(true, "MyApp", out createdNew))
            {
                if (createdNew) {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    var mainClass = new SynGesturesLogic();
                    Application.ApplicationExit += mainClass.tray_exit;
                    Application.Run();
                }
                else
                {
                    var current = Process.GetCurrentProcess();
                    foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
                    {
                        NativeMethods.SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
        }
    }
0
ответ дан kakopappa 18 October 2018 в 21:36
поделиться

От здесь .

общее использование А для межпроцессного Взаимного исключения должно гарантировать, что только экземпляр программы может работать за один раз. Вот то, как это сделано:

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

А хорошая функция Взаимного исключения - то, что, если приложение завершается без ReleaseMutex, сначала будучи названным, CLR выпустит Взаимное исключение автоматически.

82
ответ дан abatishchev 18 October 2018 в 21:36
поделиться

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

, Когда я вошел в WPF, я придумал способ использовать тот же самый код, но в приложении WPF. Это решение должно удовлетворить Ваши потребности, базирующиеся от Вашего вопроса.

Первый, мы должны создать наш класс приложений. В этом классе мы идем, переопределяют событие OnStartup и создают метод под названием, Активируются, который будет использоваться позже.

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}
117-секундный, мы должны будем создать класс, который может управлять нашими экземплярами. Прежде чем мы пройдем это, мы на самом деле собираемся снова использовать некоторый код, который находится в Microsoft. Блок VisualBasic. С тех пор я использую C# в этом примере, я должен был сделать ссылку на блок. При использовании VB.NET Вы ничего не должны делать. Классом, который мы собираемся использовать, является WindowsFormsApplicationBase, и наследуйте наш менеджер экземпляра прочь его и затем усильте свойства и события для обработки единственного инстанцирования.

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

В основном, мы используем биты VB для обнаружения единственного экземпляра и процесс соответственно. OnStartup будет запущен, когда первая инстанция загрузится. OnStartupNextInstance запущен, когда приложение повторно выполняется снова. Как Вы видите, я могу добраться до того, что было передано командной строке через аргументы события. Я установил значение к полю экземпляра. Вы могли проанализировать командную строку здесь, или Вы могли передать ее своему приложению через конструктора и вызов к Активировать методу.

В-третьих, пора создать наш EntryPoint. Вместо newing приложение как Вы обычно делало бы, мы собираемся использовать в своих интересах наш SingleInstanceManager.

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

ну, я надеюсь, что Вы в состоянии следовать, все и быть в состоянии использует эту реализацию и делает ее Вашим собственным.

101
ответ дан Dale Ragan 18 October 2018 в 21:36
поделиться

Вы никогда не должны использовать именованное взаимное исключение для реализовывания приложения единственного экземпляра (или по крайней мере не для производственного кода). Вредоносный код может легко DoS ( Отказ в обслуживании ) Ваша задница...

5
ответ дан Peter Mortensen 19 October 2018 в 07:36
поделиться

Я использую Взаимное исключение в моем решении для предотвращения нескольких экземпляров.

static Mutex mutex = null;
//A string that is the name of the mutex
string mutexName = @"Global\test";
//Prevent Multiple Instances of Application
bool onlyInstance = false;
mutex = new Mutex(true, mutexName, out onlyInstance);

if (!onlyInstance)
{
  MessageBox.Show("You are already running this application in your system.", "Already Running..", MessageBoxButton.OK);
  Application.Current.Shutdown();
}
0
ответ дан Vishnu Babu 28 July 2019 в 17:20
поделиться
  • 1
    Действительно реальной проблемой является стиль процедурного программирования, продвинутый таким " services" быть их статичными методами или нет. Можно чувствовать запах анемичной модели предметной области под. – Nils Wloka 18 March 2009 в 14:48

Просто некоторые мысли: Бывают случаи, когда требуется, чтобы только один экземпляр приложения не был «хромым», как некоторые думают. Приложения баз данных и т. Д. На порядок сложнее, если разрешить нескольким экземплярам приложения для одного пользователя получить доступ к базе данных (вы знаете, все это обновление всех записей, открытых в нескольких экземплярах приложения для пользователей машина и т. д.). Во-первых, для «конфликта имен» не используйте имя, удобочитаемое человеком - используйте вместо него GUID или, что еще лучше, GUID + удобочитаемое имя. Вероятность столкновения имен просто пропала из поля зрения, и Mutex все равно Как кто-то заметил, DOS-атака будет отстой, но если злоумышленник позаботился о том, чтобы получить имя мьютекса и встроить его в свое приложение, вы в любом случае в значительной степени являетесь целью, и вам придется сделать ГЛАВНОЕ больше для защиты. самостоятельно, чем просто возиться с именем мьютекса. Также, если используется вариант: new Mutex (true, «некоторый GUID плюс имя», out AIsFirstInstance), у вас уже есть индикатор того, является ли Mutex первым экземпляром.

8
ответ дан 22 November 2019 в 21:53
поделиться

Что ж, у меня есть одноразовый класс для этого, который легко работает для большинства случаев использования:

Используйте его так:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

Вот он:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}
19
ответ дан 22 November 2019 в 21:53
поделиться

Так много ответов на такой, казалось бы, простой вопрос. Чтобы немного встряхнуть ситуацию, вот мое решение этой проблемы.

Создание мьютекса может быть хлопотным, потому что JIT-er видит, что вы используете его только для небольшой части кода, и хочет пометить его как готовый к сборке мусора. Он практически хочет перехитрить вас, думая, что вы не собираетесь использовать этот мьютекс так долго. На самом деле вы хотите держать этот мьютекс до тех пор, пока работает ваше приложение. Лучший способ сказать сборщику мусора, чтобы он оставил ваш мьютекс в покое, - это сказать ему, чтобы он сохранял его живым в разных поколениях сборщика мусора. Пример:

var m = new Mutex(...);
...
GC.KeepAlive(m);

Я взял эту идею с этой страницы: http://www.ai.uga.edu/~mc/SingleInstance.html

6
ответ дан 22 November 2019 в 21:53
поделиться

Новым приложением, использующим Mutex и IPC, а также передающим любые аргументы командной строки работающему экземпляру, является WPF Single Instance Application .

14
ответ дан 22 November 2019 в 21:53
поделиться

Вот что я использую. Он объединил перечисление процессов для выполнения переключения и мьютекс для защиты от "активных кликеров":

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }
4
ответ дан 22 November 2019 в 21:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: