在windows server 2008上,.net 应用程序调用c++ dll时会发生崩溃

来源:互联网 发布:rtx mac 2009 编辑:程序博客网 时间:2024/05/26 05:52

本文来自http://blogs.msdn.com/asiatech/default.aspx 上我写的一篇文章:

 

.NET application may crash when calling function from native C++ dll

 

Symptom

===========

Customer built an ASP.NET web application using Visual Studio 2008 on Windows 2008. In the source code, it called a function which was imported from native c++ dll:

 

[The source code looks like below]

 

public partial class _Default : System.Web.UI.Page

 {

        protected void Page_Load(object sender, EventArgs e)

        {

        }

        [System.Runtime.InteropServices.DllImport("PinvokeLib.dll", EntryPoint = " TestStringAsResult", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern string TestStringAsResult();

                ……

        protected void Button_Click(object sender, EventArgs e)

        {

            string str = TestStringAsResult();

        }

}

 

Above code seems working fine during debugging when "Use Visual Studio Development Server" in Web properties. However, when choose the web properties to “Use IIS Web Server”,  the web page seems hang and returns server internal error message. This issue will not occur on Windows 2003.

 

Troubleshoot

===========

We got the source code of native c++ dll and the related method was written like below:

 

extern "C" PINVOKELIB_API char * TestStringAsResult()

{

   return "Test String";

}

 

We rebuilt the native dll and web application with VS 2008 on a test Windows 2008 server. When we ran the web application, w3wp.exe crashed and WerFault.exe (Windows Error Reporting service) showed up trying to record logs. In the application event log, we found 0xc0000374 exception:

Log Name:      Application

Source:        Application Error

Date:          12/12/2009 3:37:00 AM

Event ID:      1000

Task Category: (100)

Level:         Error

Keywords:      Classic

User:          N/A

Computer:      182462M

Description:

Faulting application w3wp.exe, version 7.0.6001.18000, time stamp 0x47919413, faulting module ntdll.dll, version 6.0.6001.18000, time stamp 0x4791a7a6, exception code 0xc0000374, fault offset 0x000b015d, process id 0x10e0, application start time 0x01ca7b1f684ff683.

 

0xc0000374 indicates that there is heap corruption issue existed. We used DebugDiag to catch w3wp.exe crash dump file and found below call stack:

 

0:023> kL200

ChildEBP RetAddr 

052deaf4 77df0d68 ntdll!RtlReportCriticalFailure+0x5b

052deb04 77df0e56 ntdll!RtlpReportHeapFailure+0x21

052deb38 77db0531 ntdll!RtlpLogHeapFailure+0xa1

052deb64 7680c56f ntdll!RtlFreeHeap+0x60

052deb78 77c3dc2c kernel32!HeapFree+0x14

052deb8c 77c3dc53 ole32!CRetailMalloc_Free+0x1c

052deb9c 7a0dce06 ole32!CoTaskMemFree+0x13

052debac 7a0e06c2 mscorwks!DefaultMarshalOverrides<CSTRMarshalerBase>::ReturnCLRFromNative+0x35

052dede8 79e957c3 mscorwks!RunML+0x949

052dee68 79e956b7 mscorwks!NDirectGenericStubPostCall+0x194

052deef4 79e85686 mscorwks!NDirectGenericStubReturnFromCall+0x1f

052deefc 051f0678 mscorwks!NDirectSlimStubWorker2+0x13d

WARNING: Frame IP not in any known module. Following frames may be wrong.

052def38 04836dee 0x51f0678

052def50 04836fdc System_Web_ni+0x276dee

00000000 00000000 System_Web_ni+0x276fdc

 

0:023> !clrstack

OS Thread Id: 0xb4c (23)

ESP       EIP    

052def14 77df015d [NDirectMethodFrameGeneric: 052def14] WebApplication3._Default.__PBExternal_fnreturnenstra()

052def24 051f0678 WebApplication3._Default.Button1_Click(System.Object, System.EventArgs)

052def44 04836dee System.Web.UI.WebControls.Button.OnClick(System.EventArgs)

052def58 04836fdc System.Web.UI.WebControls.Button.RaisePostBackEvent(System.String)

……

052df2ec 04663072 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)

052df2f8 0466157b System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

 

We could see that CoTaskMemFree was called to free unmanaged memory in this situation. The problem is due to CLR use different protocol to allocate memory and free memory between managed code and unmanaged code. According to below MSDN article, when CLR marshals unmanaged memory to the string, it will always invoke CoTaskMemFree method to free the unmanaged memory:

“Memory Management with the Interop Marshaler”

<http://msdn.microsoft.com/en-us/library/f1cf4kkz(VS.80).aspx>

 

Since the unmanaged memory is not allocate by CoTaskMemAlloc, it will crashed by

ntdll!RtlpLowFragHeapFree checking. This issue will also happen if we use NEW or malloc function to allocate memory in C++ dll. Below code will fail as well:

 

TCHAR* WINAPI TestStringAsResult()

{

TCHAR *ret;

ret = new TCHAR[10];

wcscpy(ret, L"test");

return ret;

}

 

To fix this problem, we need to explicitly call CoTaskMemAlloc method to allocate memory in native dll:

 

#include <objbase.h>

……

extern "C" PINVOKELIB_API char * TestStringAsResult()

{

    STRSAFE_LPWSTR result = (STRSAFE_LPWSTR)CoTaskMemAlloc( 64 );

    StringCchCopy( result, sizeof(result) , (STRSAFE_LPWSTR)"This is return value" );

    return (char *) result;

}

 

If the unmanaged memory is not allocated with the CoTaskMemAlloc method and could not be changed, we must use an IntPtr and free the memory manually using the appropriate method. The declaration in c# should be changed from:

 

[System.Runtime.InteropServices.DllImport("PinvokeLib.dll", EntryPoint = " TestStringAsResult", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern string TestStringAsResult();

 

To:

 

[System.Runtime.InteropServices.DllImport("PinvokeLib.dll", EntryPoint = " TestStringAsResult", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]

        public static extern IntPtr TestStringAsResult();

 

Another Question

===========

Why this problem doesn’t happen under Win2003?

It is because that Windows Vista and Win2008 support new heap verification feature.

“HeapEnableTerminationOnCorruption”

<http://msdn.microsoft.com/en-us/library/aa366705.aspx>

 

It enables the terminate-on-corruption feature. If the heap manager detects an error in any heap used by the process, it calls the Windows Error Reporting service and terminates the process. So the heap corruption issue will be reported by Win2008 or Vista explicitly while Win2003 OS doesn’t report this though the heap corruption indeed exsits.

 

More Information

===========

Below two articles provides the sample code of calling native c++ dll function within c#:

 

“Strings Sample”

http://msdn.microsoft.com/en-us/library/e765dyyy(VS.80).aspx

 

“PinvokeLib.dll”

http://msdn.microsoft.com/en-us/library/as6wyhwt(VS.80).aspx

 

 

References

===========

Marshaling between Managed and Unmanaged Code

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx

Default Marshaling for Strings

http://msdn.microsoft.com/en-us/library/s9ts558h(VS.80).aspx

Default Marshaling for Arrays

http://msdn.microsoft.com/en-us/library/z6cfh6e6(VS.80).aspx

Memory Management with the Interop Marshaler

http://msdn.microsoft.com/en-us/library/f1cf4kkz(VS.80).aspx

Regards,

Hanson Wang

原创粉丝点击