ホームページ バックエンド開発 Golang Go で Windows サービスを作成する

Go で Windows サービスを作成する

Aug 28, 2024 am 06:40 AM

Writing a Windows Service in Go

目次

  • はじめに
  • Windows の「サービス」とは正確には何ですか?
  • なぜ Golang を使うのですか?
  • Go で Windows サービスを作成する
  • サービスのインストールと開始
  • 結論
  • 完全なコード

導入

開発者の皆さん、久しぶりに Windows っぽいものを書きました。そこで、今日は Go で Windows サービス アプリケーションを作成する方法について説明したいと思います。はい、お聞きのとおり、それは Go 言語です。このチュートリアル ブログでは、Windows サービス アプリケーションに関するいくつかの基本事項を説明し、後半では、情報をファイルに記録する Windows サービスのコードを記述する簡単なコード ウォークスルーを説明します。早速、始めましょう...!

Windows の「サービス」とは正確には何ですか?

Windows サービス アプリケーション (別名 Windows サービス) は、バックグラウンドで実行される小さなアプリケーションです。通常の Windows アプリケーションとは異なり、GUI やユーザー インターフェイスはありません。これらのサービス アプリケーションは、コンピュータの起動時に実行を開始します。どのユーザー アカウントで実行されているかに関係なく実行されます。そのライフサイクル (開始、停止、一時停止、続行など) は、サービス コントロール マネージャー (SCM) と呼ばれるプログラムによって制御されます。

このことから、SCM が Windows サービスと対話し、そのライフサイクルを管理するような方法で Windows サービスを作成する必要があることがわかります。

なぜゴランなのか?

Windows サービスの作成に Go を検討する要因はいくつかあります。

同時実行性

Go の同時実行モデルにより、より高速でリソース効率の高い処理が可能になります。 Go のゴルーチンを使用すると、ブロックやデッドロックを発生させずにマルチタスクを実行できるアプリケーションを作成できます。

シンプルさ

従来、Windows サービスは C++ または C (場合によっては C#) を使用して記述されており、コードが複雑になるだけでなく、DX (開発者エクスペリエンス) も低下します。 Go の Windows サービスの実装は簡単で、コードの各行は意味があります

静的バイナリ

「Python のようなもっと単純な言語を使用したらどうだろうか?」と疑問に思われるかもしれません。その理由は、Python の解釈される性質によるものです。 Go は、Windows サービスが効率的に機能するために不可欠な、静的にリンクされた単一ファイル バイナリにコンパイルします。 Go バイナリにはランタイムやインタープリターは必要ありません。 Go コードはクロスコンパイルすることもできます。

低レベルアクセス

Go はガベージ コレクション言語ではありますが、低レベルの要素と対話するための確実なサポートを提供します。 go では win32 API と一般的なシステムコールを簡単に呼び出すことができます。

はい、情報は十分です。コードを書いてみましょう...

Go で Windows サービスを作成する

このコードのチュートリアルは、Go 構文に関する基本的な知識があることを前提としています。そうでない場合は、A Tour of Go がそれを学ぶのに最適な場所です。

  • まず、プロジェクトに名前を付けましょう。鉱山には cosmic/my_service という名前を付けます。 go.mod ファイルを作成します。
PS C:\> go mod init cosmic/my_service
ログイン後にコピー
  • 次に、golang.org/x/sys パッケージをインストールする必要があります。このパッケージは、Windows OS 関連アプリケーションの Go 言語サポートを提供します。
PS C:\> go get golang.org/x/sys
ログイン後にコピー

注: このパッケージには、Mac OS や Linux などの UNIX ベースの OS に対する OS レベルの Go 言語サポートも含まれています。

  • main.go ファイルを作成します。 main.go ファイルには、Go アプリケーション/サービスのエントリポイントとして機能する main 関数が含まれています。

  • サービス インスタンスを作成するには、サービス コンテキスト と呼ばれるものを記述する必要があります。これは golang.org/x/sys/windows/svc のハンドラー インターフェイスを実装します。

インターフェイス定義は次のようになります

type Handler interface {
    Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
}
ログイン後にコピー

実行関数はサービスの開始時にパッケージ コードによって呼び出され、実行が完了するとサービスは終了します。

受信専用チャネル r からサービス変更リクエストを読み取り、それに応じて動作します。また、送信専用チャネルに信号を送信してサービスを更新し続ける必要もあります。オプションの引数を args パラメータに渡すことができます。

終了時に、実行が成功すると exitCode が 0 になって戻ることができます。そのために svcSpecificEC を使用することもできます。

  • 次に、サービス コンテキストとして機能する myService という名前の型を作成します。
type myService struct{}
ログイン後にコピー
  • タイプ myService を作成した後、前述の Execute をメソッドとして追加し、Handler インターフェースを実装します。
func (m *myService) Execute(args []string, r <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {
    // to be filled
}
ログイン後にコピー
  • ハンドラー インターフェイスの実装に成功したので、実際のロジックの作成を開始できます。

サービスが SCM から受け入れることができるシグナルを含む定数を作成します。

const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
ログイン後にコピー

Our main goal is the log some data every 30 seconds. So we need to define a thread safe timer for that.

tick := time.Tick(30 * time.Second)
ログイン後にコピー

So, we have done all the initialization stuffs. It's time to send START signal to the SCM. we're going to do exactly that,

status <- svc.Status{State: svc.StartPending}
status <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
ログイン後にコピー

Now we're going to write a loop which acts as a mainloop for our application. Handling events in loop makes our application never ending and we can break the loop only when the SCM sends STOP or SHUTDOWN signal.

loop:
    for {
        select {
        case <-tick:
            log.Print("Tick Handled...!")
        case c := <-r:
            switch c.Cmd {
            case svc.Interrogate:
                status <- c.CurrentStatus
            case svc.Stop, svc.Shutdown:
                log.Print("Shutting service...!")
                break loop
            case svc.Pause:
                status <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
            case svc.Continue:
                status <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
            default:
                log.Printf("Unexpected service control request #%d", c)
            }
        }
    }
ログイン後にコピー

Here we used a select statement to receive signals from channels. In first case, we handle the Timer's tick signal. This case receives signal every 30 seconds, as we declared before. We log a string "Tick Handled...!" in this case.

Secondly, we handle the signals from SCM via the receive-only r channel. So, we assign the value of the signal from r to a variable c and using a switch statement, we can handle all the lifecycle event/signals of our service. We can see about each lifecycle below,

  1. svc.Interrogate - Signal requested by SCM on a timely fashion to check the current status of the service.
  2. svc.Stop and svc.Shutdown - Signal sent by SCM when our service needs to be stopped or Shut Down.
  3. svc.Pause - Signal sent by SCM to pause the service execution without shutting it down.
  4. svc.Continue - Signal sent by SCM to resume the paused execution state of the service.

So, when on receiving either svc.Stop or svc.Shutdown signal, we break the loop. It is to be noted that we need to send STOP signal to the SCM to let the SCM know that our service is stopping.

status <- svc.Status{State: svc.StopPending}
return false, 1
ログイン後にコピー
  • Now we write a function called runService where we enable our service to run either in Debug mode or in Service Control Mode.

Note: It's super hard to debug Windows Service Applications when running on Service Control Mode. That's why we are writing an additional Debug mode.

func runService(name string, isDebug bool) {
    if isDebug {
        err := debug.Run(name, &myService{})
        if err != nil {
            log.Fatalln("Error running service in debug mode.")
        }
    } else {
        err := svc.Run(name, &myService{})
        if err != nil {
            log.Fatalln("Error running service in debug mode.")
        }
    }
}
ログイン後にコピー
  • Finally we can call the runService function in our main function.
func main() {

    f, err := os.OpenFile("debug.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalln(fmt.Errorf("error opening file: %v", err))
    }
    defer f.Close()

    log.SetOutput(f)
    runService("myservice", false) //change to true to run in debug mode
}
ログイン後にコピー

Note: We are logging the logs to a log file. In advanced scenarios, we log our logs to Windows Event Logger. (phew, that sounds like a tongue twister ?)

  • Now run go build to create a binary '.exe'. Optionally, we can optimize and reduce the binary file size by using the following command,
PS C:\> go build -ldflags "-s -w"
ログイン後にコピー

Installing and Starting the Service

For installing, deleting, starting and stopping our service, we use an inbuilt tool called sc.exe

To install our service, run the following command in powershell as Administrator,

PS C:\> sc.exe create MyService <path to your service_app.exe>
ログイン後にコピー

To start our service, run the following command,

PS C:\> sc.exe start MyService
ログイン後にコピー

To delete our service, run the following command,

PS C:\> sc.exe delete MyService
ログイン後にコピー

You can explore more commands, just type sc.exe without any arguments to see the available commands.

Conclusion

As we can see, implementing Windows Services in go is straightforward and requires minimal implementation. You can write your own windows services which acts as a web server and more. Thanks for reading and don't forget to drop a ❤️.

Complete Code

Here is the complete code for your reference.

// file: main.go

package main

import (
    "fmt"
    "golang.org/x/sys/windows/svc"
    "golang.org/x/sys/windows/svc/debug"
    "log"
    "os"
    "time"
)

type myService struct{}

func (m *myService) Execute(args []string, r <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) {

    const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
    tick := time.Tick(5 * time.Second)

    status <- svc.Status{State: svc.StartPending}

    status <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}

loop:
    for {
        select {
        case <-tick:
            log.Print("Tick Handled...!")
        case c := <-r:
            switch c.Cmd {
            case svc.Interrogate:
                status <- c.CurrentStatus
            case svc.Stop, svc.Shutdown:
                log.Print("Shutting service...!")
                break loop
            case svc.Pause:
                status <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
            case svc.Continue:
                status <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
            default:
                log.Printf("Unexpected service control request #%d", c)
            }
        }
    }

    status <- svc.Status{State: svc.StopPending}
    return false, 1
}

func runService(name string, isDebug bool) {
    if isDebug {
        err := debug.Run(name, &myService{})
        if err != nil {
            log.Fatalln("Error running service in debug mode.")
        }
    } else {
        err := svc.Run(name, &myService{})
        if err != nil {
            log.Fatalln("Error running service in debug mode.")
        }
    }
}

func main() {

    f, err := os.OpenFile("E:/awesomeProject/debug.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalln(fmt.Errorf("error opening file: %v", err))
    }
    defer f.Close()

    log.SetOutput(f)
    runService("myservice", false)
}
ログイン後にコピー

以上がGo で Windows サービスを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Golang vs. Python:パフォーマンスとスケーラビリティ Golang vs. Python:パフォーマンスとスケーラビリティ Apr 19, 2025 am 12:18 AM

Golangは、パフォーマンスとスケーラビリティの点でPythonよりも優れています。 1)Golangのコンピレーションタイプの特性と効率的な並行性モデルにより、高い並行性シナリオでうまく機能します。 2)Pythonは解釈された言語として、ゆっくりと実行されますが、Cythonなどのツールを介してパフォーマンスを最適化できます。

Golang and C:Concurrency vs. Raw Speed Golang and C:Concurrency vs. Raw Speed Apr 21, 2025 am 12:16 AM

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

ゴーを始めましょう:初心者のガイド ゴーを始めましょう:初心者のガイド Apr 26, 2025 am 12:21 AM

goisidealforforbeginnersandsutable forcloudnetworkservicesduetoitssimplicity、andconcurrencyfeatures.1)installgofromtheofficialwebsiteandverify with'goversion'.2)

Golang vs. C:パフォーマンスと速度の比較 Golang vs. C:パフォーマンスと速度の比較 Apr 21, 2025 am 12:13 AM

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。

Golangの影響:速度、効率、シンプルさ Golangの影響:速度、効率、シンプルさ Apr 14, 2025 am 12:11 AM

speed、効率、およびシンプル性をspeedsped.1)speed:gocompilesquilesquicklyandrunseffictient、理想的なlargeprojects.2)効率:等系dribribraryreducesexexternaldedenciess、開発効果を高める3)シンプルさ:

CとGolang:パフォーマンスが重要な場合 CとGolang:パフォーマンスが重要な場合 Apr 13, 2025 am 12:11 AM

Cは、ハードウェアリソースと高性能の最適化が必要なシナリオにより適していますが、Golangは迅速な開発と高い並行性処理が必要なシナリオにより適しています。 1.Cの利点は、ハードウェア特性と高い最適化機能に近いものにあります。これは、ゲーム開発などの高性能ニーズに適しています。 2.Golangの利点は、その簡潔な構文と自然な並行性サポートにあり、これは高い並行性サービス開発に適しています。

Golang vs. Python:重要な違​​いと類似点 Golang vs. Python:重要な違​​いと類似点 Apr 17, 2025 am 12:15 AM

GolangとPythonにはそれぞれ独自の利点があります。Golangは高性能と同時プログラミングに適していますが、PythonはデータサイエンスとWeb開発に適しています。 Golangは同時性モデルと効率的なパフォーマンスで知られていますが、Pythonは簡潔な構文とリッチライブラリエコシステムで知られています。

GolangとC:パフォーマンスのトレードオフ GolangとC:パフォーマンスのトレードオフ Apr 17, 2025 am 12:18 AM

GolangとCのパフォーマンスの違いは、主にメモリ管理、コンピレーションの最適化、ランタイム効率に反映されています。 1)Golangのゴミ収集メカニズムは便利ですが、パフォーマンスに影響を与える可能性があります。

See all articles