Marshal.PtrToStructure丟出System.AccessViolationException 嘗試讀取或寫入受保護的記憶體

英文錯誤訊息是System.AccessViolationException was unhandled

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

網路上找到關於這錯誤的描述:

AccessViolationException 是一種作業系統層級的嚴重例外狀況,因為侵犯到了其他行程的記憶體,所以程式必須強制關閉。

當程式碼嘗試讀取或寫入尚未配置或其不具存取權的記憶體時,Unmanaged Unsafe 程式碼會發生存取違規。原因通常是指標有錯誤值。並非所有透過錯誤指標讀取或寫入的動作都會導致存取違規,因此存取違規通常指出已透過錯誤指標執行多次讀取或寫入,且該記憶體可能已毀損。因此,存取違規幾乎都會指出嚴重的程式設計錯誤。在 .NET Framework 2.0 版中,AccessViolationException 會明確地辨識這些嚴重錯誤。

Marshal.PtrToStructure丟出這種什麼記憶體的相關錯誤,讓我整個追錯誤的方向都錯掉,其實若原來的c++的char*有亂碼,轉回成c# 的string就會有問題了


近期在簡體中文的Server2012 R2 Datacenteros使用c#叫用winpcappcap_findalldevs函數時,丟出了上述的例外,這個函數是幫你抓出目前所有的網卡。

先是在 System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType) catch住,

最後更底層是System.Runtime.InteropServices.Marshal.PtrToStructureHelper(IntPtr ptr, Object structure, Boolean allowValueClasses) 丟出。

如果叫用pcap_findalldevs丟出問題。 一套知名的抓封包軟體wireshark應該也會同樣有錯。

沒有,wireshark有正常顯示出網卡,因wiresharkc++寫的,不需再經過PtrToStructureHelper做資料轉換。在網路上另外找一套c#的抓封包軟體,它一樣不能執行。

最後定位到.netPtrToStructure winpcappcap_findalldevs這兩個應該就是問題點。

要嘛一開始winpcap給的指標就錯了,要嘛是.net在把指標轉struct時錯掉。


可以嘗試的解決方法

1.看你的網卡名稱是否含有中文,可用dos指令查看:

wmic path Win32_NetworkAdapterConfiguration get caption,description

中文網卡名稱寫到NDIS層,不曉得是Driver的關係還是怎樣,會變成是亂碼,早在NDIS層時,那個網卡名稱就是亂碼了,所以叫用.netPtrToStructure就轉型失敗。

若是VM就換另一張網卡試試。或是安裝原廠Driver,不要用windows內建的。

上述的dos指令,查出來有個編號,可到機碼

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318},依照剛才查到有中文的網卡編號,找到DriverDesc的欄位,去改掉中文,改掉後記得重開機才會生效。總之想辦法把網卡名稱弄成英文的即可。

2.其實只要透過mono執行,就會發現那個中文的網卡名稱會變成空白,其實是null而沒印出。
mono與.net對於PtrToStructure的處理不相同。mono對於從c++的char*轉c#string有問題的話會變成null。
mono雖然有原始碼,但PtrToStructure遇到從c++的char*轉c#string的詳細作法,我目前的程度還是看不懂在搞些什麼。.net則是轉失敗就丟出例外,可能要籍由刪去法,把原struct的部份欄位註解掉看是struct的哪一個欄位造成轉型失敗。不過還是換用mono跑一下比較快,哪一個是空白的,可能就是該欄位有轉型的問題。我想自己要養成一個習慣.net跑有問題,就用mono跑。

3.剛好winpcap的pcap_findalldevs是c++的,你可以寫c++呼叫看看,看一下c++叫用有沒有問題,結果c++是正常的,看它回傳的指標,用記憶體相關工具看,也都有內容,把網卡名稱印出,結果c++看見有亂碼,這是我才定位到,有可能是網卡名稱亂碼的問題造成的。

4.通常 這樣 的 Exception 多數 是在 C++ 寫的程式才會見到,檢查是否在c#中叫用了 UnManaged DLL

5.程式的權限太低

6.驅動程式問題,更新一下晶片組與相關硬體的Driver,像是寫網路相關的,就更新網卡。

7.不要用ad帳號登入,改用本機登入.

8.關掉DEP功能,資料執行防止

dep

若上圖的方法無法關掉,要改下指令

win7以上 下此指令bcdedit.exe /set {current} nx AlwaysOff

若想改回預設值下:bcdedit.exe /set {current} nx OptIn

9.查事件檢示器是否有相關錯誤訊息

10. pcap_findalldevs回傳的指標,可以用一些記憶體工具去看見該記憶體的位址是否有內容 HxD Hex Editor 這工具,不過我是用GameMaster, 對於此指標也可以試試用Marshal.ReadInt32 讀出看是否有東西。我猜PtrToStructureHelper最後應該也是叫用ReadInt32來作轉換的。猜的因這個ReadInt32在傳進來的指標,會先轉型,轉型失敗變成null,若運算到null,一樣是丟出 AccessViolationException

11.若你一樣是寫網路相關軟體而跳出這錯誤,可以嘗試以管理員權限運行:netsh winsock reset netsh int ip reset 這兩個指令, 當執行完winsock的命令重啟計算機後,IP會消失,所以需要重新配置IP

12.若你一樣是叫用winpcap出問題,我寫這篇文章時,winpcap最新是4.1.3,可能是沒有支援server2012datacenter,你可以嘗試裝一套叫win10pcap的軟體

13 . 強制中止掃毒軟體 , 些掃毒軟體officescan以服務型式在跑,雖然沒有在右下角看見但仍在跑 ,要停掉整個officescan的服務,先排除掃毒軟體的影響,微軟預設也有掃毒,也一併先暫停。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

你正使用 WordPress.com 帳號留言。 登出 / 變更 )

Twitter picture

你正使用 Twitter 帳號留言。 登出 / 變更 )

Facebook照片

你正使用 Facebook 帳號留言。 登出 / 變更 )

Google+ photo

你正使用 Google+ 帳號留言。 登出 / 變更 )

連結到 %s