none
Access Kinect v2 from a Windows Service

    Question

  • I am currently working on a project where I manage the Kinect v2 device from a background component.

    Everything works fine as long as it is running in a console application, but once started as a Windows Service (user account, Local System or Local Service) the device is able to initialize (IsOpen = true) but does not send any frames (IsAvailable = false).

    Does anyone have any experience with the v2 device running from a Windows Service? The v1 devices worked flawlessly.

    Is there a known issue with this configuration?

    Thanks in advance

    -Simon

    Edit: i have uploaded a runnable mini-project to: http://skyseract.com/KinectTestService.zip

    When built and extracted to "C:\New folder":

    To install as a windows service: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe "C:\New folder\TestService.exe"

    To uninstall as a windows service: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /u "C:\New folder\TestService.exe"



    Sunday, August 03, 2014 6:28 PM

Answers

  • Unfortunately, we do not support using Kinect v2 APIs from a Windows service. It is not explicitly blocked, and there are cases where it will seem to work for a while, but it isn't supported and won't work reliably.

    Sorry we couldn't be of more help.

    The Kinect Team


    Jesse Kaplan [msft]

    Monday, August 04, 2014 8:47 PM

All replies

  • Unfortunately, we do not support using Kinect v2 APIs from a Windows service. It is not explicitly blocked, and there are cases where it will seem to work for a while, but it isn't supported and won't work reliably.

    Sorry we couldn't be of more help.

    The Kinect Team


    Jesse Kaplan [msft]

    Monday, August 04, 2014 8:47 PM
  • Hi Jesse,

    thank you for your answer.

    Are there any plans to get it working using a Windows Service in a later release?

    I think the use case where the Kinect is invoked in background service is not too uncommon, especially when used on smaller devices like vehicles or in embedded scenarios (like mine).

    Thanks

    -Simon

    Tuesday, August 05, 2014 10:39 AM
  • Thanks, we understand the scenario but a +1 is always helpful. We don't have any plans to announce at this time. Hopefully you can use a normal app (maybe a console app?) to enable your scenario; others in similar situations have built a basic service app that controls the normal app to gain some of the process lifetime semantics you get with a service.

    Thanks,

    Jesse


    Jesse Kaplan [msft]

    Tuesday, August 05, 2014 2:44 PM
  • My current workaround is to host a WPF application that starts when a certain user logs in to the computer. This application then provides a WCF interface on the local loopback adapter.

    The main service then connects to this local "service" which does not run in session 0. The WPF "service" then initializes the Kinect and dispatches messages to and from the device via a net.tcp binding.

    It's kind of a hack and unfortunately all the systems now need a logged in user to connect to the Kinect device but it does work at least.

    I hope you guys still realize a direct connection from a Windows service.

    Keep up the great work; cant wait for the next SDK update!

    Sunday, August 17, 2014 11:31 AM
  • For me, launching KinectService.exe from a Windows Service (session 0) running under the LocalSystem account just makes my service work...

    Here is the code:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Text;
    
    namespace MyService
    {
        class KinectServiceLoader
        {
            #region Structures
    
            [StructLayout(LayoutKind.Sequential)]
            private struct SECURITY_ATTRIBUTES
            {
                public int Length;
                public IntPtr lpSecurityDescriptor;
                public bool bInheritHandle;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            private struct STARTUPINFO
            {
                public int cb;
                public String lpReserved;
                public String lpDesktop;
                public String lpTitle;
                public uint dwX;
                public uint dwY;
                public uint dwXSize;
                public uint dwYSize;
                public uint dwXCountChars;
                public uint dwYCountChars;
                public uint dwFillAttribute;
                public uint dwFlags;
                public short wShowWindow;
                public short cbReserved2;
                public IntPtr lpReserved2;
                public IntPtr hStdInput;
                public IntPtr hStdOutput;
                public IntPtr hStdError;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct PROCESS_INFORMATION
            {
                public IntPtr hProcess;
                public IntPtr hThread;
                public uint dwProcessId;
                public uint dwThreadId;
            }
    
            #endregion
    
            #region Enumerations
    
            private enum TOKEN_TYPE : int
            {
                TokenPrimary = 1,
                TokenImpersonation = 2
            }
    
            private enum SECURITY_IMPERSONATION_LEVEL : int
            {
                SecurityAnonymous = 0,
                SecurityIdentification = 1,
                SecurityImpersonation = 2,
                SecurityDelegation = 3,
            }
    
            #endregion
    
            #region Constants
    
            private const int TOKEN_DUPLICATE = 0x0002;
            private const uint MAXIMUM_ALLOWED = 0x2000000;
    
            private const int NORMAL_PRIORITY_CLASS = 0x20;
    
            #endregion
    
            #region Win32 API Imports
    
            [DllImport("kernel32.dll")]
            private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
    
            [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
            private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
    
            [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
            private extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
                ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
                int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
    
            [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true)]
            private extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
                ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
                String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            private static extern bool CloseHandle(IntPtr hSnapshot);
    
            #endregion
    
            public static bool Go(out PROCESS_INFORMATION procInfo)
            {
                string applicationName = @"C:\Windows\System32\Kinect\KinectService.exe";
    
                procInfo = new PROCESS_INFORMATION();
    
                // obtain a handle to the current process
                IntPtr hProcess = IntPtr.Zero;
                hProcess = OpenProcess(MAXIMUM_ALLOWED, false, Process.GetCurrentProcess().Id);
    
                // obtain a handle to the access token of the current process
                IntPtr hPToken = IntPtr.Zero;
                if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
                {
                    CloseHandle(hProcess);
                    return false;
                }
    
                SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                sa.Length = Marshal.SizeOf(sa);
    
                // copy the access token of the current process; the newly created token will be a primary token
                IntPtr hUserTokenDup = IntPtr.Zero;
                if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
                {
                    CloseHandle(hPToken);
                    CloseHandle(hProcess);
                    return false;
                }
    
                STARTUPINFO si = new STARTUPINFO();
                si.cb = (int)Marshal.SizeOf(si);
                si.lpDesktop = @"";
    
                // Flags that specify the priority and creation method of the process
                int dwCreationFlags = NORMAL_PRIORITY_CLASS;
    
                // Freate a new process
                bool result = CreateProcessAsUser(hUserTokenDup,        // client's access token
                                                null,                   // file to execute
                                                applicationName,        // command line
                                                ref sa,                 // pointer to process SECURITY_ATTRIBUTES
                                                ref sa,                 // pointer to thread SECURITY_ATTRIBUTES
                                                false,                  // handles are not inheritable
                                                dwCreationFlags,        // creation flags
                                                IntPtr.Zero,            // pointer to new environment block 
                                                null,                   // name of current directory 
                                                ref si,                 // pointer to STARTUPINFO structure
                                                out procInfo            // receives information about new process
                                                );
    
                // Close handles
                CloseHandle(hUserTokenDup);
                CloseHandle(hPToken);
                CloseHandle(hProcess);
    
                return result;
            }
        }
    }

    Usage:

    • Disable the "Kinect Monitor" service
    • Call KinectServiceLoader.Go() in the OnStart() override of your service

    Configuration: Lenovo ThinkCentre M93p, Intel Core i7, integrated Intel HD4600 GPU, Windows 8.1 Pro, x64.

    Notes: CreateProcess was not enough, I had to go through the following route:

    • OpenProcess() to get the service process handle
    • OpenProcessToken() to get the service access token handle
    • DuplicateTokenEx() to create a new access token
    • CreateProcessAsUser() to finally launch KinectService.exe (specifying lpDesktop is important)

    Interestingly, KinectService consumes GPU resources in session 0... which seems contrary to the session 0 isolation principles... Maybe a "feature" available for integrated GPU???

    Launching KinectServices multiple times this way is harmless since subsequent KinectService instances close themselves when they detect an already running instance.


    Friday, October 17, 2014 6:33 PM
  • Hello Jesse, are there other types of applications where the Kinect V2 for Windows would not be available/accessible? I have been working on a web application to show information regarding the frames received from the Kinect sensor on the web server.. it works when debugging in VS 2013.. But not when I use Local IIS.. Thanks for your time!
    Monday, November 17, 2014 12:16 PM
  • Hi the link to your sample seems to be inactive.

    Would be able to post it again.

    Thanks!


    ashley reddy

    Friday, May 29, 2015 7:01 PM