一个成功传递引用类型参数到非托管环境的例子
在写一些对操作系统依赖性很强的东西时,调用windows API是很经常的。调用API就有托管与非托管交互的问题发生。非托管API返回的对象在托管环境下用IntPtr对象可以接收并传递给其它需要的非托管API,而非托管返回的结构数据可以通过在托管里面定义结构类型来接收使用。但是如果需要把托管下的对象传送给非托管API作为参数,然后再取回,就需要用到GCHandle结构了。
看代码:
代码是调用了iocp的其中几个api(什么是iocp和为什么这么调用,日后再说),代码里面:
public void PostEvent(object iValue) 和 public object getEvent()两个方法,分别就是把对象传递给非托管和从非托管取回对象的方法。其中比较关键的是GCHandle结构所起的作用。它提供从非托管内存访问托管对象的方法。
就是通过它来完成把托管对象传递给非托管环境的,具体的方法在代码可以很清楚看到。下面是测试代码:
整个列子就是这么一回事。这是1.1使用的办法。其它更高版本还没有深入接触。如果谁有其他方法,欢迎分享。
看代码:
1using System;
2using System.Threading;
3using System.Runtime.InteropServices;
4
5 /// <summary>
6 /// Win32IOCPSample 的摘要说明。
7 /// iocp的示范列子,一个成功传递引用类型参数到非托管环境的例子
8 /// </summary>
9 public class Win32IOCPSample
10 {
11 public Win32IOCPSample(int maxThread)
12 {
13 try
14 {
15 // Create an IO Completion Port for Thread Pool use
16 GetHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, IntPtr.Zero, maxThread);
17
18 // Test to make sure the IO Completion Port was created
19 if (GetHandle == 0)
20 throw new Exception("Unable To Create IO Completion Port");
21 }
22 catch
23 {
24 throw new Exception("Unhandled Exception");
25 }
26 }
27
28 [DllImport("Kernel32")]
29 private static extern void CloseHandle(UInt32 handle);
30 [DllImport("Kernel32")]
31 private static extern UInt32 CreateIoCompletionPort(UInt32 fileHandle, UInt32 existingCompletionPort,IntPtr completionKey, int numberOfConcurrentThreads);
32 [DllImport("Kernel32")]
33 private static extern bool PostQueuedCompletionStatus(UInt32 completionPort, int numberOfBytesTransferred,IntPtr completionKey, IntPtr overlapped);
34 [DllImport("Kernel32")]
35 private static extern bool GetQueuedCompletionStatus(UInt32 completionPort, ref int numberOfBytes,ref IntPtr completionKey, ref IntPtr overlapped,UInt32 milliseconds);
36
37 private const UInt32 INVALID_HANDLE_VALUE = 0xffffffff;
38 private const UInt32 INIFINITE = 0xffffffff;
39 public static readonly Int32 SHUTDOWN_IOCPTHREAD = 0x7fffffff;
40 public delegate void USER_FUNCTION(object iValue);
41 private UInt32 m_hHandle;
42 private UInt32 GetHandle { get { return m_hHandle; } set { m_hHandle = value; } }
43
44 private Boolean m_bDisposeFlag;
45
46 /// <summary>
47 /// 释放标志
48 /// </summary>
49 private Boolean IsDisposed { get { return m_bDisposeFlag; } set { m_bDisposeFlag = value; } }
50 ~Win32IOCPSample()
51 {
52 if (!IsDisposed)
53 Dispose();
54 }
55
56 public void Dispose()
57 {
58 unsafe
59 {
60 // Close the IOCP Handle
61 CloseHandle(GetHandle);
62 }
63 }
64 public object getEvent()
65 {
66 int uiNumberOfBytes=0;
67 object obj=null;
68 try
69 {
70 //声明接收对象的指针
71 IntPtr key=IntPtr.Zero;
72 IntPtr ovp=IntPtr.Zero;
73
74 // Wait for an event
75 //取回对象指针
76 if(GetQueuedCompletionStatus(GetHandle, ref uiNumberOfBytes, ref key, ref ovp, INIFINITE)==true)
77 {
78
79 if(key!=IntPtr.Zero && ovp!=IntPtr.Zero)
80 {
81 //把指针转换会GCHandle对象
82 GCHandle gcKey=(GCHandle)key;
83 GCHandle gcOvp=(GCHandle)ovp;
84 // Was this thread told to shutdown
85 if (ovp!=IntPtr.Zero && gcOvp.Target.ToString().Equals(SHUTDOWN_IOCPTHREAD.ToString()))
86 {
87 gcOvp.Free();
88 gcKey.Free();
89
90 return obj;
91 }
92 //通过GCHandle对象取得其所表示的对象
93 obj=gcOvp.Target;
94
95 gcOvp.Free();
96 gcKey.Free();
97 }
98 }
99 }
100 catch(Exception ex)
101 {
102 throw ex;
103 }
104 return obj;
105 }
106
107 public void PostEvent(object iValue)
108 {
109 try
110 {
111 // Only add work if we are not disposing
112 if (IsDisposed == false)
113 {
114
115 int i=0;
116 //取托管对象的句柄
117 GCHandle gcValue=GCHandle.Alloc(iValue);
118 GCHandle gcKey=GCHandle.Alloc(i);
119 // Post an event into the IOCP Thread Pool
120 //把对象句柄作为参数传递给api
121 PostQueuedCompletionStatus(GetHandle, 4, (IntPtr)gcKey, (IntPtr)gcValue);
122 }
123 }
124 catch (Exception e)
125 {
126 throw e;
127 }
128 catch
129 {
130 throw new Exception("Unhandled Exception");
131 }
132 }
133 }
2using System.Threading;
3using System.Runtime.InteropServices;
4
5 /// <summary>
6 /// Win32IOCPSample 的摘要说明。
7 /// iocp的示范列子,一个成功传递引用类型参数到非托管环境的例子
8 /// </summary>
9 public class Win32IOCPSample
10 {
11 public Win32IOCPSample(int maxThread)
12 {
13 try
14 {
15 // Create an IO Completion Port for Thread Pool use
16 GetHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, IntPtr.Zero, maxThread);
17
18 // Test to make sure the IO Completion Port was created
19 if (GetHandle == 0)
20 throw new Exception("Unable To Create IO Completion Port");
21 }
22 catch
23 {
24 throw new Exception("Unhandled Exception");
25 }
26 }
27
28 [DllImport("Kernel32")]
29 private static extern void CloseHandle(UInt32 handle);
30 [DllImport("Kernel32")]
31 private static extern UInt32 CreateIoCompletionPort(UInt32 fileHandle, UInt32 existingCompletionPort,IntPtr completionKey, int numberOfConcurrentThreads);
32 [DllImport("Kernel32")]
33 private static extern bool PostQueuedCompletionStatus(UInt32 completionPort, int numberOfBytesTransferred,IntPtr completionKey, IntPtr overlapped);
34 [DllImport("Kernel32")]
35 private static extern bool GetQueuedCompletionStatus(UInt32 completionPort, ref int numberOfBytes,ref IntPtr completionKey, ref IntPtr overlapped,UInt32 milliseconds);
36
37 private const UInt32 INVALID_HANDLE_VALUE = 0xffffffff;
38 private const UInt32 INIFINITE = 0xffffffff;
39 public static readonly Int32 SHUTDOWN_IOCPTHREAD = 0x7fffffff;
40 public delegate void USER_FUNCTION(object iValue);
41 private UInt32 m_hHandle;
42 private UInt32 GetHandle { get { return m_hHandle; } set { m_hHandle = value; } }
43
44 private Boolean m_bDisposeFlag;
45
46 /// <summary>
47 /// 释放标志
48 /// </summary>
49 private Boolean IsDisposed { get { return m_bDisposeFlag; } set { m_bDisposeFlag = value; } }
50 ~Win32IOCPSample()
51 {
52 if (!IsDisposed)
53 Dispose();
54 }
55
56 public void Dispose()
57 {
58 unsafe
59 {
60 // Close the IOCP Handle
61 CloseHandle(GetHandle);
62 }
63 }
64 public object getEvent()
65 {
66 int uiNumberOfBytes=0;
67 object obj=null;
68 try
69 {
70 //声明接收对象的指针
71 IntPtr key=IntPtr.Zero;
72 IntPtr ovp=IntPtr.Zero;
73
74 // Wait for an event
75 //取回对象指针
76 if(GetQueuedCompletionStatus(GetHandle, ref uiNumberOfBytes, ref key, ref ovp, INIFINITE)==true)
77 {
78
79 if(key!=IntPtr.Zero && ovp!=IntPtr.Zero)
80 {
81 //把指针转换会GCHandle对象
82 GCHandle gcKey=(GCHandle)key;
83 GCHandle gcOvp=(GCHandle)ovp;
84 // Was this thread told to shutdown
85 if (ovp!=IntPtr.Zero && gcOvp.Target.ToString().Equals(SHUTDOWN_IOCPTHREAD.ToString()))
86 {
87 gcOvp.Free();
88 gcKey.Free();
89
90 return obj;
91 }
92 //通过GCHandle对象取得其所表示的对象
93 obj=gcOvp.Target;
94
95 gcOvp.Free();
96 gcKey.Free();
97 }
98 }
99 }
100 catch(Exception ex)
101 {
102 throw ex;
103 }
104 return obj;
105 }
106
107 public void PostEvent(object iValue)
108 {
109 try
110 {
111 // Only add work if we are not disposing
112 if (IsDisposed == false)
113 {
114
115 int i=0;
116 //取托管对象的句柄
117 GCHandle gcValue=GCHandle.Alloc(iValue);
118 GCHandle gcKey=GCHandle.Alloc(i);
119 // Post an event into the IOCP Thread Pool
120 //把对象句柄作为参数传递给api
121 PostQueuedCompletionStatus(GetHandle, 4, (IntPtr)gcKey, (IntPtr)gcValue);
122 }
123 }
124 catch (Exception e)
125 {
126 throw e;
127 }
128 catch
129 {
130 throw new Exception("Unhandled Exception");
131 }
132 }
133 }
代码是调用了iocp的其中几个api(什么是iocp和为什么这么调用,日后再说),代码里面:
public void PostEvent(object iValue) 和 public object getEvent()两个方法,分别就是把对象传递给非托管和从非托管取回对象的方法。其中比较关键的是GCHandle结构所起的作用。它提供从非托管内存访问托管对象的方法。
就是通过它来完成把托管对象传递给非托管环境的,具体的方法在代码可以很清楚看到。下面是测试代码:
1static Win32IOCPSample w;
2public static void Main(string[] args)
3{
4
5 w=new Win32IOCPSample(2);
6
7 for(int i=0; i<5;i++)
8 {
9 Thread t=new Thread(new ThreadStart(worker));
10 t.IsBackground=true;
11 t.Start();
12 }
13
14 for(int i =1;i<20;i++)
15 {
16 w.PostEvent(ExTimer.GetExactNow());
17 }
18
19 for(int i =0;i<5;i++)
20 {
21 w.PostEvent(Win32IOCPSample.SHUTDOWN_IOCPTHREAD);
22 }
23 Thread.Sleep(100);
24
25
26 Console.ReadLine();
27 w.Dispose();
28}
29
30static public void worker()
31{
32 while(true)
33 {
34 try
35 {
36 object obj=w.getEvent();
37
38 Console.Write(qp.Duration(1).ToString()+"\r\n");
39 if(obj==null) break;
40
41 Console.WriteLine("Value: {0}", obj.ToString());
42
43 Thread.Sleep(500);
44 }
45 catch(Exception ex)
46 {
47 Console.WriteLine("ex: {0}", ex.ToString());
48 }
49 }
50}
2public static void Main(string[] args)
3{
4
5 w=new Win32IOCPSample(2);
6
7 for(int i=0; i<5;i++)
8 {
9 Thread t=new Thread(new ThreadStart(worker));
10 t.IsBackground=true;
11 t.Start();
12 }
13
14 for(int i =1;i<20;i++)
15 {
16 w.PostEvent(ExTimer.GetExactNow());
17 }
18
19 for(int i =0;i<5;i++)
20 {
21 w.PostEvent(Win32IOCPSample.SHUTDOWN_IOCPTHREAD);
22 }
23 Thread.Sleep(100);
24
25
26 Console.ReadLine();
27 w.Dispose();
28}
29
30static public void worker()
31{
32 while(true)
33 {
34 try
35 {
36 object obj=w.getEvent();
37
38 Console.Write(qp.Duration(1).ToString()+"\r\n");
39 if(obj==null) break;
40
41 Console.WriteLine("Value: {0}", obj.ToString());
42
43 Thread.Sleep(500);
44 }
45 catch(Exception ex)
46 {
47 Console.WriteLine("ex: {0}", ex.ToString());
48 }
49 }
50}
整个列子就是这么一回事。这是1.1使用的办法。其它更高版本还没有深入接触。如果谁有其他方法,欢迎分享。