1

I am trying to write an application that it will run as a Windows Service. For this, I have used the node-windows package. The application will print a simple message every second when started (at least for now) - used tasktimer for defying the timer. The problem that I found was that when the service is not installed, it will attempt to start the service and then install it (it works asynchronous). So I've tried to solve this problem using promisify (to be able to use async - await, but now it gives me the error: TypeError: Cannot read property 'svc' of undefined...

This is the class that creates the service object and declares the methods:

import * as pEvent from 'p-event'
var Service = require('node-windows').Service;

export class winServ {
    svc = new Service({
        name: 'Cli app',
        description: 'Just a cli app...',
        script: './src/index.js'
    });

    async alreadyInstalled() {
        //check if service was already installed - return 0 or 1
        this.svc.install();

        await pEvent(this.svc, 'alreadyinstalled')
        .then( () => {
            console.log('Already installed!');
            return 1;
        });

        return 0;
    }

    async installWinServ() {
        this.svc.install();

        await pEvent(this.svc, 'install')
        .then( () => {
            console.log('Service now installed!');
        });
    }

    async uninstallWinServ() {
        this.svc.uninstall();

        // await pEvent(this.svc, 'alreadyuninstalled')
        // .then( () => {
        //  console.log('Already uninstalled!');
        //  return;
        // });

        await pEvent(this.svc, 'uninstall')
        .then( () => {
            console.log('Uninstall complete.');
            console.log('The service exists: ', this.svc.exists);
        })
    }

    async runWinServ() {
        console.log('Service has started!');
        await this.svc.start();
    }

    async stopWinServ() {
        await this.svc.stop();
    }
}

And this is the index.ts file in which I call the class and try to do the logic: 1. run the install method (if it's already installed, just print a message and return; if not, install the service) 2. run the app (it will run infinitely)

import { winServ } from './windowsService/winServ';

var main = async () => {
    try {
            let serv = new winServ();

            //let alreadyInstalled = await serv.alreadyInstalled();
            //if (alreadyInstalled == 0) {
                await serv.installWinServ();
            //}

            await serv.runWinServ();

            let timer = new TaskTimer();

            // define a timer that will run infinitely
            timer.add(() => {
               console.log("abcd");
               timer.interval = 1000;
            });

            // start the timer
            timer.start();
            }
    }
    catch (err) {
        console.log(err);
    }
}

main();

UPDATE: Ok, now I skipped the (un)install check, and it throws me the error:

TypeError: Cannot read property 'send' of null
    at process.killkid (C:\Programs\node_modules\node-windows\lib\wrapper.js:177:11)

This makes the service to stop right after it's started. Any thoughts? :)

user3063909
  • 363
  • 3
  • 16

1 Answers1

2

util.promisify(serv.installWinServ) isn't bound to serv and cannot access correct this context.

The promisification of async functions is a mistake. installWinServ, etc. already return a promise. They don't use promises correctly because this.svc.on doesn't return a promise and cannot be awaited. Event listener needs to be promisified, e.g. with p-event:

await pEvent(this.svc, 'alreadyinstalled');

Or events.once (Node 11 or higher):

await once(this.svc, 'alreadyinstalled');
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    We've added an event emitter promises API into core: https://nodejs.org/api/events.html#events_events_once_emitter_name – Benjamin Gruenbaum May 06 '19 at 07:45
  • @BenjaminGruenbaum Didn't know this, thanks, great news. – Estus Flask May 06 '19 at 07:47
  • @estus updated the code based on your suggestions, but right now it doesn't do anything (installing/running/printing any message). It passes the compilation successfully and the it closes... (no `console.log` message is printed) – user3063909 May 06 '19 at 08:26
  • @user3063909 The problem is specific to node-windows and not promise issues, I cannot assist with it. I assume that this may be because you await for installation events before running `svc.install()`. This is chicken/egg problem. Try to do `svc.install()` first. Also you don't need to use `.then` and `.catch` everywhere, this defies the purpose of async/await. Excessive catching may affect how it works in negative way, one try-catch in `main` is likely enough. – Estus Flask May 06 '19 at 08:55
  • @estus I've managed to make it working in order (I mean it installs it correctly first and then it tries to open it). The problem is that the process closes as soon as it starts (and then saw the error above in the logger output)... Any thoughts? – user3063909 May 06 '19 at 12:32
  • @user3063909 I'm not sure what happens at this point. This is very specific to how node-windows works. I'm currently unable to help debugging it, only with issues that are specific to Node in general. Btw, there's still a problem with stopWinServ, stop doesn't return a promise to await, too. You likely need to await for `pEvent(svc, 'stop')` event there. – Estus Flask May 06 '19 at 13:04