std::lock_guard — C++最简单的线程安全锁(避免死锁)

std::lock_guard 简单用法

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

void increment()
{
    lock_guard<mutex> lock(g_mutex); // 开启后g_count总是可以输出20000,否则会少于20000
    ++g_count;
    this_thread::sleep_for(chrono::microseconds(2));
}

void run(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment();
    }
}

int main()
{

    thread th1(run, 10000);
    thread th2(run, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

直接使用lock容易不对称导致死锁

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

int increment1()
{
    g_mutex.lock();
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    g_mutex.unlock();
    return 0;
}

int increment2()
{
    g_mutex.lock();
    this_thread::sleep_for(chrono::microseconds(1));
    if (g_count > 11000)
    {
        return 0;
    }
    g_count++;
    g_mutex.unlock();
    return 0;
}

void run1(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment1();
    }
}

void run2(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment2();
    }
}

int main()
{

    thread th1(run1, 10000);
    thread th2(run2, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

使用lock_guard避免死锁

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

int increment1()
{
    lock_guard<mutex> lock(g_mutex);
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    return 0;
}

int increment2()
{
    lock_guard<mutex> lock(g_mutex);
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    if (g_count > 11000)
    {
        return 0;
    }
    return 0;
}

void run1(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment1();
    }
}

void run2(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment2();
    }
}

int main()
{

    thread th1(run1, 10000);
    thread th2(run2, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

防止list下标越界,同样适用于map,set,vector等

#include <iostream>
#include <thread>
#include <mutex>
#include <list>

using namespace std;

class MonitorList
{

private:
    mutex g_mutex;
    list<int> g_list = {};

public:
    void push()
    {
        lock_guard<mutex> lock(g_mutex); // 不加会导致程序异常
        this_thread::sleep_for(chrono::microseconds(1));
        g_list.push_back(0);
    }

    void pop()
    {
        lock_guard<mutex> lock(g_mutex); // 不加会导致程序异常
        this_thread::sleep_for(chrono::microseconds(1));
        for (auto it = g_list.begin(); it != g_list.end(); ++it)
        {
            if (*it % 2 == 0)
            {
                it = g_list.erase(it);
            }
        }
    }
    int size()
    {
        return g_list.size();
    }
};

MonitorList g_MonitorList;

int main()
{

    thread th1([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.push();
                   }
               },
               10000);

    thread th2([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.pop();
                   }
               },
               3000);

    thread th3([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.pop();
                   }
               },
               3000);
    th1.join();
    th2.join();
    th3.join();

    cout << g_MonitorList.size() << endl;

    return 0;
}

使用ab,siege,wrk,jmeter进行快速JSON API性能测试

ab

ab -c 10 -n 100 -T 'application/json' -p test.json https://abc.com/test

wrk (推荐,高性能,多核多线程)

https://github.com/wg/wrk

Wrk 是一个现代的 HTTP 基准测试工具,能够在单个多核 CPU 上运行时产生大量负载。它将多线程设计与可伸缩的事件通知系统(如 epoll 和 kqueue)结合在一起。

可以使用lua脚本生成测试请求

siege

siege -c50 -t60S --content-type "application/json" 'http://domain.com/path/ POST {"ids": ["1","2","3"]}'

Jmeter

下载安装jmeter后,运行bin目录下的jmeterw.cmd即可。
file

待研究 plow

https://github.com/six-ddc/plow

  • 跨平台,无运行时依赖(基于 golang )
  • 高性能,比常见的 hey,ab,siege 等性能高不少(基本和 wrk 持平)
  • 终端实时展示性能数据,包括 RPS,延迟统计,百分位,Histogram 分布等
  • 支持实时网页展示,更全方面展示各时间点的压测数据

待研究 hey

https://github.com/rakyll/hey

基于Go语言的ab替代工具

参考

PhpStorm/IntelliJ/Pycharm 支持git-bash以及powershell

How to use PowerShell as default terminal in IntelliJ/PhpStorm/Pycharm/…

  1. Open IntelliJ settings.
  2. Go to the “Tools” section.
  3. Press the “Terminal” option.
  4. Set the correct Shell path to the PowerShell EXE file. In the case of Windows 8.1, the path is: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe. You should use the latest PowerShell version available!
  5. Create a new terminal session in IntelliJ. Alternatively, you can just restart IntelliJ to apply the changes.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Setup JetBrains(InteliJ, WebStorm, PHPStorm) IDE terminal to use GIT bash

  1. File
  2. Settings
  3. (Enter ‘Terminal’ in search)

Change Shell path to:

"C:\Program Files\Git\bin\sh.exe" --login -i

git对于不同仓库使用不同的ssh key

配置 core.sshCommand:

从 Git 版本 2.10.0 开始, 可以通过这个配置配置全局或者个别仓库使用的key了

git config core.sshCommand "ssh -i ~/.ssh/id_rsa_example -F /dev/null"
git pull
git push

WPF使用MVVM入门

MVVM 的三个部分

让我们看一下MVVM的三个部分:模型,视图和视图模型。

视图:这些都是UI元素,是应用程序的漂亮面孔。对于WPF,这些都是您的所有XAML文件。它们可能是Windows,用户控件或资源字典。尽管完全可以从代码构造视图当然是可能的,但绝大多数UI将(并且应该)使用XAML构建。该视图可能非常动态,甚至可以处理一些用户交互(请参见下面的“命令”部分)。

视图模型:这些是为每个视图提供数据和功能的对象。通常,视图和视图模型类之间通常存在一对一的映射。视图模型类,将数据公开给视图,并提供处理用户交互的命令。与其他设计模式不同,视图模型不应该知道其视图。关注点的分离是MVVM的主要宗旨之一。视图模型是视图和模型之间的连接。

模型:从广义上讲,模型提供对应用程序所需数据和服务的访问。根据您的应用程序,这是完成实际工作的地方。虽然视图模型与将模型的数据汇总在一起有关,但是模型类执行应用程序的实际工作。如果使用依赖项注入,则通常在视图模型中将模型类作为接口构造函数参数传递。

由于视图模型与模型之间的交互将在很大程度上取决于您的特定应用程序,因此在本文的其余部分中,我们将仅着眼于视图与视图模型之间的交互。

Bidding

绑定引擎使MVVM模式成为可能。绑定在视图中声明,并将视图中的属性链接回视图模型中的属性。

public class ViewModel
{
    public string FirstName { get; set; }
}
<TextBlock Text="{Binding Path=FirstName}" VerticalAlignment="Center" HorizontalAlignment="Center"/>

上面的代码是实现MVVM模式的开始。绑定将Text属性的值设置为FirstName属性中的值。如果要运行此代码,则TextBlock的Text仍为空。这是因为没有将ViewModel类链接到Window的东西。在WPF中,此链接来自DataContext属性。

在Window的构造函数中,我们将设置其DataContext。如果在UI元素上未指定DataContext,它将继承其父级的DataContext。因此,在Window上设置DataContext将有效地为Window中的每个元素设置它。

public MainWindow()
{
    var viewModel = new ViewModel();
    viewModel.FirstName = "Kevin";

    DataContext = viewModel;
    InitializeComponent();

    viewModel.FirstName = "Mark";
}

如果我们运行应用程序,TextBox 仍将显示“ Kevin”,而不是更新后的值“ Mark”。虽然属性的值已经更改,但是没有通知 Binding 更新其值。我们可以通过实现 INotifyPropertyChanged (INPC)接口来解决这个问题。此接口有一个事件,通知绑定,特定属性已更改,使用该事件的任何绑定都应重新计算其值。

我们可以这样实现它:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string FirstName { get; set; }

    public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

现在,在 Window 的构造函数中,我们通知视图该属性已更改。

public MainWindow()
{
    var viewModel = new ViewModel();
    viewModel.FirstName = "Kevin";

    DataContext = viewModel;
    InitializeComponent();

    viewModel.FirstName = "Mark";
    viewModel.OnPropertyChanged(nameof(ViewModel.FirstName));
}

现在绑定正确地更新以显示“ Mark”。

但是,每次更改属性值时都要记住引发该事件可能会变得非常乏味。因为这种模式非常普遍,许多 MVVM 框架为你的视图模型类提供了一个基类,类似于下面这样:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
    {
        if(!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

这使我们可以像这样重写FirstName属性:

public class ViewModel : ViewModelBase
{
    private string _firstName;
    public string FirstName
    {
        get => _firstName;
        set => SetProperty(ref _firstName, value);
    }
}

Command

绑定是将数据从视图模型移入视图的一种好方法,但是我们还需要允许我们的视图模型响应用户交互。大多数具有默认用户交互功能(例如单击按钮)的用户控件均由命令处理。所有实现ICommandSource接口的用户控件都支持Command属性,当控件的默认操作发生时,该属性将被调用。有许多实现此接口的控件,例如按钮,菜单项,复选框,单选按钮,超链接等。

命令只是实现ICommand接口的对象。或者换一种说法,命令是从视图到视图模型的消息。当控件的默认事件发生时,例如单击按钮时,将调用命令上的Execute方法。更重要的是,命令还可以指示它们何时能够执行。这允许控件根据是否可以执行其命令来启用或禁用自身。

命令示例

从我们非常简单的示例中可以看出,单击按钮时,我们会更改名字的值。

首先,我们需要在视图模型中添加一个command属性:

public class ViewModel : ViewModelBase
{
    public ICommand ChangeNameCommand { get; }
    ...
}

接下来,我们将向MainWindow添加一个按钮,并使用Binding将其Command属性设置为视图模型中的命令。

<Button Content="Change Name" Command="{Binding Path=ChangeNameCommand}" VerticalAlignment="Bottom" HorizontalAlignment="Center" />

现在,我们只需要向视图模型中的ChangeNameCommand属性分配一个新的命令对象即可。不幸的是,WPF没有附带适合在视图模型中使用的默认ICommand实现,但是该接口非常简单,可以实现:


public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;

    public DelegateCommand(Action<object> executeAction)
    {
        _executeAction = executeAction;
    }

    public void Execute(object parameter) => _executeAction(parameter);

    public bool CanExecute(object parameter) => true;

    public event EventHandler CanExecuteChanged;
}

例如,在这个非常简单的实现中,执行命令时将调用Action委托。现在,我们将忽略接口的CanExecute部分,并始终允许执行命令。

现在,我们可以完成视图模型中的代码。


public class ViewModel : ViewModelBase
{
    ...

    private readonly DelegateCommand _changeNameCommand;
    public ICommand ChangeNameCommand => _changeNameCommand;

    public ViewModel()
    {
        _changeNameCommand = new DelegateCommand(OnChangeName);
    }

    private void OnChangeName(object commandParameter)
    {
        FirstName = "Walter";
    }
}

运行我们的简单应用程序,我们可以看到单击按钮确实可以更改名称。

file

接下来,让我们返回并实现ICommand接口的CanExecute部分。


public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;
    private readonly Func<object, bool> _canExecuteAction;

    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
    {
        _executeAction = executeAction;
        _canExecuteAction = canExecuteAction;
    }

    public void Execute(object parameter) => _executeAction(parameter);

    public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;

    public event EventHandler CanExecuteChanged;

    public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

与execute方法类似,此命令还将接受CanExecute委托。同样,CanExecuteChanged事件也使用公共方法公开,因此我们可以在CanExecute委托的返回值更改时随时提高它。

回到我们的视图模型中,我们将进行以下补充。


public ViewModel()
{
    _changeNameCommand = new DelegateCommand(OnChangeName, CanChangeName);
}

private void OnChangeName(object commandParameter)
{
    FirstName = "Walter";
    _changeNameCommand.InvokeCanExecuteChanged();
}

private bool CanChangeName(object commandParameter)
{
    return FirstName != "Walter";
}

调用CanChangeName以确定命令是否可以执行。在这种情况下,一旦名称更改为“ Walter”,我们将仅阻止命令执行。最后,在OnChangeName方法中更改名称后,该命令通过引发其事件来通知按钮其CanExecute状态已更改。

运行该应用程序,我们可以看到在更改名称后该按钮可以正确禁用。

file

参考

使用MailCatcher作为开发环境邮件服务器

MailCatcher 运行一个超级简单的 SMTP 服务器,它可以捕获发送到它的任何消息并在 web 界面中显示。

运行 mailcatcher,将你最喜欢的应用程序设置为 smtp://127.0.0.1:1025,而不是默认的 SMTP 服务器,然后检查 http://127.0.0.1:1080,查看到目前为止的邮件。

file

ubuntu安装MailCatcher

sudo apt install ruby ruby-dev libsqlite3-dev build-essential
sudo gem install mailcatcher

运行

默认运行即可,更多参数如下

mailcatcher -h
Usage: mailcatcher [options]
        --ip IP                      Set the ip address of both servers
        --smtp-ip IP                 Set the ip address of the smtp server
        --smtp-port PORT             Set the port of the smtp server
        --http-ip IP                 Set the ip address of the http server
        --http-port PORT             Set the port address of the http server
        --http-path PATH             Add a prefix to all HTTP paths
        --no-quit                    Don't allow quitting the process
    -f, --foreground                 Run in the foreground
    -v, --verbose                    Be more verbose
    -h, --help                       Display this help information

Cmder + Windows Terminal + PhpStrom/PyCharm/IDEA

Cmder是一个可用于linux的便携式控制台虚拟器,可以让你在windows下使用linux命令。

Windows Terminal是Windows 10的控制台管理界面。

IDEA是广大开发人员的最爱。

本文介绍如何将Cmder整合到Windows Terminal和IDEA。

file

安装Cmder

可以去 https://cmder.net/ 下载最新的压缩文件,解压后点击Cmder.exe即可使用。如果已经安装了git for windows下载mini版本就可以了,否则可以下载包含git的full版本。

设置系统环境变量

以下d:\tools\Cmder是Cmder解压目录

CMDER_ROOT = d:\tools\Cmder

配置 Windows Terminal

生成guid

进入Power shell,输入New-Guid

PS C:\Users\Administrator> New-Guid

Guid
----
092f8948-9ea5-4928-bea7-a1f71938e717

修改 Windows Terminal 配置文件

点击Windows Terminal的下拉菜单,选择“配置”,会打开它的配置文件

增加

{
  "guid": "{092f8948-9ea5-4928-bea7-a1f71938e717}",
  "name": "Cmder",
  "commandline": "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
  "startingDirectory": "%USERPROFILE%",
  "icon": "%CMDER_ROOT%\\icons\\cmder.ico",
  "background": "#2e3436",
  "padding": "15",
  "fontFace": "Cascadia Code",
  "fontSize": 10
}

然后修改

"defaultProfile": "Cmder",

Windows Terminal 使用效果

点击Windows Terminal的加号,查看效果

file

配置 PhpStorm/IDEA/PyCharm

配置 PhpStorm

打开 PhpStrom 的设置,找到 Tools 下的 terminal 修改 Shell path"cmd" /k ""%CMDER_ROOT%\vendor\init.bat""

file

PhpStrom 配置后效果

file

NSIS完整实例(含服务的安装,运行前卸载)


# NetHalt - NSIS installer script
# Copyright (C) 2008 Daniel Collins <solemnwarning@solemnwarning.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#
#    * Neither the name of the author nor the names of its contributors may
#      be used to endorse or promote products derived from this software
#      without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

!include MUI2.nsh
!include LogicLib.nsh
!include nsDialogs.nsh

Name "NetHalt"
OutFile "nhclient.exe"

InstallDir $PROGRAMFILES\NetHalt
InstallDirRegKey HKLM "SOFTWARE\NetHalt" "InstallDir"

!define MUI_ABORTWARNING

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "COPYING"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES

!insertmacro MUI_LANGUAGE "English"

Function .onInit
    nsisos::osversion

    ${If} $0 < 5
        MessageBox MB_OK "Windows 2000 (NT 5.0) or greater is required"
        Abort
    ${EndIf}
FunctionEnd

# Do local install
#
Section "NetHalt"
    SetShellVarContext all
    SetOutPath $INSTDIR

    # Stop all running nhtray.exe processes
    #
    StrCpy $0 "nhtray.exe"
    KillProc::KillProcesses

    # Check if the NetHalt service is installed
    #
    SimpleSC::ExistsService "nhclient"
    Pop $0

    # Stop+Remove the NetHalt service if it's installed
    #
    ${If} $0 == 0
        DetailPrint "Stopping NetHalt Client service..."
        SimpleSC::StopService "nhclient"

        DetailPrint "Removing NetHalt Client service..."
        SimpleSC::RemoveService "nhclient"
    ${EndIf}

    WriteRegStr HKLM "SOFTWARE\NetHalt" "InstallDir" $INSTDIR

    cinst::reg_write "dword" "SOFTWARE\NetHalt" "use_server" "0"
    cinst::reg_write "string" "SOFTWARE\NetHalt" "server_name" ""
    cinst::reg_write "dword" "SOFTWARE\NetHalt" "server_port" "0"
    cinst::reg_write "dword" "SOFTWARE\NetHalt" "server_refresh" "1800"

    cinst::reg_write "dword" "SOFTWARE\NetHalt" "warning" "300"
    cinst::reg_write "dword" "SOFTWARE\NetHalt" "abort" "0"
    cinst::reg_write "dword" "SOFTWARE\NetHalt" "delay" "0"
    cinst::reg_write "string" "SOFTWARE\NetHalt" "sdtimes" ""

    WriteUninstaller "$INSTDIR\uninstall.exe"

    File "src\nhclient.exe"
    File "src\evlog.dll"
    File "src\nhtray.exe"
    File "src\nhconfig.exe"

    # Add the event log source (evlog.dll) to the registry
    #
    WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client" "EventMessageFile" "$INSTDIR\evlog.dll"
    WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client" "TypesSupported" 0x00000007

    # Add the uninstaller to Add/Remove programs
    #
    WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "DisplayName" "NetHalt"
    WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "UninstallString" "$INSTDIR\uninstall.exe"
    WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "NoModify" 1
    WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "NoRepair" 1

    # Install and start the NetHalt service
    # TODO: Check for errors
    #
    DetailPrint "Installing NetHalt Client service..."
    SimpleSC::InstallService "nhclient" "NetHalt Client" "16" "2" "$INSTDIR\nhclient.exe" "" "" ""

    DetailPrint "Starting NetHalt Client service..."
    SimpleSC::StartService "nhclient"

    # Add nhtray.exe to the registry to run at login
    #
    WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "NetHalt" "$INSTDIR\nhtray.exe"

    # Launch nhtray.exe
    #
    Exec '"$INSTDIR\nhtray.exe"'

    # Create shortcuts
    #
    CreateDirectory "$SMPROGRAMS\NetHalt"
    CreateShortCut "$SMPROGRAMS\NetHalt\Tray Icon.lnk" "$INSTDIR\nhtray.exe"
    CreateShortCut "$SMPROGRAMS\NetHalt\Configuration.lnk" "$INSTDIR\nhconfig.exe"
    CreateShortCut "$SMPROGRAMS\NetHalt\Uninstall.lnk" "$INSTDIR\uninstall.exe"
SectionEnd

Function un.onInit
    SetShellVarContext all
    ReadRegStr $INSTDIR HKLM "SOFTWARE\NetHalt" "InstallDir"

    MessageBox MB_YESNO "This will uninstall NetHalt, continue?" IDYES NoAbort
    Abort
    NoAbort:
FunctionEnd

Section "Uninstall"
    # Stop and remove the NetHalt service
    #
    DetailPrint "Stopping NetHalt Client service..."
    SimpleSC::StopService "nhclient"

    DetailPrint "Removing NetHalt Client service..."
    SimpleSC::RemoveService "nhclient"

    # Stop all running nhtray.exe processes
    #
    StrCpy $0 "nhtray.exe"
    KillProc::KillProcesses

    # Delete shortcuts
    #
    Delete "$SMPROGRAMS\NetHalt\Tray Icon.lnk"
    Delete "$SMPROGRAMS\NetHalt\Configuration.lnk"
    Delete "$SMPROGRAMS\NetHalt\Un-Install.lnk"
    Delete "$SMPROGRAMS\NetHalt"

    DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "NetHalt"
    DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client"
    DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt"

    Delete "$INSTDIR\nhclient.exe"
    Delete "$INSTDIR\evlog.dll"
    Delete "$INSTDIR\nhtray.exe"
    Delete "$INSTDIR\uninstall.exe"
    RMDir $INSTDIR
SectionEnd

NSIS实现安装前先停止并卸载旧版

以下脚本会先找到正在运行的程序和服务,停止并删除服务,然后杀掉正在运行的程序进程

    ;检查服务是否存在
    SimpleSC::ExistsService  "${SVCHOST_EXE_NAME}"
    Pop $0
    ;停止并删除服务
    ${If} $0 == 0
        DetailPrint "停止正在运行的打印服务"
        SimpleSC::StopService "${SVCHOST_EXE_NAME}" 1 30
        DetailPrint "删除已安装的打印服务"
        SimpleSC::RemoveService "${SVCHOST_EXE_NAME}"
    ${EndIf}
    ;检查主程序是否运行,如果正在运行则进行终止
    nsProcess::_FindProcess "${ANALYST_EXE_NAME}"
    Pop $R0
    ${If} $R0 = 0
      DetailPrint "停止正在运行的主程序"
      nsProcess::_KillProcess "${ANALYST_EXE_NAME}"
      Pop $R0
    ${EndIf}
    ;检查升级程序是否运行,如果正在运行则进行终止
    nsProcess::_FindProcess "${UPDATE_EXE_NAME}"
    Pop $R0
    ${If} $R0 = 0
      DetailPrint "停止正在运行的升级程序"
      nsProcess::_KillProcess "${UPDATE_EXE_NAME}"
      Pop $R0
    ${EndIf}