GC.KeepAlive 方法
引用指定对象,使其从当前例程开始到调用此方法的那一刻为止均不符合进行垃圾回收的条件。
**命名空间:**System
**程序集:**mscorlib(在 mscorlib.dll 中)
语法
声明
Public Shared Sub KeepAlive ( _
obj As Object _
)
用法
Dim obj As Object
GC.KeepAlive(obj)
public static void KeepAlive (
Object obj
)
public:
static void KeepAlive (
Object^ obj
)
public static void KeepAlive (
Object obj
)
public static function KeepAlive (
obj : Object
)
参数
- obj
要引用的对象。
备注
KeepAlive 方法的目的是确保对对象的引用存在,该对象有被垃圾回收器过早回收的危险。这种现象可能发生的一种常见情形是,当在托管代码或数据中已没有对该对象的引用,但该对象仍然在非托管代码(如 Win32 API、非托管 DLL 或使用 COM 的方法)中使用。
另一种过早发生垃圾回收的情形是,在一个方法中创建并使用一个对象。此时,当对对象的某个成员的调用仍在执行时,可能会对该对象进行回收,如第一个代码示例所示。
此方法引用 obj,从而使该对象从例程开始到调用此方法的那个位置(按执行顺序)均不符合进行垃圾回收的条件。在 obj 必须可用的指令范围的结尾(而不是开头)编写此方法的代码。
KeepAlive 方法除了延长作为参数传递的对象的生存期之外,不会执行任何操作,也会不产生任何其他副作用。
示例
本节包含两个代码示例。第一个示例演示如何使用 KeepAlive 方法防止过早进行垃圾回收,而第二个示例演示如何在长时间运行的方法中使用 KeepAlive 方法。
示例 1
下面的代码示例演示 KeepAlive 方法如何在被回收对象的方法仍在执行时,防止过早地进行垃圾回收。
提示
此示例要求计算机安装有多个处理器。
此示例将启动一个重复调用测试方法的线程。在您按 Enter 键之前,该线程将一直执行。默认情况下,此代码示例会运行 DoWork
方法,而该方法将创建一个 Example
对象,并读取其 Hash
属性。由于 DoWork
方法不使用 KeepAlive 方法,因此 Example
对象的终结器有时会在读取完 Hash
属性之前运行。DoWork
方法将检测到这一现象并向控制台显示一条消息。
在使用参数 KeepAlive 执行时,此示例将运行 SafeDoWork
方法。SafeDoWork
方法与 DoWork
方法的唯一区别在最后一行,它的最后一行将调用 KeepAlive 方法。这可以防止 Example
对象在 SafeDoWork
方法结束之前被回收。
在使用参数 Trivial 执行时,此示例将运行 Trivial
方法,该方法访问整数属性的速度大大快于访问 Hash
属性。正是由于访问速度太快,导致终结器几乎不会首先运行。
Imports System
Imports System.Threading
Public Class Example
' The N property is very fast, because all it does is return
' a stored integer. Therefore the finalizer almost never runs
' before this property is read.
'
Private nValue As Integer
Public ReadOnly Property N() As Integer
Get
Return nValue
End Get
End Property
' The Hash property is slower because it clones an array. When
' KeepAlive is not used, the finalizer sometimes runs before
' the Hash property value is read.
'
Private hashValue() As Byte
Public ReadOnly Property Hash() As Byte()
Get
Return CType(hashValue.Clone(), Byte())
End Get
End Property
' The constructor initializes the property values.
'
Public Sub New()
nValue = 2
hashValue = New Byte(19) {}
hashValue(0) = 2
End Sub
' The finalizer sets the N property to zero, and clears all
' the elements of the array for the Hash property. The finalizer
' runs on a separate thread provided by the system. In some
' cases, the finalizer can run while a member of the Example
' instance is still executing.
'
Overloads Protected Overrides Sub Finalize()
nValue = 0
If Not (hashValue Is Nothing) Then
Array.Clear(hashValue, 0, hashValue.Length)
End If
MyBase.Finalize()
End Sub
End Class
Public Class Test
Private Shared totalCount As Integer = 0
Private Shared finalizerFirstCount As Integer = 0
' This variable controls the thread that runs the demo.
Private Shared running As Boolean = True
' The default is to run without KeepAlive.
Private Shared kind As TestKind = TestKind.NoKeepAlive
' See the comment at the end of the SafeDoWork method.
'private static bool keepAlive = false;
' In order to demonstrate the finalizer running first, the
' DoWork method must create an Example object and invoke its
' Hash property. If there are no other calls to members of
' the Example object in DoWork, garbage collection reclaims
' the Example object aggressively. Sometimes this means that
' the finalizer runs before the call to the Hash property
' completes.
Private Shared Sub DoWork()
' Count the number of times DoWork executes.
totalCount += 1
' Create an Example object and save the value of the
' Hash property. There are no more calls to members of
' the object in the DoWork method, so it is available
' for aggressive garbage collection.
'
Dim ex As New Example()
Dim res As Byte() = ex.Hash
' If the finalizer runs before the call to the Hash
' property completes, the hashValue array might be
' cleared before the property value is read. The
' following test detects that.
'
If res(0) <> 2 Then
finalizerFirstCount += 1
Console.WriteLine("The finalizer ran first at {0} iterations.", _
totalCount)
End If
End Sub
' In the SafeDoWork method the finalizer never runs first,
' because of the KeepAlive at the end of the method.
'
Private Shared Sub SafeDoWork()
totalCount += 1
' Create an Example object and save the value of the
' Hash property.
Dim ex As New Example()
Dim res As Byte() = ex.Hash
' The finalizer cannot run before the property is read,
' because the KeepAlive method prevents the Example object
' from being reclaimed by garbage collection.
'
If res(0) <> 2 Then
finalizerFirstCount += 1
Console.WriteLine("The finalizer ran first at {0} iterations.", _
totalCount)
End If
GC.KeepAlive(ex)
' The KeepAlive method need never be executed. For example,
' if the keepAlive field is uncommented, the following line
' of code prevents the finalizer from running first, even
' though it is impossible for the KeepAlive method ever to
' be executed.
' if (keepAlive) GC.KeepAlive(ex);
' However, if the compiler detects that the KeepAlive can
' never be executed, as in the following line, then it will
' not prevent the finalizer from running first.
' if (false) GC.KeepAlive(ex);
End Sub
' In the TrivialDoWork method the finalizer almost never runs
' first, even without a KeepAlive at the end of the method,
' because accessing the N property is so fast.
'
Private Shared Sub TrivialDoWork()
totalCount += 1
' Create an Example object and save the value of the
' N property.
Dim ex As New Example()
Dim res As Integer = ex.N
' The finalizer almost never runs before the property is read,
' because accessing the N property is so fast.
'
If res <> 2 Then
finalizerFirstCount += 1
Console.WriteLine("The finalizer ran first at {0} iterations.", _
totalCount)
End If
End Sub
Public Shared Sub Main(ByVal args() As String)
' Check to see what command-line argument was passed.
If args.Length <> 0 Then
Dim arg As String = args(0).ToLower()
' The AndAlso operator prevents the second condition from
' being evaluated if the first condition is false.
If (arg.Length < 10) AndAlso _
(arg = "keepalive".Substring(0, arg.Length)) Then
kind = TestKind.KeepAlive
End If
If arg.Length < 8 AndAlso _
arg = "trivial".Substring(0, arg.Length) Then
kind = TestKind.Trivial
End If
End If
Console.WriteLine("Test: {0}", kind)
' Create a thread to run the test.
Dim t As New Thread(New ThreadStart(AddressOf ThreadProc))
t.Start()
' The thread runs until Enter is pressed.
Console.WriteLine("Press Enter to stop the program.")
Console.ReadLine()
running = False
' Wait for the thread to end.
t.Join()
Console.WriteLine("{0} iterations total; the " & _
"finalizer ran first {1} times.", _
totalCount, finalizerFirstCount)
End Sub
' This method executes the selected test.
Private Shared Sub ThreadProc()
Select Case kind
Case TestKind.KeepAlive
While running
SafeDoWork()
End While
Case TestKind.Trivial
While running
TrivialDoWork()
End While
Case Else
While running
DoWork()
End While
End Select
End Sub
Private Enum TestKind
NoKeepAlive
KeepAlive
Trivial
End Enum
End Class
' When run with the default NoKeepAlive test, on a dual-processor
' computer, this example produces output similar to the following:
'
'Test: NoKeepAlive
'Press Enter to stop the program.
'The finalizer ran first at 21098618 iterations.
'The finalizer ran first at 33944444 iterations.
'The finalizer ran first at 35160207 iterations.
'
'53169451 iterations total; the finalizer ran first 3 times.
'
using System;
using System.Threading;
public class Example
{
// The N property is very fast, because all it does is return
// a stored integer. Therefore the finalizer almost never runs
// before this property is read.
//
private int nValue;
public int N { get { return nValue; }}
// The Hash property is slower because it clones an array. When
// KeepAlive is not used, the finalizer sometimes runs before
// the Hash property value is read.
//
private byte[] hashValue;
public byte[] Hash { get { return (byte[]) hashValue.Clone(); }}
// The constructor initializes the property values.
//
public Example()
{
nValue = 2;
hashValue = new byte[20];
hashValue[0] = 2;
}
// The finalizer sets the N property to zero, and clears all
// the elements of the array for the Hash property. The finalizer
// runs on a separate thread provided by the system. In some
// cases, the finalizer can run while a member of the Example
// instance is still executing.
//
~Example()
{
nValue = 0;
if (hashValue != null)
{
Array.Clear(hashValue, 0, hashValue.Length);
}
}
}
public class Test
{
private static int totalCount = 0;
private static int finalizerFirstCount = 0;
// This variable controls the thread that runs the demo.
private static bool running = true;
// The default is to run without KeepAlive.
private static TestKind kind = TestKind.NoKeepAlive;
// See the comment at the end of the SafeDoWork method.
//private static bool keepAlive = false;
// In order to demonstrate the finalizer running first, the
// DoWork method must create an Example object and invoke its
// Hash property. If there are no other calls to members of
// the Example object in DoWork, garbage collection reclaims
// the Example object aggressively. Sometimes this means that
// the finalizer runs before the call to the Hash property
// completes.
private static void DoWork()
{
totalCount++;
// Create an Example object and save the value of the
// Hash property. There are no more calls to members of
// the object in the DoWork method, so it is available
// for aggressive garbage collection.
//
Example ex = new Example();
byte[] res = ex.Hash;
// If the finalizer runs before the call to the Hash
// property completes, the hashValue array might be
// cleared before the property value is read. The
// following test detects that.
//
if (res[0] != 2)
{
finalizerFirstCount++;
Console.WriteLine("The finalizer ran first at {0} iterations.",
totalCount);
}
}
// In the SafeDoWork method the finalizer never runs first,
// because of the KeepAlive at the end of the method.
//
private static void SafeDoWork()
{
totalCount++;
// Create an Example object and save the value of the
// Hash property.
Example ex = new Example();
byte[] res = ex.Hash;
// The finalizer cannot run before the property is read,
// because the KeepAlive method prevents the Example object
// from being reclaimed by garbage collection.
//
if (res[0] != 2)
{
finalizerFirstCount++;
Console.WriteLine("The finalizer ran first at {0} iterations.",
totalCount);
}
GC.KeepAlive(ex);
// The KeepAlive method need never be executed. For example,
// if the keepAlive field is uncommented, the following line
// of code prevents the finalizer from running first, even
// though it is impossible for the KeepAlive method ever to
// be executed.
// if (keepAlive) GC.KeepAlive(ex);
// However, if the compiler detects that the KeepAlive can
// never be executed, as in the following line, then it will
// not prevent the finalizer from running first.
// if (false) GC.KeepAlive(ex);
}
// In the TrivialDoWork method the finalizer almost never runs
// first, even without a KeepAlive at the end of the method,
// because accessing the N property is so fast.
//
private static void TrivialDoWork()
{
totalCount++;
// Create an Example object and save the value of the
// N property.
Example ex = new Example();
int res = ex.N;
// The finalizer almost never runs before the property is read,
// because accessing the N property is so fast.
//
if (res != 2)
{
finalizerFirstCount++;
Console.WriteLine("The finalizer ran first at {0} iterations.",
totalCount);
}
}
public static void Main (string[] args)
{
if (args.Length != 0)
{
string arg = args[0].ToLower();
if (arg.Length < 10 && arg == "keepalive".Substring(0, arg.Length))
kind = TestKind.KeepAlive;
if (arg.Length < 8 && arg == "trivial".Substring(0, arg.Length))
kind = TestKind.Trivial;
}
Console.WriteLine("Test: {0}", kind);
// Create a thread to run the test.
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
// The thread runs until Enter is pressed.
Console.WriteLine("Press Enter to stop the program.");
Console.ReadLine();
running = false;
// Wait for the thread to end.
t.Join();
Console.WriteLine("{0} iterations total; the finalizer ran first {1} times.",
totalCount, finalizerFirstCount);
}
private static void ThreadProc()
{
switch (kind)
{
case TestKind.KeepAlive:
while (running) SafeDoWork();
break;
case TestKind.Trivial:
while (running) TrivialDoWork();
break;
default:
while (running) DoWork();
break;
}
}
private enum TestKind
{
NoKeepAlive,
KeepAlive,
Trivial
}
}
/* When run with the default NoKeepAlive test, on a dual-processor
computer, this example produces output similar to the following:
Test: NoKeepAlive
Press Enter to stop the program.
The finalizer ran first at 21098618 iterations.
The finalizer ran first at 33944444 iterations.
The finalizer ran first at 35160207 iterations.
53169451 iterations total; the finalizer ran first 3 times.
*/
using namespace System;
using namespace System::Threading;
public ref class Example
{
// The N property is very fast, because all it does is return
// a stored integer. Therefore the finalizer almost never runs
// before this property is read.
//
private:
int nValue;
public:
property int N
{
int get()
{
return nValue;
}
}
// The Hash property is slower because it clones an array. When
// KeepAlive is not used, the finalizer sometimes runs before
// the Hash property value is read.
//
private:
array<Byte>^ hashValue;
public:
property array<Byte>^ Hash
{
array<Byte>^ get()
{
return (array<Byte>^) hashValue->Clone();
}
}
// The constructor initializes the property values.
//
public:
Example()
{
nValue = 2;
hashValue = gcnew array<Byte>(20);
hashValue[0] = 2;
}
// The finalizer sets the N property to zero, and clears all
// the elements of the array for the Hash property. The finalizer
// runs on a separate thread provided by the system. In some
// cases, the finalizer can run while a member of the Example
// instance is still executing.
//
~Example()
{
nValue = 0;
if (hashValue != nullptr)
{
Array::Clear(hashValue, 0, hashValue->Length);
}
}
};
public ref class Test
{
private:
enum class TestKind
{
NoKeepAlive,
KeepAlive,
Trivial
};
private:
static int totalCount;
private:
static int finalizerFirstCount;
// This variable controls the thread that runs the demo.
private:
static bool running = true;
// The default is to run without KeepAlive.
private:
static TestKind kind = TestKind::NoKeepAlive;
// See the comment at the end of the SafeDoWork method.
//private static bool keepAlive = false;
// In order to demonstrate the finalizer running first, the
// DoWork method must create an Example object and invoke its
// Hash property. If there are no other calls to members of
// the Example object in DoWork, garbage collection reclaims
// the Example object aggressively. Sometimes this means that
// the finalizer runs before the call to the Hash property
// completes.
private:
static void DoWork()
{
totalCount++;
// Create an Example object and save the value of the
// Hash property. There are no more calls to members of
// the object in the DoWork method, so it is available
// for aggressive garbage collection.
//
Example^ exampleObject = gcnew Example();
array<Byte>^ hashArray = exampleObject->Hash;
// If the finalizer runs before the call to the Hash
// property completes, the hashValue array might be
// cleared before the property value is read. The
// following test detects that.
//
if (hashArray[0] != 2)
{
finalizerFirstCount++;
Console::WriteLine("The finalizer ran first at {0}"+
"iterations.",totalCount);
}
}
// In the SafeDoWork method the finalizer never runs first,
// because of the KeepAlive at the end of the method.
//
private:
static void SafeDoWork()
{
totalCount++;
// Create an Example object and save the value of the
// Hash property.
Example^ exampleObject = gcnew Example();
array<Byte>^ hashArray = exampleObject->Hash;
// The finalizer cannot run before the property is read,
// because the KeepAlive method prevents the Example object
// from being reclaimed by garbage collection.
//
if (hashArray[0] != 2)
{
finalizerFirstCount++;
Console::WriteLine("The finalizer ran first at {0}"+
"iterations.",totalCount);
}
GC::KeepAlive(exampleObject);
// The KeepAlive method need never be executed. For example,
// if the keepAlive field is uncommented, the following line
// of code prevents the finalizer from running first, even
// though it is impossible for the KeepAlive method ever to
// be executed.
// if (keepAlive) GC.KeepAlive(ex);
// However, if the compiler detects that the KeepAlive can
// never be executed, as in the following line, then it will
// not prevent the finalizer from running first.
// if (false) GC.KeepAlive(ex);
}
// In the TrivialDoWork method the finalizer almost never runs
// first, even without a KeepAlive at the end of the method,
// because accessing the N property is so fast.
//
private:
static void TrivialDoWork()
{
totalCount++;
// Create an Example object and save the value of the
// N property.
Example^ exampleObject = gcnew Example();
int hashArray = exampleObject->N;
// The finalizer almost never runs before the property is
// read because accessing the N property is so fast.
//
if (hashArray != 2)
{
finalizerFirstCount++;
Console::WriteLine("The finalizer ran first at {0}"+
"iterations.",totalCount);
}
}
public:
static void Work(array<String^>^ args)
{
if (args->Length != 0)
{
String^ arg = args[0]->ToLower();
if (arg == "keepalive")
{
kind = TestKind::KeepAlive;
}
if (arg == "trivial")
{
kind = TestKind::Trivial;
}
}
Console::WriteLine("Test: {0}", kind);
// Create a thread to run the test.
Thread^ sampleThread = gcnew Thread(
gcnew ThreadStart(ThreadProc));
sampleThread->Start();
// The thread runs until Enter is pressed.
Console::WriteLine("Press Enter to stop the program.");
Console::ReadLine();
running = false;
// Wait for the thread to end.
sampleThread->Join();
Console::WriteLine("{0} iterations total; the finalizer ran " +
"first {1} times.",totalCount, finalizerFirstCount);
}
private:
static void ThreadProc()
{
switch (kind)
{
case TestKind::KeepAlive:
while(running)
{
SafeDoWork();
}
break;
case TestKind::Trivial:
while(running)
{
TrivialDoWork();
}
break;
default:
while(running)
{
DoWork();
}
break;
}
}
};
int main(array<String^>^ args)
{
Test::Work(args);
};
/* When run with the default NoKeepAlive test, on a dual-processor
computer, this example produces output similar to the following:
Test: NoKeepAlive
Press Enter to stop the program.
The finalizer ran first at 21098618 iterations.
The finalizer ran first at 33944444 iterations.
The finalizer ran first at 35160207 iterations.
53169451 iterations total; the finalizer ran first 3 times.
*/
示例 2
下面的代码示例在其 Main
方法开始处创建一个对象,但直到代码结尾处调用 KeepAlive 方法时才再次引用该对象。在 Main
方法 30 秒的持续期间,即使调用 Collect 和 WaitForPendingFinalizers 方法,该对象会一直保持。
Imports System
Imports System.Threading
Imports System.Runtime.InteropServices
' A simple class that exposes two static Win32 functions.
' One is a delegate type and the other is an enumerated type.
Public Class MyWin32
' Declare the SetConsoleCtrlHandler function as external
' and receiving a delegate.
<DllImport("Kernel32")> _
Public Shared Function SetConsoleCtrlHandler(ByVal Handler As HandlerRoutine, _
ByVal Add As Boolean) As Boolean
End Function
' A delegate type to be used as the handler routine
' for SetConsoleCtrlHandler.
Delegate Function HandlerRoutine(ByVal CtrlType As CtrlTypes) As [Boolean]
' An enumerated type for the control messages
' sent to the handler routine.
Public Enum CtrlTypes
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT
CTRL_CLOSE_EVENT
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT
End Enum
End Class
Public Class MyApp
' A private static handler function in the MyApp class.
Shared Function Handler(ByVal CtrlType As MyWin32.CtrlTypes) As [Boolean]
Dim message As [String] = "This message should never be seen!"
' A select case to handle the event type.
Select Case CtrlType
Case MyWin32.CtrlTypes.CTRL_C_EVENT
message = "A CTRL_C_EVENT was raised by the user."
Case MyWin32.CtrlTypes.CTRL_BREAK_EVENT
message = "A CTRL_BREAK_EVENT was raised by the user."
Case MyWin32.CtrlTypes.CTRL_CLOSE_EVENT
message = "A CTRL_CLOSE_EVENT was raised by the user."
Case MyWin32.CtrlTypes.CTRL_LOGOFF_EVENT
message = "A CTRL_LOGOFF_EVENT was raised by the user."
Case MyWin32.CtrlTypes.CTRL_SHUTDOWN_EVENT
message = "A CTRL_SHUTDOWN_EVENT was raised by the user."
End Select
' Use interop to display a message for the type of event.
Console.WriteLine(message)
Return True
End Function
Public Shared Sub Main()
' Use interop to set a console control handler.
Dim hr As New MyWin32.HandlerRoutine(AddressOf Handler)
MyWin32.SetConsoleCtrlHandler(hr, True)
' Give the user some time to raise a few events.
Console.WriteLine("Waiting 30 seconds for console ctrl events...")
' The object hr is not referred to again.
' The garbage collector can detect that the object has no
' more managed references and might clean it up here while
' the unmanaged SetConsoleCtrlHandler method is still using it.
' Force a garbage collection to demonstrate how the hr
' object will be handled.
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
Thread.Sleep(30000)
' Display a message to the console when the unmanaged method
' has finished its work.
Console.WriteLine("Finished!")
' Call GC.KeepAlive(hr) at this point to maintain a reference to hr.
' This will prevent the garbage collector from collecting the
' object during the execution of the SetConsoleCtrlHandler method.
GC.KeepAlive(hr)
Console.Read()
End Sub
End Class
using System;
using System.Threading;
using System.Runtime.InteropServices;
// A simple class that exposes two static Win32 functions.
// One is a delegate type and the other is an enumerated type.
public class MyWin32
{
// Declare the SetConsoleCtrlHandler function
// as external and receiving a delegate.
[DllImport("Kernel32")]
public static extern Boolean SetConsoleCtrlHandler(HandlerRoutine Handler,
Boolean Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate Boolean HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
}
public class MyApp
{
// A private static handler function in the MyApp class.
static Boolean Handler(MyWin32.CtrlTypes CtrlType)
{
String message = "This message should never be seen!";
// A switch to handle the event type.
switch(CtrlType)
{
case MyWin32.CtrlTypes.CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case MyWin32.CtrlTypes.CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case MyWin32.CtrlTypes.CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case MyWin32.CtrlTypes.CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case MyWin32.CtrlTypes.CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
}
// Use interop to display a message for the type of event.
Console.WriteLine(message);
return true;
}
public static void Main()
{
// Use interop to set a console control handler.
MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
MyWin32.SetConsoleCtrlHandler(hr, true);
// Give the user some time to raise a few events.
Console.WriteLine("Waiting 30 seconds for console ctrl events...");
// The object hr is not referred to again.
// The garbage collector can detect that the object has no
// more managed references and might clean it up here while
// the unmanaged SetConsoleCtrlHandler method is still using it.
// Force a garbage collection to demonstrate how the hr
// object will be handled.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Thread.Sleep(30000);
// Display a message to the console when the unmanaged method
// has finished its work.
Console.WriteLine("Finished!");
// Call GC.KeepAlive(hr) at this point to maintain a reference to hr.
// This will prevent the garbage collector from collecting the
// object during the execution of the SetConsoleCtrlHandler method.
GC.KeepAlive(hr);
Console.Read();
}
}
using namespace System;
using namespace System::Threading;
using namespace System::Runtime::InteropServices;
// A simple class that exposes two static Win32 functions.
// One is a delegate type and the other is an enumerated type.
public ref class MyWin32
{
public:
// An enumerated type for the control messages sent to the handler routine.
enum class CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
};
delegate Boolean HandlerRoutine( // A delegate type to be used as the Handler Routine for SetConsoleCtrlHandler.
CtrlTypes CtrlType );
// Declare the SetConsoleCtrlHandler function as external and receiving a delegate.
[DllImport("Kernel32")]
static Boolean SetConsoleCtrlHandler( HandlerRoutine^ Handler, Boolean Add );
};
public ref class MyApp
{
private:
// A private static handler function in the MyApp class.
static Boolean Handler( MyWin32::CtrlTypes CtrlType )
{
String^ message = "This message should never be seen!";
// A switch to handle the event type.
switch ( CtrlType )
{
case MyWin32::CtrlTypes::CTRL_C_EVENT:
message = "A CTRL_C_EVENT was raised by the user.";
break;
case MyWin32::CtrlTypes::CTRL_BREAK_EVENT:
message = "A CTRL_BREAK_EVENT was raised by the user.";
break;
case MyWin32::CtrlTypes::CTRL_CLOSE_EVENT:
message = "A CTRL_CLOSE_EVENT was raised by the user.";
break;
case MyWin32::CtrlTypes::CTRL_LOGOFF_EVENT:
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
break;
case MyWin32::CtrlTypes::CTRL_SHUTDOWN_EVENT:
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
break;
}
// Use interop to display a message for the type of event.
Console::WriteLine( message );
return true;
}
public:
static void Test()
{
// Use interop to set a console control handler.
MyWin32::HandlerRoutine^ hr = gcnew MyWin32::HandlerRoutine( Handler );
MyWin32::SetConsoleCtrlHandler( hr, true );
// Give the user some time to raise a few events.
Console::WriteLine( "Waiting 30 seconds for console ctrl events..." );
// The Object hr is not referred to again.
// The garbage collector can detect that the object has no
// more managed references and might clean it up here while
// the unmanaged SetConsoleCtrlHandler method is still using it.
// Force a garbage collection to demonstrate how the hr
// object will be handled.
GC::Collect();
GC::WaitForPendingFinalizers();
GC::Collect();
Thread::Sleep( 30000 );
// Display a message to the console when the unmanaged method
// has finished its work.
Console::WriteLine( "Finished!" );
// Call GC::KeepAlive(hr) at this point to maintain a reference to hr.
// This will prevent the garbage collector from collecting the
// object during the execution of the SetConsoleCtrlHandler method.
GC::KeepAlive( hr );
}
};
int main()
{
MyApp::Test();
}
import System.* ;
import System.Threading.* ;
import System.Runtime.InteropServices.* ;
// A simple class that exposes two static Win32 functions.
// One is a delegate type and the other is an enumerated type.
public class MyWin32
{
// Declare the SetConsoleCtrlHandler function
// as external and receiving a delegate.
/** @attribute DllImport("Kernel32")
*/
public static native boolean SetConsoleCtrlHandler(HandlerRoutine Handler,
boolean Add);
/** @delegate
*/
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate boolean HandlerRoutine(int CtrlType);
// The control messages sent to the handler routine.
public static int CtrlTypes[] = new int[] { 0,/*CTRL_C_EVENT*/
1,/*CTRL_BREAK_EVENT*/
2,/*CTRL_CLOSE_EVENT*/
5,/*CTRL_LOGOFF_EVENT*/
6 /*CTRL_SHUTDOWN_EVENT*/ };
} //MyWin32
public class MyApp
{
// A private static handler function in the MyApp class.
static boolean Handler(int CtrlType)
{
String message = "This message should never be seen!";
if ( MyWin32.CtrlTypes[0] == CtrlType) {
message = "A CTRL_C_EVENT was raised by the user.";
}
if (MyWin32.CtrlTypes[1] == CtrlType) {
message = "A CTRL_BREAK_EVENT was raised by the user.";
}
if (MyWin32.CtrlTypes[2] == CtrlType) {
message = "A CTRL_CLOSE_EVENT was raised by the user.";
}
if ( MyWin32.CtrlTypes[3] == CtrlType) {
message = "A CTRL_LOGOFF_EVENT was raised by the user.";
}
if (MyWin32.CtrlTypes[4] == CtrlType) {
message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
}
// Use interop to display a message for the type of event.
Console.WriteLine(message);
return true ;
} //Handler
public static void main(String[] args)
{
// Use interop to set a console control handler.
MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
MyWin32.SetConsoleCtrlHandler(hr, true);
// Give the user some time to raise a few events.
Console.WriteLine("Waiting 30 seconds for console ctrl events...");
// The object hr is not referred to again.
// The garbage collector can detect that the object has no
// more managed references and might clean it up here while
// the unmanaged SetConsoleCtrlHandler method is still using it.
// Force a garbage collection to demonstrate how the hr
// object will be handled.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
try {
Thread.sleep(30000);
}
catch (InterruptedException e) {
}
// Display a message to the console when the unmanaged method
// has finished its work.
Console.WriteLine("Finished!");
// Call GC.KeepAlive(hr) at this point to maintain a reference to hr.
// This will prevent the garbage collector from collecting the
// object during the execution of the SetConsoleCtrlHandler method.
GC.KeepAlive(hr);
Console.Read();
} //main
} //MyApp
平台
Windows 98、Windows 2000 SP4、Windows CE、Windows Millennium Edition、Windows Mobile for Pocket PC、Windows Mobile for Smartphone、Windows Server 2003、Windows XP Media Center Edition、Windows XP Professional x64 Edition、Windows XP SP2、Windows XP Starter Edition
.NET Framework 并不是对每个平台的所有版本都提供支持。有关受支持版本的列表,请参见系统要求。
版本信息
.NET Framework
受以下版本支持:2.0、1.1、1.0
.NET Compact Framework
受以下版本支持:2.0、1.0