P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
.Net程序调试
P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-13, 15:30 下午
情况描述:
我在win32 dll 项目中封装了段asm代码调用另一个dll中的函数. 在我的win32 dll项目中公开的函数声明如下:
1 extern "C" _declspec(dllexport) void No(const char* source, char* result)
2 {
3 _asm
4 {
5 mov eax, source
6 push eax
7 mov eax, result
8 push eax
9 call pFun //pFun 是我从另一个dll中用GetProcAddress函数提取的函数地址,这个函数规则是被调用者清除栈;
10 //下面使用eax寄存器,向调用者返回结果,而实际上我们的函数声明并没有返值,这种调用在我的winxp上调用是成功的.
11 //但是在另一台win7系统中,当这此函数返回时却总是失败,报异常.
12 //由于我的最终项目是wcf项目托管于iis7下,当发生异常时,提示ntdll堆被破坏.....
13 mov eax,dword ptr [result]
14 add eax,1
15 mov dword ptr [result],eax
16 }
17 }
下面是我在C#项目中声明的函数导入代码;
[DllImport("No.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void No(string strArg0, byte[] strArg1); //此种调用方式,在winxp,win7系统都可以正常运行,结果是通过strArg1传递的.
//注意:下面代码声明了string类型的返回值,也就是获取eax返回的地址.
private static extern string No(string strArg0, byte[] strArg1);
此种调用方式不能在win7系统下运行,但也是仅仅是第一次报异常,当第二次调用此函数时,就可以通过返回值取到结果.
我的问题是为什么这种方式会报异常呢?
难道win7下CLR jit编译做了更严格的内存检查吗?
或者与JIT没有关系,而是由于系统api的低层函数有更多的检查呢?
我目前还没有找到这个问题所在,但我非常有兴趣....想知道原因,与大家共同讨论一次吧.
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-13, 15:51 下午
张老师,你有Intel 指令集参考手册 的中文版pdf吗?
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
sPhinX
2010-10-13, 17:44 下午
中文版......,这个东西硬邦邦的,翻译成中文看不懂的还是看不懂,还不如看原文准确,应该没有人翻译这个吧?
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
格蠹老雷
2010-10-13, 23:52 下午
我也有兴趣, :-)要看一下哪里出异常, 把能重现问题的执行文件发给我一份,我来看一下
关于IA32 SDM,国内的确有个兴趣小组翻译过, 搜索一下应该可以搜到, 记得本站的一个朋友参与过这个翻译
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-14, 09:07 上午
我目前只有这本.还算不错
英特尔64和IA-32架构软件开发人员的手册卷3 中文版-部分
花了一点时间 针对这次的问题写了一个Demo.
附件里是Release版的测试程序.
可以在xp下正常运行,但是放到win7下会出错.
下面的贴子我会贴上源码工程,可以在调试模式(F5)运行能发现异常的报告,但是以非调试(Ctrl+F5)就不会提示异常可以正常运行.
非常有意思.
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-14, 10:48 上午
源码工程,包含win32dll与托管项目,我已设好依赖项.
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
格蠹老雷
2010-10-14, 22:45 下午
原因是把返回值声明成了string,即:
private static extern string TestFunA(byte[] result);
对于PInvoke函数,返回值如果声明为string,那么CLR便认为,被调用的函数是使用CoTaskMemAlloc分配的内存,而且需要CLR来释放这个内存。异常正是释放返回的内存时发生的。
声明可以改为:
[DllImport("TestedDLL.dll", EntryPoint = "TestFun", CallingConvention = CallingConvention.Cdecl)]
private static extern System.IntPtr TestFunB(byte[] result);
对应的函数可以改为:
var result = new byte[36];
IntPtr returnValue = TestFunB(result);
MessageBox.Show("result:" + System.Text.Encoding.Default.GetString(result));
MessageBox.Show("eax: " + Marshal.PtrToStringAnsi(returnValue));
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-15, 09:28 上午
张老师,您是通过调试的手段发现这个问题的还是之前看过关于clr平台调用时内存分配规则的描述呢?
msdn上有一些文章描述了您说的这种情况,而且还有些是与win具体版本有关,比如vista也会出问题.
下面是调试的调用栈:
ntdll.dll!_DbgBreakPoint@0()
ntdll.dll!_RtlpBreakPointHeap@4() + 0x28 字节
ntdll.dll!_RtlpValidateHeapEntry@12() + 0x113 字节
ntdll.dll!_RtlDebugFreeHeap@12() + 0x97 字节
ntdll.dll!_RtlFreeHeapSlowly@12() + 0x228bf 字节
ntdll.dll!_RtlFreeHeap@12() + 0x17646 字节
ole32.dll!CRetailMalloc_Free() + 0x1c 字节
ole32.dll!_CoTaskMemFree@4() + 0x13 字节
[托管到本机的转换]
mscorlib.dll!System.StubHelpers.CSTRMarshaler.ClearNative(System.IntPtr pNative) 行 111 + 0x1c 字节 C#
TestInvoke.exe!TestInvoke.Form1.btnError_Click() C#
我看到了的确调用了那个清除内存的方法,但是我却在反汇编里看不到谁调用了ClearNative方法呢????
是由于函数返回时,clr马上调用了它,而在我们的反汇编代码里没有体现吗?
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
格蠹老雷
2010-10-15, 20:09 下午
通过调试看到,CoTaskMemFree在释放DLL里的常量...
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
不及格的程序员-八神
2010-10-19, 17:13 下午
可是即使是clr在回收内存,为什么Release版能够在xp中运行通过,
而在win7则不能呢? 哪些新的规则在限制着它呢?
不及格的程序员-八神
Re: P/Invoke调用dll函数,将本来void的函数声明为带返回值的,在win7出错,在winxp下正常,理由是?
格蠹老雷
2010-10-19, 22:56 下午
错误检查的严格程度不同而已