Fixed NotifyServiceStatusChange code and unit test

pull/83/head
David Hall 2019-08-17 19:26:09 -06:00
parent fb62105f42
commit 692524a94a
2 changed files with 47 additions and 63 deletions

View File

@ -82,9 +82,9 @@ namespace Vanara.PInvoke
/// <summary>Callback function used in <see cref="SERVICE_NOTIFY_2"/> to alert changes registered by <see cref="NotifyServiceStatusChange"/>.</summary>
/// <param name="pParameter">A pointer to the SERVICE_NOTIFY structure provided by the caller.</param>
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
[PInvokeData("winsvc.h", MSDNShortId = "52ede72e-eb50-48e2-b5c1-125816f6fe57")]
public delegate void PFN_SC_NOTIFY_CALLBACK(ref SERVICE_NOTIFY_2 pParameter);
public delegate void PFN_SC_NOTIFY_CALLBACK(in SERVICE_NOTIFY_2 pParameter);
/// <summary>
/// <para>The entry point for a service.</para>
@ -2696,7 +2696,7 @@ namespace Vanara.PInvoke
// SC_HANDLE hService, DWORD dwNotifyMask, PSERVICE_NOTIFYA pNotifyBuffer );
[DllImport(Lib.AdvApi32, SetLastError = false, CharSet = CharSet.Auto)]
[PInvokeData("winsvc.h", MSDNShortId = "e22b7f69-f096-486f-97fa-0465bef499cd")]
public static extern Win32Error NotifyServiceStatusChange(SC_HANDLE hService, SERVICE_NOTIFY_FLAGS dwNotifyMask, ref SERVICE_NOTIFY_2 pNotifyBuffer);
public static extern Win32Error NotifyServiceStatusChange(SC_HANDLE hService, SERVICE_NOTIFY_FLAGS dwNotifyMask, in SERVICE_NOTIFY_2 pNotifyBuffer);
/// <summary>
/// <para>
@ -4795,8 +4795,7 @@ namespace Vanara.PInvoke
/// </para>
/// <para>If this member is valid, the notification callback function must free the string using the LocalFree function.</para>
/// </summary>
[MarshalAs(UnmanagedType.LPTStr)]
public string pszServiceNames;
public StrPtrAuto pszServiceNames;
}
/// <summary>
@ -4957,7 +4956,7 @@ namespace Vanara.PInvoke
// dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD
// dwCheckPoint; DWORD dwWaitHint; } SERVICE_STATUS, *LPSERVICE_STATUS;
[PInvokeData("winsvc.h", MSDNShortId = "d268609b-d442-4d0f-9d49-ed23fee84961")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
/// <summary>
@ -5480,7 +5479,7 @@ namespace Vanara.PInvoke
/// is running and on normal termination.
/// </para>
/// </summary>
public uint dwWin32ExitCode;
public Win32Error dwWin32ExitCode;
/// <summary>
/// <para>

View File

@ -1,7 +1,6 @@
using NUnit.Framework;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using static Vanara.PInvoke.AdvApi32;
@ -20,9 +19,9 @@ namespace Vanara.PInvoke.Tests
public void _Setup()
{
hSvcMgr = OpenSCManager(null, null, ScManagerAccessTypes.SC_MANAGER_ALL_ACCESS);
AssertHandleIsValid(hSvcMgr);
Assert.That(hSvcMgr, ResultIs.ValidHandle);
hSvc = OpenService(hSvcMgr, svcName, ServiceAccessTypes.SERVICE_ALL_ACCESS);
AssertHandleIsValid(hSvc);
Assert.That(hSvc, ResultIs.ValidHandle);
}
[OneTimeTearDown]
@ -83,53 +82,49 @@ namespace Vanara.PInvoke.Tests
{
var sb = new StringBuilder(1024, 1024);
var sz = (uint)sb.Capacity;
var ret = GetServiceKeyName(hSvcMgr, svcKey, sb, ref sz);
TestContext.WriteLine(ret ? sb.ToString() : $"Error: {Win32Error.GetLastError()}");
Assert.That(ret, Is.True);
Assert.That(GetServiceKeyName(hSvcMgr, svcKey, sb, ref sz), ResultIs.Successful);
TestContext.WriteLine(sb);
Assert.That(sb.ToString(), Is.EqualTo(svcName));
}
[Test]
public void NotifyServiceStatusChangeTest()
{
var cnt = 0;
Thread.BeginThreadAffinity();
var callback = new PFN_SC_NOTIFY_CALLBACK(ChangeDelegate);
GC.KeepAlive(callback);
var svcNotify = new SERVICE_NOTIFY_2
{
dwVersion = 2,
pfnNotifyCallback = ChangeDelegate
pfnNotifyCallback = callback
};
Thread.BeginThreadAffinity();
var ret = NotifyServiceStatusChange(hSvc, SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_PAUSE_PENDING | SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_CONTINUE_PENDING, ref svcNotify);
if (ret.Failed) TestContext.WriteLine(ret);
Assert.That(ret.Succeeded, Is.True);
new Thread(ThreadExec).Start();
Kernel32.SleepEx(10000, true);
GC.KeepAlive(svcNotify);
Assert.That(NotifyServiceStatusChange(hSvc, SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_PAUSED | SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_PAUSE_PENDING | SERVICE_NOTIFY_FLAGS.SERVICE_NOTIFY_CONTINUE_PENDING, svcNotify), ResultIs.Successful);
var th = new Thread(ThreadExec);
th.Start((SC_HANDLE)hSvc);
while (th.IsAlive)
Kernel32.SleepEx(100, true);
Thread.EndThreadAffinity();
Assert.That(cnt, Is.EqualTo(3));
void ChangeDelegate(ref SERVICE_NOTIFY_2 pParameter)
void ChangeDelegate(in SERVICE_NOTIFY_2 pParameter)
{
TestContext.WriteLine(pParameter.ServiceStatus.dwCurrentState);
cnt++;
System.Diagnostics.Debug.WriteLine(pParameter.ServiceStatus.dwCurrentState);
}
void ThreadExec()
void ThreadExec(object handle)
{
using (var mgr = OpenSCManager(null, null, ScManagerAccessTypes.SC_MANAGER_ALL_ACCESS))
{
if (!mgr.IsInvalid)
{
using (var svc = OpenService(hSvcMgr, svcName, ServiceAccessTypes.SERVICE_ALL_ACCESS))
{
if (!svc.IsInvalid)
{
TestContext.WriteLine("Pausing...");
ControlService(svc, ServiceControl.SERVICE_CONTROL_PAUSE, out _);
Thread.Sleep(3000);
TestContext.WriteLine("Continuing...");
ControlService(svc, ServiceControl.SERVICE_CONTROL_CONTINUE, out _);
Thread.Sleep(3000);
}
}
}
}
var svc = (SC_HANDLE)handle;
System.Diagnostics.Debug.WriteLine("Pausing...");
if (!ControlService(svc, ServiceControl.SERVICE_CONTROL_PAUSE, out _))
System.Diagnostics.Debug.WriteLine($"Pausing failed: {Win32Error.GetLastError()}");
WaitForServiceStatus(svc, ServiceState.SERVICE_PAUSED);
System.Diagnostics.Debug.WriteLine("Continuing...");
if (!ControlService(svc, ServiceControl.SERVICE_CONTROL_CONTINUE, out _))
System.Diagnostics.Debug.WriteLine($"Pausing failed: {Win32Error.GetLastError()}");
WaitForServiceStatus(svc, ServiceState.SERVICE_RUNNING);
}
}
@ -138,7 +133,7 @@ namespace Vanara.PInvoke.Tests
{
using (var scm = OpenSCManager(null, null, ScManagerAccessTypes.SC_MANAGER_CONNECT))
{
AssertHandleIsValid(scm);
Assert.That(scm, ResultIs.ValidHandle);
}
}
@ -148,19 +143,10 @@ namespace Vanara.PInvoke.Tests
//opens task scheduler service
using (var service = OpenService(hSvcMgr, "Schedule", ServiceAccessTypes.SERVICE_QUERY_STATUS))
{
AssertHandleIsValid(service);
Assert.That(service, ResultIs.ValidHandle);
}
}
[Test]
public void QueryServiceStatusTest()
{
//query service status
var ret = QueryServiceStatus(hSvc, out var i);
TestContext.WriteLine(ret ? i.dwCurrentState.ToString() : $"Error: {Win32Error.GetLastError()}");
Assert.That(ret, Is.True);
}
[Test]
public void QueryServiceStatusExTest()
{
@ -171,6 +157,15 @@ namespace Vanara.PInvoke.Tests
Assert.That(status.dwServiceFlags, Is.EqualTo(0));
}
[Test]
public void QueryServiceStatusTest()
{
//query service status
var ret = QueryServiceStatus(hSvc, out var i);
TestContext.WriteLine(ret ? i.dwCurrentState.ToString() : $"Error: {Win32Error.GetLastError()}");
Assert.That(ret, Is.True);
}
[Test]
public void StartStopServiceTest()
{
@ -199,19 +194,9 @@ namespace Vanara.PInvoke.Tests
}
}
private static void AssertHandleIsValid(SafeSC_HANDLE handle)
{
if (handle.IsInvalid)
Win32Error.ThrowLastError();
Assert.That(handle.IsNull, Is.False);
Assert.That(handle.IsClosed, Is.False);
Assert.That(handle.IsInvalid, Is.False);
}
private static ServiceState GetState(SC_HANDLE handle) => QueryServiceStatus(handle, out var i) ? i.dwCurrentState : throw Win32Error.GetLastError().GetException();
private static void WaitForServiceStatus(SafeSC_HANDLE service, ServiceState status)
private static void WaitForServiceStatus(SC_HANDLE service, ServiceState status)
{
//query service status again to check that it changed
var tests = 0;