2011-03-16 57 views
3

我有一個Visual Studio 2008 C#.NET 3.5應用程序,它將P /調用接受文件句柄作爲參數的本地方法。最初,我只是使用FileStream.SafeFileHandle.DangerousGetHandle()來獲取文件句柄。但是,在開啓FX COP後,我得到了一個CA2001的警告。所以,經過一番研究,我發現了「約束執行區域」。這對我來說是新的,我還沒有看到關於它的大量信息。我希望有更多經驗的人可以看看並確認我已經正確地做了這件事。使用約束執行區域

class MyClass 
{ 
    public static bool Write(string filename) 
    { 
     using (var fs = new System.IO.FileStream(filename, 
      System.IO.FileMode.Create, 
      System.IO.FileAccess.Write, 
      System.IO.FileShare.None)) 
     { 
      bool got_handle; 
      bool result; 

      System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); 
      try { } 
      finally 
      { 
       fs.SafeFileHandle.DangerousAddRef(ref got_handle); 
       result = NativeMethods.Foo(fs.SafeFileHandle.DangerousGetHandle()); 
       if (got_handle) 
        fs.SafeFileHandle.DangerousRelease(); 
      } 

      return result; 
     } 
    } 
} 

internal sealed class NativeMethods 
{ 
    [DllImport("mylib.dll", 
     EntryPoint = "Foo", 
     CallingConvention = CallingConvention.StdCall, 
     CharSet = CharSet.Unicode, 
     ExactSpelling = true, 
     SetLastError = true)] 
    public static extern bool Foo(IntPtr hFile); 
} 

感謝, PaulH

+4

這是不行的,一個CER解決了一個不同的問題。 CER能夠抑制可能會影響終結器線程的異常。這在SQL Server中很重要,但它是您程序中的一項*責任*。當關鍵任務SQL Server實例由於用戶代碼鬆動而失敗時,Microsoft將獲得支持電話。他們不需要它,他們不能修復該代碼。你可以修復你的代碼,例外告訴你需要修復什麼。不要隱藏它們。 – 2011-03-16 21:15:51

+0

那麼我會忽略CA2001的警告嗎?還是有更好的方式將文件句柄傳遞給本地函數? – PaulH 2011-03-16 21:28:30

+3

是的。這是微軟員工發出處理回收攻擊可能性的警告。好人,但有點近視,他們認爲他們的問題也是我們的問題。我們希望。一個IntPtr是一個體面的方式來存儲一個句柄,該句柄不會引起任何投訴。如果您的代碼在一百萬臺桌面上運行,請不要採取此建議。 – 2011-03-16 21:44:16

回答

6

你在這裏做了幾件事。

  1. 在finally代碼塊中執行代碼以防止執行安全代碼時的ThreadAbortExceptions。

  2. 在try/finally技巧之前,您可以調用PrepareConstrainedRegions,它除了檢查是否存在足夠的線程堆棧空間以確保至少可以進行一些方法調用以便您的安全代碼不會受到警戒一個StackOverFlowException。

所以是的,你的代碼看起來像它可以得到的安全。在關於CERs的官方docu中,有人表示CLR確實認可了這個try/finally塊,並採取了其他措施。從我所看到的情況來看,除了在CER代碼運行之後OutOfMemoryExceptions也被延遲之外,沒有太大區別。

爲了確保您的代碼符合您的期望,您應該爲這些事情創建測試。

  • 堆棧用盡
  • 內存
  • Thread.Abort的

編寫可靠的代碼是真的很難,甚至大部分的BCL類的都沒有hardened這樣的事喬·達菲說。即使你的代碼沒有失敗,BCL代碼也可以。在BCL代碼的主要部分能夠以明確定義的方式應對這些極端情況之前,您不會獲得太多額外的好處。

你的, 阿洛伊斯·克勞斯

0

我不明白你怎麼能有任何問題可言,除非你生成try塊中的例外。

  • finally部分中的代碼是否爲原子?
  • NativeMethods.Foo()有沒有機會泄漏內存或中止一個線程?
2

來處理它忠實地安全的方式是傳遞的SafeHandle到位IntPtr的參考 - 的P /調用層的SafeHandle感知,並會自動進行這項工作給你。唯一的例外是當你調用本地API來關閉你的句柄時,因爲SafeHandle在你使用它時被丟棄。

例如:

[DllImport("qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
internal static extern bool QOSCreateHandle(ref QosVersion version, out QosSafeHandle qosHandle); 

[DllImport("qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
internal static extern bool QOSCloseHandle(IntPtr qosHandle); 

[DllImport("qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)] 
internal static extern bool QOSAddSocketToFlow(
    QosSafeHandle qosHandle, 
    IntPtr socket, 
    byte[] destAddr, 
    QosTrafficType trafficType, 
    QosFlowFlags flags, 
    ref uint flowId 
); 


/// <summary> 
/// Safely stores a handle to the QWave QoS win32 API and ensures the handle is properly 
/// closed when all references to the handle have been garbage collected. 
/// </summary> 
public class QosSafeHandle : SafeHandle 
{ 
    /// <summary> 
    /// Initializes a new instance of the QosSafeHandle class. 
    /// </summary> 
    public QosSafeHandle() : 
     base(IntPtr.Zero, true) 
    { 
    } 

    /// <summary> 
    /// Whether or not the handle is invalid. 
    /// </summary> 
    public override bool IsInvalid 
    { 
     get { return this.handle == IntPtr.Zero; } 
    } 

    /// <summary> 
    /// Releases the Qos API instance handle. 
    /// </summary> 
    /// <returns></returns> 
    protected override bool ReleaseHandle() 
    { 
     QosNativeMethods.QOSCloseHandle(this.handle); 
     return true; 
    } 
} 

然而,這可能不是如果的SafeHandle實現正在作爲一個結構參數傳遞,或者是可能的,如果基礎句柄比一個IntPtr更多。例如,Win32 SSPI api使用兩個IntPtrs的句柄。要處理這種情況,您必須手動執行CER。

您的CER使用不正確。 DangerousAddRef仍可能失敗。以下是他們的淨源使用微軟的模式:

public static bool Write(string filename) 
{ 
    using(var fs = new System.IO.FileStream(filename, 
     System.IO.FileMode.Create, 
     System.IO.FileAccess.Write, 
     System.IO.FileShare.None)) 
    { 
     bool got_handle; 
     bool result; 

     // The CER is here to ensure that reference counting on fs.SafeFileHandle is never 
     // corrupted. 
     RuntimeHelpers.PrepareConstrainedRegions(); 
     try 
     { 
      fs.SafeFileHandle.DangerousAddRef(ref got_handle); 
     } 
     catch(Exception e) 
     { 
      if(got_handle) 
      { 
       fs.SafeFileHandle.DangerousRelease(); 
      } 

      got_handle = false; 

      throw; 
     } 
     finally 
     { 
      if(got_handle) 
      { 
       result = NativeMethods.Foo(fs.SafeFileHandle.DangerousGetHandle()); 

       fs.SafeFileHandle.DangerousRelease(); 
      } 
     } 

     return result; 
    } 
} 

你可以看到在Microsoft參考源 - 影響這種模式見_SafeNetHandle.cs,線2071