如何将DART应用程序作为Windows服务运行?

时间:2014-01-10 23:32:00

标签: windows-services dart

我一直在研究在下一个项目中使用DART语言的可能性。在这一点上唯一真正阻碍我的是我无法找到将DART应用程序作为Windows服务运行的方法。我在Google上运行了搜索,并阅读了DART网站上的大部分文档。我发现的大部分信息都与在DART中创建服务器有关,但与Windows服务无关。

有人可以向我指明方向或详细说明必要的步骤吗?

谢谢, 乔恩

2 个答案:

答案 0 :(得分:1)

将Dart用于Windows服务与任何其他可执行文件没有区别;你只需要用正确的参数调用dart.exe。

但是,Windows不支持将任意exes作为Windows服务运行,因为它们需要一些元数据/引导。我对NSSM - the Non-Sucking Service Manager有过很好的体会。在评论中,建议使用SC.exe;但我无法在最新版本的Windows Server上运行它:(

答案 1 :(得分:0)

我真的来晚了,但是我找到了解决此问题的方法,而不必使用第三方应用程序。

我的解决方案有点像黑客,但是,它可以工作。我将dart应用程序编译为可执行文件,然后使用sc.exe create将其注册为Windows Service。 sc.exe create的问题在于,应用程序的主要功能需要执行一些额外的步骤,以通知Windows它正在运行。如果不这样做,则Windows服务将停留在“启动状态”。

我认为没有可用于执行此职责的发布包。但是,我们可以使用两件事:Dart:FFI,以及Mohit Arora的以下文章,其中介绍了如何使用C ++创建Windows服务。 https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus

我抓住了Mohit的代码并进行了大量更改(包括将其反向移植到C,因为... 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();
}


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);

只需确保将WINSERVICE_EXPORTS添加到其中一个定义中,或将其替换为项目中的相应定义。

Dart

我还需要从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);
}