jbo*_*ard 6 windows-services dart
我一直在研究在下一个项目中使用 DART 语言的可能性。此时唯一真正阻碍我的是我无法找到将 DART 应用程序作为 Windows 服务运行的方法。我在 Google 上进行了搜索,并阅读了 DART 网站上的大部分文档。我找到的大部分信息都与在 DART 中创建服务器有关,但没有与 Windows 服务相关的信息。
有人可以为我指明方向或详细说明这样做所需的步骤吗?
谢谢,乔恩
===最新更新===
我最初的答案包括使用 C 和 Dart FFI 来引导 Windows 服务。然而,这些都不是真正需要的,因为通过将 Docker 与 Windows 容器结合使用可以得到一个非常非常简单的解决方案。
另一种方法是将应用程序编译为可执行的 Windows 控制台应用程序,创建一个 Docker 文件和一个包含该应用程序的 Windows Docker 映像,而不是实际将应用程序作为 Windows 服务运行。在服务器上,您将需要 docker,并且只需使用 --restart 选项运行映像即可。为了测试这一点,Windows 10 支持带有 Windows 容器的 Docker。
因此,简单的解决方案是,我们实际上不需要将 Dart 代码作为 Windows 服务运行,因为我们可以将其作为服务器上的 docker 容器运行。
===原答案===
我进入游戏的时间确实很晚,但我找到了解决这个问题的方法,而无需使用第三方应用程序。
我的解决方案有点像黑客,但是嘿,它有效。我将 dart 应用程序编译为可执行文件,然后使用 sc.exe create 将其注册为 Windows 服务。sc.exe create 的问题在于应用程序的主要功能需要执行一些额外的步骤来通知 Windows 它正在运行。如果不这样做,Windows 服务将陷入“启动状态”。
我认为没有可用的酒吧套餐来履行这项职责。但是,我们可以使用 2 个东西:Dart:FFI,以及 Mohit Arora 的以下文章,该文章解释了如何使用 C++ 创建 Windows 服务。https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus
我抓住了 Mohit 的代码并进行了大量更改(包括将其向后移植到 C,因为……C++)
以下是 Service.c 文件的完整代码:
// Provides an API for Dart console applications to
// integrate themselves as Windows Services
// The entry point to this API is the Init(...)
// function at the bottom of this file.
// The Init(...) function registers the ServiceMain(...)
// function as the actual windows service function.
// the ServiceMain function does the following:
//
// 1. Registers the ServiceCtrlHandler(...) function
// as the service control handler, which is essentially
// tasked to handle control requests (in this case we
// are only handling the request to stop the service).
//
// 2. Creates an event object that and then waits indefinitely
// for the event to be set.
//
// The ServiceCtrlHandler(...) function responds to a
// close request by setting the event created by the
// ServiceMain(...) function, essentially freeing
// the latter from the indefinite wait and terminating
// it.
// The functions in this file don't actually
// do any work, but keep the Windows Service
// alive. The work be initiated by the calling
// application either before or after the call to Init(...).
// Because this was developed for the purpose
// of enabling Dart applications to run as
// Windows Services, it it the Dart Application
// that needs to call Init(...) using Dart FFI.
// It must also be the Dart Application to
// spawn an isolate that does the actual work
// before the call to Init(...)
#include <Windows.h>
#include <tchar.h>
#include "service.h"
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
LPWSTR w_service_name;
void UpdateStatus(
DWORD newState,
DWORD checkPoint,
DWORD exitCode,
DWORD controlsAccepted)
{
g_ServiceStatus.dwControlsAccepted = controlsAccepted;
g_ServiceStatus.dwCurrentState = newState;
g_ServiceStatus.dwWin32ExitCode = exitCode;
g_ServiceStatus.dwCheckPoint = checkPoint;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
}
// Responds to control events. This implementation is
// only responding to the SERVICE_CONTROL_STOP event
// This method signals the ServiceMain function
// that it can stop waiting before terminating.
void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
if (CtrlCode != SERVICE_CONTROL_STOP || g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
return;
UpdateStatus(SERVICE_STOP_PENDING, 4, 0, 0);
SetEvent(g_ServiceStopEvent);
}
void InitServiceStatus()
{
ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
UpdateStatus(SERVICE_START_PENDING, 0, 0, 0);
}
// This function essentially creates an event object
// and enters a holding pattern until that event object
// is set by the ServiceCtrlHandler(...) in response
// to a close request.
// The function doesn't actually do any work,
// except to keep the Windows Service alive.
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
g_StatusHandle = RegisterServiceCtrlHandler(w_service_name, ServiceCtrlHandler);
if (g_StatusHandle == NULL)
return;
InitServiceStatus();
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_ServiceStopEvent == NULL)
{
UpdateStatus(SERVICE_STOPPED, 1, GetLastError(), 0);
return;
}
UpdateStatus(SERVICE_RUNNING, 0, 0, SERVICE_ACCEPT_STOP);
while (WaitForSingleObject(g_ServiceStopEvent, INFINITE) != WAIT_OBJECT_0)
;
CloseHandle(g_ServiceStopEvent);
UpdateStatus(SERVICE_STOPPED, 3, 0, 0);
}
LPWSTR get_service_name(const char* service_name)
{
int max_count = strlen(service_name);
int size = max_count + 1;
LPWSTR ret = malloc(sizeof(wchar_t) * size);
size_t outSize;
mbstowcs_s(&outSize, ret, size, service_name, max_count);
return ret;
}
/// This is the entry point that should be called
/// by the Dart application (or any application
/// of a similar kind of platform) in order to
/// integrate itself as a Windows Service.
/// It registers the ServiceMain(...) function
/// as the service main function. Please consult
/// the comments at that function to understand
/// what it does.
int init(const char* service_name)
{
w_service_name = get_service_name(service_name);
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{w_service_name, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
return GetLastError();
}
Run Code Online (Sandbox Code Playgroud)
Service.h 头文件自然要小得多:
#pragma once
#ifdef WINSERVICE_EXPORTS
#define WINSERVICE_API __declspec(dllexport)
#else
#define WINSERVICE_API __declspec(dllimport)
#endif
WINSERVICE_API int init(const char* service_name);
Run Code Online (Sandbox Code Playgroud)
只需确保将 WINSERVICE_EXPORTS 添加到其中一个定义或将其替换为项目中相应的定义即可。
我还需要从 Dart 方面进行一些更改。这是我的原型:
import 'dart:ffi' as ffi;
import 'dart:io';
import 'dart:isolate';
import 'package:ffi/ffi.dart';
import 'package:grpc/grpc.dart' as grpc;
// These two types represent the
// Init(...) function of the C API
typedef init_func = ffi.Int32 Function(ffi.Pointer<Utf8>);
typedef Init = int Function(ffi.Pointer<Utf8>);
// Entry point to the Dart application.
// When run as a Windows Service,
// this is still the entry point.
// This code is not embeded but is started
// as a regular console application.
void main() async {
final init = createInit();
// Starts the actual work in a separate Isolate
await Isolate.spawn(run, 'message');
final serviceName = Utf8.toUtf8('MProto_Server_from_Dart');
// calls the Init(...) function
var result = init(serviceName);
if (result != 0) return;
// blocks this Isolate indefinitely from continuing
while (true) {
sleep(Duration(days: 365));
}
}
// Creates the instance of the proxy to the Init(...)
// function.
Init createInit() {
final path =
r'[PATH to the C compiled DLL]';
final dylib = ffi.DynamicLibrary.open(path);
// ignore: omit_local_variable_types
final Init init =
dylib.lookup<ffi.NativeFunction<init_func>>('init').asFunction();
return init;
}
// Performs the actual work that needs to
// be done, in this case, we are hosting
// a gRPC service, but this should
// work with any other kind of
// payload, namely other types of
// http services.
void run(String message) async {
print('inside isolate');
var server = grpc.Server(
[
// my service classes
],
);
await server.serve(port: 5001);
}
Run Code Online (Sandbox Code Playgroud)
将 Dart 用于 Windows 服务与任何其他可执行文件没有区别;您只需要使用正确的参数调用 dart.exe 即可。
但是,Windows 不支持将任意 exe 作为 Windows 服务运行,因为它们需要一些元数据/引导。我在NSSM(Non-Sucking Service Manager)方面获得了很好的体验。评论中建议使用SC.exe;但我无法让它在最新版本的 Windows Server 上运行:(
| 归档时间: |
|
| 查看次数: |
2338 次 |
| 最近记录: |