ServiceController serviceController = new ServiceController(someService);
serviceController.Stop();
serviceController.WaitForStopped();
DoSomething();
SomeService works on a sqlserver file. DoSomething() wants to copy that SQL file. If SomeService isn't closed fully it will throw an error because the database file is still locked. In the aforementioned code, I get past the WaitForStopped() method and yet the service doesn't release the database file until after DoSomething(), thus I get an error.
Doing some more investigation, I find that before the DoSomething method call I see that the service controller status shows a stopped and yet looking at some ProcMon logs the service releases the database file after I'm thrown an error from DoSomething.
Also, if I put a Thread.Sleep between the WaitForStopped and the DoSomething method for say... 5 seconds, the database file is released and all is well. Not the solution of surety I'm looking for however.
Any ideas?
Windows Services are a layer on top of processes; in order to be a service, an application must connect to the Service Control Manager and announce which services are available. This connection is handled within the ADVAPI32.DLL library. Once this connection is established, the library maintains a thread waiting for commands from the Service Control Manager, which can then start and stop services arbitrarily. I don't believe the process is required to exit when the last service in it terminates. Though that is what typically happens, the end of the link with the Service Control Manager, which occurs after the last service enters the "Stopped" state, can occur significantly before the process actually terminates, releasing any resources it hasn't already explicitly released.
The Windows Service API includes functionality that lets you obtain the Process ID of the process that is hosting the service. It is possible for a single process to host many services, and so the process might not actually exit when the service you are interested in has terminated, but you should be safe with SQL Server. Unfortunately, the .NET Framework does not expose this functionality. It does, however, expose the handle to the service that it uses internally for API calls, and you can use it to make your own API calls. With a bit of P/Invoke, then, you can obtain the process ID of the Windows Service process, and from there, provided you have the necessary permission, you can open a handle to the process that can be used to wait for it to exit.
Something like this:
[DllImport("advapi32")]
static extern bool QueryServiceStatusEx(IntPtr hService, int InfoLevel, ref SERVICE_STATUS_PROCESS lpBuffer, int cbBufSize, out int pcbBytesNeeded);
const int SC_STATUS_PROCESS_INFO = 0;
[StructLayout(LayoutKind.Sequential)]
struct SERVICE_STATUS_PROCESS
{
public int dwServiceType;
public int dwCurrentState;
public int dwControlsAccepted;
public int dwWin32ExitCode;
public int dwServiceSpecificExitCode;
public int dwCheckPoint;
public int dwWaitHint;
public int dwProcessId;
public int dwServiceFlags;
}
const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
const int SERVICE_INTERACTIVE_PROCESS = 0x00000100;
const int SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001;
public static void StopServiceAndWaitForExit(string serviceName)
{
using (ServiceController controller = new ServiceController(serviceName))
{
SERVICE_STATUS_PROCESS ssp = new SERVICE_STATUS_PROCESS();
int ignored;
// Obtain information about the service, and specifically its hosting process,
// from the Service Control Manager.
if (!QueryServiceStatusEx(controller.ServiceHandle.DangerousGetHandle(), SC_STATUS_PROCESS_INFO, ref ssp, Marshal.SizeOf(ssp), out ignored))
throw new Exception("Couldn't obtain service process information.");
// A few quick sanity checks that what the caller wants is *possible*.
if ((ssp.dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) != SERVICE_WIN32_OWN_PROCESS)
throw new Exception("Can't wait for the service's hosting process to exit because there may be multiple services in the process (dwServiceType is not SERVICE_WIN32_OWN_PROCESS");
if ((ssp.dwServiceFlags & SERVICE_RUNS_IN_SYSTEM_PROCESS) != 0)
throw new Exception("Can't wait for the service's hosting process to exit because the hosting process is a critical system process that will not exit (SERVICE_RUNS_IN_SYSTEM_PROCESS flag set)");
if (ssp.dwProcessId == 0)
throw new Exception("Can't wait for the service's hosting process to exit because the process ID is not known.");
// Note: It is possible for the next line to throw an ArgumentException if the
// Service Control Manager's information is out-of-date (e.g. due to the process
// having *just* been terminated in Task Manager) and the process does not really
// exist. This is a race condition. The exception is the desirable result in this
// case.
using (Process process = Process.GetProcessById(ssp.dwProcessId))
{
// EDIT: There is no need for waiting in a separate thread, because MSDN says "The handles are valid until closed, even after the process or thread they represent has been terminated." ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms684868%28v=vs.85%29.aspx ), so to keep things in the same thread, the process HANDLE should be opened from the process id before the service is stopped, and the Wait should be done after that.
// Response to EDIT: What you report is true, but the problem is that the handle isn't actually opened by Process.GetProcessById. It's only opened within the .WaitForExit method, which won't return until the wait is complete. Thus, if we try the wait on the current therad, we can't actually do anything until it's done, and if we defer the check until after the process has completed, it won't be possible to obtain a handle to it any more.
// The actual wait, using process.WaitForExit, opens a handle with the SYNCHRONIZE
// permission only and closes the handle before returning. As long as that handle
// is open, the process can be monitored for termination, but if the process exits
// before the handle is opened, it is no longer possible to open a handle to the
// original process and, worse, though it exists only as a technicality, there is
// a race condition in that another process could pop up with the same process ID.
// As such, we definitely want the handle to be opened before we ask the service
// to close, but since the handle's lifetime is only that of the call to WaitForExit
// and while WaitForExit is blocking the thread we can't make calls into the SCM,
// it would appear to be necessary to perform the wait on a separate thread.
ProcessWaitForExitData threadData = new ProcessWaitForExitData();
threadData.Process = process;
Thread processWaitForExitThread = new Thread(ProcessWaitForExitThreadProc);
processWaitForExitThread.IsBackground = Thread.CurrentThread.IsBackground;
processWaitForExitThread.Start(threadData);
// Now we ask the service to exit.
controller.Stop();
// Instead of waiting until the *service* is in the "stopped" state, here we
// wait for its hosting process to go away. Of course, it's really that other
// thread waiting for the process to go away, and then we wait for the thread
// to go away.
lock (threadData.Sync)
while (!threadData.HasExited)
Monitor.Wait(threadData.Sync);
}
}
}
class ProcessWaitForExitData
{
public Process Process;
public volatile bool HasExited;
public object Sync = new object();
}
static void ProcessWaitForExitThreadProc(object state)
{
ProcessWaitForExitData threadData = (ProcessWaitForExitData)state;
try
{
threadData.Process.WaitForExit();
}
catch {}
finally
{
lock (threadData.Sync)
{
threadData.HasExited = true;
Monitor.PulseAll(threadData.Sync);
}
}
}
ServiceController.WaitForStopped()/WaitForStatus() will return once the service implementation claims it has stopped. This doesn't necessary mean the process has released all of its resources and has exited. I've seen database other than SQL Server do this as well.
If you really want to be sure the database is fully and truly stopped, you will have to interface with the database itself, get ahold of the process id and wait for it to exit, wait for locks on the files to be released, ...
In my case I have used the interops:
[StructLayout(LayoutKind.Sequential)]
public struct SC_HANDLE__
{
public int unused;
}
[Flags]
public enum SERVICE_CONTROL : uint
{
STOP = 0x00000001,
PAUSE = 0x00000002,
CONTINUE = 0x00000003,
INTERROGATE = 0x00000004,
SHUTDOWN = 0x00000005,
PARAMCHANGE = 0x00000006,
NETBINDADD = 0x00000007,
NETBINDREMOVE = 0x00000008,
NETBINDENABLE = 0x00000009,
NETBINDDISABLE = 0x0000000A,
DEVICEEVENT = 0x0000000B,
HARDWAREPROFILECHANGE = 0x0000000C,
POWEREVENT = 0x0000000D,
SESSIONCHANGE = 0x0000000E
}
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
/// DWORD->unsigned int
public uint dwServiceType;
/// DWORD->unsigned int
public uint dwCurrentState;
/// DWORD->unsigned int
public uint dwControlsAccepted;
/// DWORD->unsigned int
public uint dwWin32ExitCode;
/// DWORD->unsigned int
public uint dwServiceSpecificExitCode;
/// DWORD->unsigned int
public uint dwCheckPoint;
/// DWORD->unsigned int
public uint dwWaitHint;
}
public class NativeMethods
{
public const int SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED
| (SC_MANAGER_CONNECT
| (SC_MANAGER_CREATE_SERVICE
| (SC_MANAGER_ENUMERATE_SERVICE
| (SC_MANAGER_LOCK
| (SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_MODIFY_BOOT_CONFIG))))));
/// STANDARD_RIGHTS_REQUIRED -> (0x000F0000L)
public const int STANDARD_RIGHTS_REQUIRED = 983040;
/// SC_MANAGER_CONNECT -> 0x0001
public const int SC_MANAGER_CONNECT = 1;
/// SC_MANAGER_CREATE_SERVICE -> 0x0002
public const int SC_MANAGER_CREATE_SERVICE = 2;
/// SC_MANAGER_ENUMERATE_SERVICE -> 0x0004
public const int SC_MANAGER_ENUMERATE_SERVICE = 4;
/// SC_MANAGER_LOCK -> 0x0008
public const int SC_MANAGER_LOCK = 8;
/// SC_MANAGER_QUERY_LOCK_STATUS -> 0x0010
public const int SC_MANAGER_QUERY_LOCK_STATUS = 16;
/// SC_MANAGER_MODIFY_BOOT_CONFIG -> 0x0020
public const int SC_MANAGER_MODIFY_BOOT_CONFIG = 32;
/// SERVICE_CONTROL_STOP -> 0x00000001
public const int SERVICE_CONTROL_STOP = 1;
/// SERVICE_QUERY_STATUS -> 0x0004
public const int SERVICE_QUERY_STATUS = 4;
public const int GENERIC_EXECUTE = 536870912;
/// SERVICE_RUNNING -> 0x00000004
public const int SERVICE_RUNNING = 4;
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW")]
public static extern IntPtr OpenSCManagerW(
[In()] [MarshalAs(UnmanagedType.LPWStr)] string lpMachineName,
[In()] [MarshalAs(UnmanagedType.LPWStr)] string lpDatabaseName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ControlService(IntPtr hService, SERVICE_CONTROL dwControl, ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll", EntryPoint = "QueryServiceStatus", CharSet = CharSet.Auto)]
public static extern bool QueryServiceStatus(IntPtr hService, ref SERVICE_STATUS dwServiceStatus);
[SecurityCritical]
[HandleProcessCorruptedStateExceptions]
public static void ServiceStop()
{
IntPtr manager = IntPtr.Zero;
IntPtr service = IntPtr.Zero;
SERVICE_STATUS status = new SERVICE_STATUS();
if ((manager = OpenSCManagerW(null, null, SC_MANAGER_ALL_ACCESS)) != IntPtr.Zero)
{
if ((service = OpenService(manager, Resources.ServiceName, SC_MANAGER_ALL_ACCESS)) != IntPtr.Zero)
{
QueryServiceStatus(service, ref status);
}
if (status.dwCurrentState == SERVICE_RUNNING)
{
int i = 0;
//not the best way, but WaitStatus didnt work correctly.
while (i++ < 10 && status.dwCurrentState != SERVICE_CONTROL_STOP)
{
ControlService(service, SERVICE_CONTROL.STOP, ref status);
QueryServiceStatus(service, ref status);
Thread.Sleep(200);
}
}
}
if (manager != IntPtr.Zero)
{
var b = CloseServiceHandle(manager);
}
if (service != IntPtr.Zero)
{
var b = CloseServiceHandle(service);
}
}
}
I've seen this before when I stopped a service that was a dependency to another service, and the second service was holding resources I didn't even know it was using. Do you think this might be the case? I know SQL has quite a few different components, but I haven't looked into whether there are multiple services associated with it.
Good luck!
Even using #Jonathan Gilbert excellent answer I still had cases where I could not delete the service executable file after the service process was stopped. I found out I had to also call process.Kill() at the end of it all to totally free resources.
Here is a version of Jonathan Gilbert answer which adds Kill, a timeout, and better thread synchronization:
namespace System.ServiceProcess {
public static class ExtensionMethods {
[DllImport("advapi32")]
static extern bool QueryServiceStatusEx(IntPtr hService, int InfoLevel, ref SERVICE_STATUS_PROCESS lpBuffer, int cbBufSize, out int pcbBytesNeeded);
[StructLayout(LayoutKind.Sequential)] struct SERVICE_STATUS_PROCESS { public int dwServiceType; public int dwCurrentState; public int dwControlsAccepted; public int dwWin32ExitCode; public int dwServiceSpecificExitCode; public int dwCheckPoint; public int dwWaitHint; public int dwProcessId; public int dwServiceFlags; }
const int SC_STATUS_PROCESS_INFO = 0, SERVICE_WIN32_OWN_PROCESS = 0x00000010, SERVICE_INTERACTIVE_PROCESS = 0x00000100, SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001;
record ProcessWaitForExitData(int ProcessId, AutoResetEvent MWaitForThread, int TimeoutMilliseconds);
// wait for the actual process that runs the service to stop
public static void StopAndWaitForProcessToExit(this ServiceController controller, int timeoutMilliseconds=-1) {
var processId = -1;
try {
var ssp = new SERVICE_STATUS_PROCESS();
// check can't obtain service process informatio
if (QueryServiceStatusEx(controller.ServiceHandle.DangerousGetHandle(), SC_STATUS_PROCESS_INFO, ref ssp, Marshal.SizeOf(ssp), out int ignored)
// check can't wait for the service's hosting process to exit because there may be multiple services in the process (dwServiceType is not SERVICE_WIN32_OWN_PROCESS)
&& (ssp.dwServiceType & ~SERVICE_INTERACTIVE_PROCESS) == SERVICE_WIN32_OWN_PROCESS
// check can't wait for the service's hosting process to exit because the hosting process is a critical system process that will not exit (SERVICE_RUNS_IN_SYSTEM_PROCESS flag set)");
&& (ssp.dwServiceFlags & SERVICE_RUNS_IN_SYSTEM_PROCESS) == 0
// chec can't wait for the service's hosting process to exit because the process ID is not known.");
&& ssp.dwProcessId != 0) processId = ssp.dwProcessId;
} catch (Exception) {
}
if (processId==-1) {
controller.Stop(); // stop the service
return; // we did all we can
}
// we need to call WaitForExit before stopping the service so we do it in a separate thread
var mWaitForThread = new AutoResetEvent(false);
var processWaitForExitThread = new Thread(ProcessWaitForExitThreadProc) { IsBackground = Thread.CurrentThread.IsBackground };
processWaitForExitThread.Start(new ProcessWaitForExitData(processId, mWaitForThread, timeoutMilliseconds));
Task.Delay(5).Wait(); // let thread reach WaitForExit, is there a better way ?
controller.Stop(); // stop the service
mWaitForThread.WaitOne(); // wait for process to exit
}
static void ProcessWaitForExitThreadProc(object? state) {
ProcessWaitForExitData threadData = (ProcessWaitForExitData)state!;
try {
using Process process = Process.GetProcessById(threadData.ProcessId);
var stopwatch = Stopwatch.StartNew();
process.WaitForExit(threadData.TimeoutMilliseconds);
process.Kill(true); // free all process resources
var killTimeout = threadData.TimeoutMilliseconds==-1 ? -1 : (int)Math.Max(threadData.TimeoutMilliseconds - stopwatch.ElapsedMilliseconds, 10);
if (!process.WaitForExit(killTimeout)) throw new TimeoutException();
}
catch { }
finally {
threadData.MWaitForThread.Set();
}
}
}
}
To use:
using ServiceController controller = new(serviceName);
controller.StopAndWaitForProcessToExit(timeoutMilliseconds);
Try to use Environment.Exit(1);
Related
I am working on a c# windows app and I want to add a feature where the app will shut itself off after 10 minutes of inactivity.
any implementation code will be welcome.
You might need some p-invoke, specifically GetLastInputInfo windows function. It tells you when was the last input (keyboard, mouse) detected for current user.
internal class Program {
private static void Main() {
// don't run timer too often, you just need to detect 10-minutes idle, so running every 5 minutes or so is ok
var timer = new Timer(_ => {
var last = new LASTINPUTINFO();
last.cbSize = (uint)LASTINPUTINFO.SizeOf;
last.dwTime = 0u;
if (GetLastInputInfo(ref last)) {
var idleTime = TimeSpan.FromMilliseconds(Environment.TickCount - last.dwTime);
// Console.WriteLine("Idle time is: {0}", idleTime);
if (idleTime > TimeSpan.FromMinutes(10)) {
// shutdown here
}
}
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
Console.ReadKey();
timer.Dispose();
}
[DllImport("user32.dll")]
public static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
[StructLayout(LayoutKind.Sequential)]
public struct LASTINPUTINFO {
public static readonly int SizeOf = Marshal.SizeOf(typeof (LASTINPUTINFO));
[MarshalAs(UnmanagedType.U4)] public UInt32 cbSize;
[MarshalAs(UnmanagedType.U4)] public UInt32 dwTime;
}
}
I have a C# winforms application which functions as a server for some access devices through a .DLL.
User access is determined by sending input to a webservice (set up as a webreference), and returning the results to the devices, however in case of a timeout, the app disconnects all devices, stops the server, and starts up a backgroundworker. The backgroundworker retries the connection to the webservice, and if succesfull, starts up the server again, and reconnects the devices.
This all works very well, but unfortunately, on the third fourth or fifth time, the backgroundworker tries to reconnect to the webservice, the connection fails with an exception "Either the application has not called WSAStartup, or WSAStartup failed" . each following try, gets a simmilar error.
Here is the source for the backgroundworker, its very simple code:
private void backgroundWorkerStopServer_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(2000);
stopServer();
NewDoorCheck.Doorcheck ndoorcheck = new NewDoorCheck.Doorcheck();
ndoorcheck.Timeout = 15000;
bool disconnected = true;
while (disconnected)
{
try
{
ndoorcheck.WebserviceIsUp();
UpdateLog("Connected web");
disconnected = false;
startServer();
}
catch (Exception ex)
{
UpdateLog(ex.Message);
UpdateLog(ex.StackTrace);
UpdateLog("Still Down");
System.Threading.Thread.Sleep(60000);
}
}
As a side note, the webservice works like a charm otherwise.
I ended up solving this by calling WSAstartup manually in the exception handler, and retrying.
Import of Winsock
class WSAinterop
{
[StructLayout(LayoutKind.Sequential)]
internal struct WSAData
{
public short wVersion;
public short wHighVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string szDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)]
public string szSystemStatus;
public short iMaxSockets;
public short iMaxUdpDg;
public int lpVendorInfo;
}
[DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int WSAStartup(
[In] short wVersionRequested,
[Out] out WSAData lpWSAData
);
[DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int WSACleanup();
private const int IP_SUCCESS = 0;
private const short VERSION = 2;
public static bool SocketInitialize()
{
WSAData d;
return WSAStartup(VERSION, out d) == IP_SUCCESS;
}
/// <summary>
/// The main entry point for the application.
/// </summary>
}
And simply use the SocketInitialize() Method.
bool startup = WSAinterop.SocketInitialize();
I am wondering if it is possible to fall victim to issues around the management of managed threads in the native world when you marshal a callback delegate to a DLL through P/Invoke in my particular case below (see example code).
This MSDN article on Managed and Unmanaged Threading in Windows states:
"An operating-system ThreadId has no fixed relationship to a managed thread, because an unmanaged host can control the relationship between managed and unmanaged threads. Specifically, a sophisticated host can use the Fiber API to schedule many managed threads against the same operating system thread, or to move a managed thread among different operating system threads."
First of all, who or what is the unmanaged host this article describes? If you use marshaling like in the example code I give below, then who or what is the unmanaged host there?
Also, this StackOverflow question's accepted answer states:
"It's perfectly legal from a CLR perspective for a single managed thread to be backed by several different native threads during it's lifetime. This means the result of GetCurrentThreadId can (and will) change throughout the course of a thread's lifetime."
So, does this mean my APC will be queued in a native thread, or delegated directly to my managed thread because of the marshaling layer?
Here is the example. Let's say I use the following class to P/Invoke the NotifyServiceStatusChange function in managed code to check when a service is stopped:
using System;
using System.Runtime.InteropServices;
namespace ServiceStatusChecking
{
class QueryServiceStatus
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
public static void WaitForServiceToStop(string serviceName)
{
IntPtr hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
IntPtr hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
GCHandle notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(uint.MaxValue, true);
notifyHandle.Free();
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
Is the APC queued onto whichever native thread was hosting my managed thread, or is the APC delegated directly to my managed thread? I thought the delegate was there to handle exactly this case, so that we don't need to worry about how managed threads are handled natively, but I could be wrong!
Edit: I guess this is a more agreeable answer.
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ServiceAssistant
{
class ServiceHelper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
/// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
{
Thread.BeginThreadAffinity();
GCHandle? notifyHandle = null;
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
IntPtr hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
IntPtr hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = ((GCHandle)notifyHandle).AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(timeout, true);
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != null)
{
((GCHandle)notifyHandle).Free();
}
Thread.EndThreadAffinity();
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
Edit again! I guess THIS is an even MORE agreeable answer:
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ServiceAssistant
{
class ServiceHelper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public class SERVICE_NOTIFY
{
public uint dwVersion;
public IntPtr pfnNotifyCallback;
public IntPtr pContext;
public uint dwNotificationStatus;
public SERVICE_STATUS_PROCESS ServiceStatus;
public uint dwNotificationTriggered;
public IntPtr pszServiceNames;
};
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SERVICE_STATUS_PROCESS
{
public uint dwServiceType;
public uint dwCurrentState;
public uint dwControlsAccepted;
public uint dwWin32ExitCode;
public uint dwServiceSpecificExitCode;
public uint dwCheckPoint;
public uint dwWaitHint;
public uint dwProcessId;
public uint dwServiceFlags;
};
[DllImport("advapi32.dll")]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll")]
static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll")]
static extern uint NotifyServiceStatusChange(IntPtr hService, uint dwNotifyMask, IntPtr pNotifyBuffer);
[DllImportAttribute("kernel32.dll")]
static extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport("advapi32.dll")]
static extern bool CloseServiceHandle(IntPtr hSCObject);
delegate void StatusChangedCallbackDelegate(IntPtr parameter);
/// <summary>
/// Block until a service stops, is killed, or is found to be already dead.
/// </summary>
/// <param name="serviceName">The name of the service you would like to wait for.</param>
/// <param name="timeout">An amount of time you would like to wait for. uint.MaxValue is the default, and it will force this thread to wait indefinitely.</param>
public static void WaitForServiceToStop(string serviceName, uint timeout = uint.MaxValue)
{
// Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
Thread.BeginThreadAffinity();
GCHandle notifyHandle = default(GCHandle);
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
IntPtr hSCM = IntPtr.Zero;
IntPtr hService = IntPtr.Zero;
try
{
hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
SERVICE_NOTIFY notify = new SERVICE_NOTIFY();
notify.dwVersion = 2;
notify.pfnNotifyCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
notify.ServiceStatus = new SERVICE_STATUS_PROCESS();
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
IntPtr pinnedNotifyStructure = notifyHandle.AddrOfPinnedObject();
NotifyServiceStatusChange(hService, (uint)0x00000001, pinnedNotifyStructure);
SleepEx(timeout, true);
}
}
}
finally
{
// Clean up at the end of our operation, or if this thread is aborted.
if (hService != IntPtr.Zero)
{
CloseServiceHandle(hService);
}
if (hSCM != IntPtr.Zero)
{
CloseServiceHandle(hSCM);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != default(GCHandle))
{
notifyHandle.Free();
}
Thread.EndThreadAffinity();
}
}
public static void ReceivedStatusChangedEvent(IntPtr parameter)
{
}
}
}
Yes, it is possible to fall victim to these issues. In this particular case it is difficult. The host can't switch the managed thread to a different OS thread while a native frame is on the managed thread's stack, and since you immediately p/invoke SleepEx, the window for the host to switch the managed thread is between the two p/invoke calls. Still, it is sometimes a disagreeable possibility when you need to p/invoke on the same OS thread and Thread.BeginThreadAffinity() exists to cover this scenario.
Now for the APC question. Remember that the OS knows nothing about managed threads. So the APC will be delivered into the original native thread when it does something alertable. I don't know how the CLR host creates managed contexts in these cases, but if managed threads are one-to-one with OS threads the callback will probably use the managed thread's context.
UPDATE Your new code is much safer now, but you went a bit too far in the other direction:
1) There is no need to wrap the whole code with thread affinity. Wrap just the two p/invokes that do need to run on the same OS thread (Notify and Sleep). It does not matter whether you use a finite timeout, because the problem you're solving with thread affinity is a managed-to-OS thread migration between the two p/invokes. The callback should not assume it is running on any particular managed thread anyway, because there is little it can safely do, and little it should do: interlocked operations, setting events and completing TaskCompletionSources is about it.
2) GCHandle is a simple, IntPtr-sized struct, and can be compared for equality. Instead of using GCHandle?, use plain GCHandle and compare to default(GCHandle). Besides, GCHandle? looks fishy to me on general principles.
3) Notification stops when you close the service handle. The SCM handle can stay open, you might want to keep it around for the next check.
// Thread.BeginThreadAffinity();
// GCHandle? notifyHandle = null;
var hSCM = OpenSCManager(null, null, (uint)0xF003F);
if (hSCM != IntPtr.Zero)
{
StatusChangedCallbackDelegate changeDelegate = ReceivedStatusChangedEvent;
var notifyHandle = default(GCHandle);
var hService = OpenService(hSCM, serviceName, (uint)0xF003F);
if (hService != IntPtr.Zero)
{
...
notifyHandle = GCHandle.Alloc(notify, GCHandleType.Pinned);
var addr = notifyHandle.AddrOfPinnedObject();
Thread.BeginThreadAffinity();
NotifyServiceStatusChange(hService, (uint)0x00000001, addr);
SleepEx(timeout, true);
Thread.EndThreadAffinity();
CloseServiceHandle(hService);
}
GC.KeepAlive(changeDelegate);
if (notifyHandle != default(GCHandle))
notifyHandle.Free();
CloseServiceHandle(hSCM);
}
Also, to be as safe as possible, if your code is going to run for a long time, or if you're writing a library, you must use constrained regions and/or SafeHandles to ensure your cleanup routines run even if the thread is aborted. Look at all the hoops BCL code jumps through in, e.g., System.Threading.Mutex (use Reflector or the CLR source). At the very least, use SafeHandles and try/finally to free the GCHandle and end thread affinity.
As for callback-related problems, these are just a bad case of normal multi-threading sort of problems: deadlocks, livelocks, priority inversion etc. The worst thing about this sort of APC callback is that (unless you block the whole thread yourself until it happens, in which case it's easier just to block in native code) you don't control when it happens: your thread might be deep inside BCL waiting for I/O, for an event to be signaled, etc., and it is very difficult to reason about the state the program might be in.
Asynchronous procedure calls exist completely on the native side. APC's know nothing of managed threads nor of marshaling. NotifyServiceStatusChange would have to call (or use the equivalent of) QueueUserAPC to dispatch the APC, which only takes an native thread handle. So, the APC will be queued to the native thread that calls NotifyServiceStatusChange.
So this APC being queued and dispatched correctly rely on two things:
The CLR keeps the native thread that it called NotifyServiceStatusChange.
The CLR puts that native thread into an alterable wait.
You control neither of these two things.
I'd like to be able to automatically attach a debugger, something like: System.Diagnostics.Debugger.Launch(), except rather than the current process to another named process. I've got a process name and PID to identify the other process.
Is this possible?
Edit:
GSerjo offered the correct solution. I'd like to share a few thoughts on how to improve it (and an explanation). I hope my improved answer will be useful to to others who experience the same problem.
Attaching the VS Debugger to a Process
Manually
Open the Windows Task Manager (Ctrl + Shift + Esc).
Go to the Tab Processes.
Right click the process.
Select Debug.
Or, within Visual Studio, select Debug > Attach to Process....
Results will vary depending on whether you have access to the source code.
Automatically with C#
A note of caution: The following code is brittle in the sense that certain values,
such as the Visual Studio Version number, are hard-coded. Keep this in mind going forward
if you are planning to distribute your program.
First of all, add a reference to EnvDTE to your project (right click on the references folder in the solution explorer, add reference). In the following code, I'll only show the unusual using directives; the normal ones such as using System are omitted.
Because you are interacting with COM you need to make sure to decorate your Main method (the entry point of your application) with the STAThreadAttribute.
Then, you need to define the IOleMessageFilter Interface that will allow you to interact with the defined COM methods (note the ComImportAttribute). We need to access the message filter so we can retry if the Visual Studio COM component blocks one of our calls.
using System.Runtime.InteropServices;
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
Now, we need to implement this interface in order to handle incoming messages:
public class MessageFilter : IOleMessageFilter
{
private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return Handled;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RetryAllowed ? Retry : Cancel;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return WaitAndDispatch;
}
public static void Register()
{
CoRegisterMessageFilter(new MessageFilter());
}
public static void Revoke()
{
CoRegisterMessageFilter(null);
}
private static void CoRegisterMessageFilter(IOleMessageFilter newFilter)
{
IOleMessageFilter oldFilter;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
I defined the return values as constants for better readability and refactored the whole thing a bit to get rid of some of the duplication from the MSDN example, so I hope you'll find it self-explanatory. extern int CoRegisterMessageFilter is our connection to the unmanaged message filter code - you can read up on the extern keyword at MSDN.
Now all that's left is some code illustrating the usage:
using System.Runtime.InteropServices;
using EnvDTE;
[STAThread]
public static void Main()
{
MessageFilter.Register();
var process = GetProcess(7532);
if (process != null)
{
process.Attach();
Console.WriteLine("Attached to {0}", process.Name);
}
MessageFilter.Revoke();
Console.ReadLine();
}
private static Process GetProcess(int processID)
{
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.10.0");
var processes = dte.Debugger.LocalProcesses.OfType<Process>();
return processes.SingleOrDefault(x => x.ProcessID == processID);
}
Check this out
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
using NUnit.Framework;
namespace UnitTests
{
[TestFixture]
public class ForTest
{
[STAThread]
[Test]
public void Test()
{
var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.10.0");
MessageFilter.Register();
IEnumerable<Process> processes = dte.Debugger.LocalProcesses.OfType<Process>();
var process = processes.SingleOrDefault(x => x.ProcessID == 6152);
if (process != null)
{
process.Attach();
}
}
}
public class MessageFilter : IOleMessageFilter
{
//
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
//
// IOleMessageFilter functions.
// Handle incoming thread requests.
#region IOleMessageFilter Members
int IOleMessageFilter.HandleInComingCall(int dwCallType,
IntPtr hTaskCaller, int dwTickCount, IntPtr
lpInterfaceInfo)
{
//Return the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee,
int dwTickCount, int dwPendingType)
{
//Return the flag PENDINGMSG_WAITDEFPROCESS.
return 2;
}
#endregion
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out
IOleMessageFilter oldFilter);
}
[ComImport, Guid("00000016-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
}
How to: Create and Attach to Another Instance of Visual Studio
How to: Fix 'Application is Busy' and 'Call was Rejected By Callee'
Errors
Simpler way of doing it.
public static void Attach(DTE2 dte)
{
var processes = dte.Debugger.LocalProcesses;
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf("YourProcess.exe") != -1))
proc.Attach();
}
internal static DTE2 GetCurrent()
{
var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.12.0"); // For VisualStudio 2013
return dte2;
}
Usage:
Attach(GetCurrent());
An option is to run; vsjitdebugger.exe -p ProcessId
It may be possible to use Process.Start to do this within a c# app.
If you have troubles with attaching debugger to process that is too quick to attach manually, don't forget you can sometimes add Console.ReadKey(); to a first line of your code and then you have all the time you need to attach it manually. Surprisingly it took me a while to figure that one out :D
I'd like to be able to automatically attach a debugger, something like: System.Diagnostics.Debugger.Launch(), except rather than the current process to another named process. I've got a process name and PID to identify the other process.
Is this possible?
Edit:
GSerjo offered the correct solution. I'd like to share a few thoughts on how to improve it (and an explanation). I hope my improved answer will be useful to to others who experience the same problem.
Attaching the VS Debugger to a Process
Manually
Open the Windows Task Manager (Ctrl + Shift + Esc).
Go to the Tab Processes.
Right click the process.
Select Debug.
Or, within Visual Studio, select Debug > Attach to Process....
Results will vary depending on whether you have access to the source code.
Automatically with C#
A note of caution: The following code is brittle in the sense that certain values,
such as the Visual Studio Version number, are hard-coded. Keep this in mind going forward
if you are planning to distribute your program.
First of all, add a reference to EnvDTE to your project (right click on the references folder in the solution explorer, add reference). In the following code, I'll only show the unusual using directives; the normal ones such as using System are omitted.
Because you are interacting with COM you need to make sure to decorate your Main method (the entry point of your application) with the STAThreadAttribute.
Then, you need to define the IOleMessageFilter Interface that will allow you to interact with the defined COM methods (note the ComImportAttribute). We need to access the message filter so we can retry if the Visual Studio COM component blocks one of our calls.
using System.Runtime.InteropServices;
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
Now, we need to implement this interface in order to handle incoming messages:
public class MessageFilter : IOleMessageFilter
{
private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return Handled;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RetryAllowed ? Retry : Cancel;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return WaitAndDispatch;
}
public static void Register()
{
CoRegisterMessageFilter(new MessageFilter());
}
public static void Revoke()
{
CoRegisterMessageFilter(null);
}
private static void CoRegisterMessageFilter(IOleMessageFilter newFilter)
{
IOleMessageFilter oldFilter;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
I defined the return values as constants for better readability and refactored the whole thing a bit to get rid of some of the duplication from the MSDN example, so I hope you'll find it self-explanatory. extern int CoRegisterMessageFilter is our connection to the unmanaged message filter code - you can read up on the extern keyword at MSDN.
Now all that's left is some code illustrating the usage:
using System.Runtime.InteropServices;
using EnvDTE;
[STAThread]
public static void Main()
{
MessageFilter.Register();
var process = GetProcess(7532);
if (process != null)
{
process.Attach();
Console.WriteLine("Attached to {0}", process.Name);
}
MessageFilter.Revoke();
Console.ReadLine();
}
private static Process GetProcess(int processID)
{
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.10.0");
var processes = dte.Debugger.LocalProcesses.OfType<Process>();
return processes.SingleOrDefault(x => x.ProcessID == processID);
}
Check this out
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
using NUnit.Framework;
namespace UnitTests
{
[TestFixture]
public class ForTest
{
[STAThread]
[Test]
public void Test()
{
var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.10.0");
MessageFilter.Register();
IEnumerable<Process> processes = dte.Debugger.LocalProcesses.OfType<Process>();
var process = processes.SingleOrDefault(x => x.ProcessID == 6152);
if (process != null)
{
process.Attach();
}
}
}
public class MessageFilter : IOleMessageFilter
{
//
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
//
// IOleMessageFilter functions.
// Handle incoming thread requests.
#region IOleMessageFilter Members
int IOleMessageFilter.HandleInComingCall(int dwCallType,
IntPtr hTaskCaller, int dwTickCount, IntPtr
lpInterfaceInfo)
{
//Return the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee,
int dwTickCount, int dwPendingType)
{
//Return the flag PENDINGMSG_WAITDEFPROCESS.
return 2;
}
#endregion
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out
IOleMessageFilter oldFilter);
}
[ComImport, Guid("00000016-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
}
How to: Create and Attach to Another Instance of Visual Studio
How to: Fix 'Application is Busy' and 'Call was Rejected By Callee'
Errors
Simpler way of doing it.
public static void Attach(DTE2 dte)
{
var processes = dte.Debugger.LocalProcesses;
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf("YourProcess.exe") != -1))
proc.Attach();
}
internal static DTE2 GetCurrent()
{
var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.12.0"); // For VisualStudio 2013
return dte2;
}
Usage:
Attach(GetCurrent());
An option is to run; vsjitdebugger.exe -p ProcessId
It may be possible to use Process.Start to do this within a c# app.
If you have troubles with attaching debugger to process that is too quick to attach manually, don't forget you can sometimes add Console.ReadKey(); to a first line of your code and then you have all the time you need to attach it manually. Surprisingly it took me a while to figure that one out :D