Cách tạo trình cài đặt cho .net Windows Service bằng Visual Studio


Câu trả lời:


227

Trong dự án dịch vụ, hãy làm như sau:

  1. Trong trình khám phá giải pháp, nhấp đúp vào tệp .cs dịch vụ của bạn. Nó sẽ hiển thị một màn hình toàn màu xám và nói về việc kéo các thứ từ hộp công cụ.
  2. Sau đó nhấp chuột phải vào vùng màu xám và chọn thêm trình cài đặt. Điều này sẽ thêm một tệp dự án cài đặt vào dự án của bạn.
  3. Sau đó, bạn sẽ có 2 thành phần trên khung nhìn thiết kế của ProjectInstaller.cs (serviceProcessInstaller1 và serviceInstaller1). Sau đó, bạn nên thiết lập các thuộc tính mà bạn cần, chẳng hạn như tên dịch vụ và người dùng mà nó sẽ chạy.

Bây giờ bạn cần phải thực hiện một dự án thiết lập. Điều tốt nhất để làm là sử dụng trình hướng dẫn thiết lập.

  1. Nhấp chuột phải vào giải pháp của bạn và thêm dự án mới: Thêm> Dự án mới> Thiết lập và triển khai dự án> Trình hướng dẫn cài đặt

    a. Điều này có thể thay đổi một chút cho các phiên bản khác nhau của Visual Studio. b. Visual Studio 2010 được đặt trong: Cài đặt mẫu> Các loại dự án khác> Cài đặt và triển khai> Trình cài đặt Visual Studio

  2. Ở bước thứ hai, chọn "Tạo thiết lập cho ứng dụng Windows."

  3. Ở bước thứ 3, chọn "Đầu ra chính từ ..."

  4. Nhấp qua để kết thúc.

Tiếp theo chỉnh sửa trình cài đặt của bạn để đảm bảo đầu ra chính xác được bao gồm.

  1. Nhấp chuột phải vào dự án thiết lập trong Solution Explorer của bạn.
  2. Chọn Xem> Tác vụ tùy chỉnh. (Trong VS2008 có thể là Xem> Trình chỉnh sửa> Tác vụ tùy chỉnh)
  3. Nhấp chuột phải vào hành động Cài đặt trong cây Hành động tùy chỉnh và chọn 'Thêm hành động tùy chỉnh ...'
  4. Trong hộp thoại "Chọn mục trong dự án", chọn Thư mục ứng dụng và bấm OK.
  5. Nhấp OK để chọn tùy chọn "Đầu ra chính từ ...". Một nút mới sẽ được tạo.
  6. Lặp lại các bước 4 - 5 cho các hành động cam kết, khôi phục và gỡ cài đặt.

Bạn có thể chỉnh sửa tên đầu ra của trình cài đặt bằng cách nhấp chuột phải vào dự án Trình cài đặt trong giải pháp của bạn và chọn Thuộc tính. Thay đổi 'Tên tệp đầu ra:' thành bất cứ điều gì bạn muốn. Bằng cách chọn dự án trình cài đặt cũng và nhìn vào cửa sổ thuộc tính, bạn có thể chỉnh sửa Product Name, Title, Manufacturer, vv ...

Tiếp theo xây dựng trình cài đặt của bạn và nó sẽ tạo ra MSI và setup.exe. Chọn bất cứ điều gì bạn muốn sử dụng để triển khai dịch vụ của bạn.


37
@El Ronnoco, tôi đã có câu trả lời từ lâu trước khi đăng. Tôi muốn ghi lại nó ở đây bởi vì tôi luôn phải tìm kiếm nó sau mỗi 6-12 tháng (và nó không dễ tìm thấy) vì vậy bây giờ tôi có thể dễ dàng tìm kiếm cho mọi người và tôi có thể tự tìm thấy nó một cách nhanh chóng :)
Kelsey

1
Thật không may, đó cũng là câu trả lời sai. Vâng, tôi biết bạn sẽ tìm thấy điều này trong sách và MSDN nhưng đó là trường hợp một nhóm trong Microsoft không nói chuyện với một nhóm khác ở Microsoft và đưa ra giải pháp kém hơn cho một vấn đề đã được giải quyết. Xem blog.iswix.com/2006/07/msi-vs-net.html để biết thêm thông tin.
Họa sĩ Christopher

9
@Christopher Họa sĩ Tôi đã sử dụng trình cài đặt MS kể từ2k5 và nó chưa bao giờ có vấn đề. Cho dù bạn có đồng ý với nó hay không và coi đó là một 'phản mẫu' không phải là vấn đề của câu hỏi này, đó là cách tôi làm x với y chứ không phải làm thế nào để tôi làm a với b. Khi tôi đăng câu hỏi đó là cho mục đích tài liệu.
Kelsey

3
Sau đó, bạn đã gặp may mắn trong 6 năm mà bạn không biết điều đó. Bạn có thể muốn đọc: robmensching.com/blog/posts/2007/4/19/ nay
Christopher Họa sĩ

1
Nếu bạn gặp Service name contains invalid characters, is empty, or is too long (max length = 80)lỗi khi thêm Trình cài đặt thì nhấp chuột phải vào vùng màu xám một lần nữa, đi đến Thuộc tính và đảm bảo rằng giá trị Tên Dịch vụ được đặt.
wolfyuk

51

Tôi làm theo các bước đầu tiên của Kelsey để thêm các lớp trình cài đặt vào dự án dịch vụ của mình, nhưng thay vì tạo trình cài đặt MSI hoặc setup.exe, tôi tự cài đặt / gỡ cài đặt dịch vụ. Đây là một chút mã mẫu từ một trong các dịch vụ của tôi mà bạn có thể sử dụng làm điểm bắt đầu.

public static int Main(string[] args)
{
    if (System.Environment.UserInteractive)
    {
        // we only care about the first two characters
        string arg = args[0].ToLowerInvariant().Substring(0, 2);

        switch (arg)
        {
            case "/i":  // install
                return InstallService();

            case "/u":  // uninstall
                return UninstallService();

            default:  // unknown option
                Console.WriteLine("Argument not recognized: {0}", args[0]);
                Console.WriteLine(string.Empty);
                DisplayUsage();
                return 1;
        }
    }
    else
    {
        // run as a standard service as we weren't started by a user
        ServiceBase.Run(new CSMessageQueueService());
    }

    return 0;
}

private static int InstallService()
{
    var service = new MyService();

    try
    {
        // perform specific install steps for our queue service.
        service.InstallService();

        // install the service with the Windows Service Control Manager (SCM)
        ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null && ex.InnerException.GetType() == typeof(Win32Exception))
        {
            Win32Exception wex = (Win32Exception)ex.InnerException;
            Console.WriteLine("Error(0x{0:X}): Service already installed!", wex.ErrorCode);
            return wex.ErrorCode;
        }
        else
        {
            Console.WriteLine(ex.ToString());
            return -1;
        }
    }

    return 0;
}

private static int UninstallService()
{
    var service = new MyQueueService();

    try
    {
        // perform specific uninstall steps for our queue service
        service.UninstallService();

        // uninstall the service from the Windows Service Control Manager (SCM)
        ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
    }
    catch (Exception ex)
    {
        if (ex.InnerException.GetType() == typeof(Win32Exception))
        {
            Win32Exception wex = (Win32Exception)ex.InnerException;
            Console.WriteLine("Error(0x{0:X}): Service not installed!", wex.ErrorCode);
            return wex.ErrorCode;
        }
        else
        {
            Console.WriteLine(ex.ToString());
            return -1;
        }
    }

    return 0;
}

1
Vì tò mò, lợi ích của việc có một dịch vụ tự cài đặt / không cài đặt là gì? Nếu dịch vụ tự cài đặt, làm thế nào để bạn bắt đầu dịch vụ trước để có thể cài đặt dịch vụ ngay từ đầu? Nếu có một cơ chế để bắt đầu dịch vụ mà không cài đặt nó, tại sao lại phải cài đặt nó?
Kiley Naro

3
@Christopher - Tôi không. Giải pháp của tôi không phải là sự thay thế cho trình cài đặt đầy đủ mà bạn sẽ sử dụng để phân phối phần mềm. Tôi đang trình bày một tùy chọn khác hoạt động trong một số tình huống, chẳng hạn như tôi viết phần mềm điều khiển các PC nhúng trong các ki-ốt không giám sát.

4
Khi bạn cài đặt nó trên một máy sản xuất, hãy nhớ chạy nó với tư cách quản trị viên. Tôi đã tạo một tệp BAT gọi tệp EXE bằng tham số / i nhưng nó không hoạt động trên môi trường sản xuất, mặc dù tôi đã thực thi tệp BAT với tư cách quản trị viên. Tôi đã phải mở một dấu nhắc dòng lệnh với tư cách quản trị viên và gọi tệp EXE / i một cách rõ ràng (không sử dụng tệp BAT). Ít nhất điều đó đã xảy ra với tôi trên Windows Server 2012.
Francisco Goldenstein

1
RE: Không có đầu ra trên dòng lệnh. Sử dụng Cộng đồng VS 2017, dự án dịch vụ mới của tôi được mặc định là loại Đầu ra: Windows Applicationvà đối tượng Khởi động : (none). Tôi đã phải thay đổi loại đầu ra thành Console Applicationvà đặt đối tượng khởi động của mình, vd myservice.Program. Nếu có thể có sự phân nhánh mà tôi không biết, xin vui lòng tư vấn.
Jonathan

1
Mã ví dụ có lỗi chính tả không? Tại sao có ba dịch vụ khác nhau (CSMessageQueueService, MyService, MyQueueService)?
Nils Guillermin

27

Các giải pháp của Nor Kelsey cũng như Brendan đều không phù hợp với tôi trong Cộng đồng Visual Studio 2015.

Dưới đây là các bước ngắn gọn của tôi về cách tạo dịch vụ với trình cài đặt:

  1. Chạy Visual Studio, đi đến File->New->Project
  2. Chọn .NET Framework 4, trong 'Tìm kiếm mẫu đã cài đặt' gõ 'Dịch vụ'
  3. Chọn 'Dịch vụ Windows'. Nhập tên và địa điểm. Nhấn OK.
  4. Nhấp đúp chuột vào Service1.cs, nhấp chuột phải vào trình thiết kế và chọn 'Thêm trình cài đặt'
  5. Nhấp đúp chuột vào ProjectInstaller.cs. Đối với serviceProcessInstaller1 mở tab Thuộc tính và thay đổi giá trị thuộc tính 'Tài khoản' thành 'LocalService'. Đối với serviceInstaller1 thay đổi 'ServiceName' và đặt 'StartType' thành 'Automatic'.
  6. Nhấp đúp chuột vào dịch vụInstaller1. Visual Studio tạo serviceInstaller1_AfterInstallsự kiện. Viết mã:

    private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
    {
        using (System.ServiceProcess.ServiceController sc = new 
        System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName))
        {
            sc.Start();
        }
    }
  7. Xây dựng giải pháp. Nhấp chuột phải vào dự án và chọn 'Mở thư mục trong File Explorer'. Chuyển đến bin \ Gỡ lỗi .

  8. Tạo install.bat với tập lệnh bên dưới:

    :::::::::::::::::::::::::::::::::::::::::
    :: Automatically check & get admin rights
    :::::::::::::::::::::::::::::::::::::::::
    @echo off
    CLS 
    ECHO.
    ECHO =============================
    ECHO Running Admin shell
    ECHO =============================
    
    :checkPrivileges 
    NET FILE 1>NUL 2>NUL
    if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges ) 
    
    :getPrivileges 
    if '%1'=='ELEV' (shift & goto gotPrivileges)  
    ECHO. 
    ECHO **************************************
    ECHO Invoking UAC for Privilege Escalation 
    ECHO **************************************
    
    setlocal DisableDelayedExpansion
    set "batchPath=%~0"
    setlocal EnableDelayedExpansion
    ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs" 
    ECHO UAC.ShellExecute "!batchPath!", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs" 
    "%temp%\OEgetPrivileges.vbs" 
    exit /B 
    
    :gotPrivileges 
    ::::::::::::::::::::::::::::
    :START
    ::::::::::::::::::::::::::::
    setlocal & pushd .
    
    cd /d %~dp0
    %windir%\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i "WindowsService1.exe"
    pause
  9. Tạo tệp Uninstall.bat (thay đổi dòng pen-ult /ithành /u)
  10. Để cài đặt và bắt đầu dịch vụ chạy install.bat, để dừng và gỡ cài đặt, hãy gỡ cài đặt.bat

14

Đối với VS2017, bạn sẽ cần thêm tiện ích mở rộng VS "Microsoft Visual Studio 2017 Installer Project". Điều này sẽ cung cấp cho bạn các mẫu dự án Visual Studio Installer bổ sung. https://marketplace.visualstudio.com/items?itemName=VisualStudio ProducttTeam.MicrosoftVisualStudio2017InstallerProjects#overview

Để cài đặt dịch vụ windows, bạn có thể thêm dự án loại trình hướng dẫn thiết lập mới và làm theo các bước từ câu trả lời của Kelsey https://stackoverflow.com/a/9021107/1040040


1

Các lớp InstallUtil (ServiceInstaller) được cộng đồng Windows Installer coi là một kiểu chống mẫu. Đó là một sự mong manh, không theo quy trình, phát minh lại bánh xe mà bỏ qua thực tế là Windows Installer có hỗ trợ tích hợp cho Dịch vụ.

Các dự án triển khai Visual Studio (cũng không được đánh giá cao và không dùng nữa trong phiên bản tiếp theo của Visual Studio) không có hỗ trợ riêng cho các dịch vụ. Nhưng họ có thể tiêu thụ các mô-đun hợp nhất. Vì vậy, tôi sẽ xem bài viết trên blog này để hiểu cách tạo mô-đun hợp nhất bằng Windows Installer XML có thể thể hiện dịch vụ và sau đó sử dụng mô-đun hợp nhất đó trong giải pháp VDPROJ của bạn.

Tăng cường InstallShield bằng Windows Installer XML - Windows Services

Hướng dẫn dịch vụ Windows IsWiX

Video dịch vụ Windows của IsWiX


1
Trong Visual Studio cũ đã có một dự án triển khai, với trình cài đặt tạo dễ dàng. Bây giờ tôi phải mua thành phần phần mềm của bên thứ ba?
Alexey Obukhov

@AlexeyObukhov Bạn có thể sử dụng Wix miễn phí, đó là những gì bản thân VS sử dụng, nhưng vấn đề với Wix cũng giống như vấn đề với Git - đường cong học tập gần dọc.
Alan B
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.