背景: 网上查到,C#的调用约定是 stdcall。
前提: 这里的测试主要是针对 C#项目的"目标平台"为"x86"的情况,其它没有具体测试...
测试环境: Win7 64位,vs2013制作exe(C#,现在用的.net版本为3.5),VC6制作dll 。
ZC: 我没有查到 显示指定 C#普通函数调用约定的方式。然而,我调试的时候,看到的并非是stdcall...
ZC: 具体是这样: ecx存放this指针,edx存放第一个参数,剩下的参数从左至右依次push。没查到这种调用规则 叫什么名字...
ZC: 在C#调用WinAPI/使用COM接口函数的时候,乍一看 也是上面的的调用规则,然而 跟到更底层的call的时候 可以看到 它重新组织了参数 再用stdcall的方式 调用WinAPI/COM接口函数。
1、exe (C# .net版本为3.5)
1.1、ComIntf.cs (接口定义的代码文件)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.InteropServices; 5 using System.Text; 6 7 namespace WindowsFormsApplication 8 { 9 [Guid("FCE9DCF3-9E38-441C-B10F-2BA31B57DCDC")] 10 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 11 public interface IntfTest 12 { 13 unsafe Int32 TestZ01(int _i, int _j, int _k, int* _piOut); 14 unsafe Int32 TestZ02(int _i, int _j, int _k, int _m, int* _piOut); 15 unsafe Int32 TestZ03(int _i, int _j, int _k, int _m, int _n, int* _piOut); 16 } 17 }
1.2、界面中的 测试代码:
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.ComponentModel; 5 using System.Data; 6 using System.Drawing; 7 using System.Runtime.InteropServices; 8 //using System.Linq; 9 using System.Text; 10 //using System.Threading.Tasks; 11 using System.Windows.Forms; 12 13 namespace WindowsFormsApplication 14 { 15 public partial class Form1 : Form 16 { 17 public Form1() 18 { 19 InitializeComponent(); 20 } 21 22 private void button1_Click(object sender, EventArgs e) 23 { 24 //* 25 TdrSvgDll svgdll = new TdrSvgDll(); 26 AAA(1, 2, 3); // 用汇编查看 调用普通C#函数时的调用规则 27 int kk = TestZZ(1, 2, 3, 4 ,5); // 查看调用DLL导出函数时的调用规则 28 29 TestCB(callback); 30 MessageBox.Show(Fhh.ToString()); 31 32 TRect rect = new TRect(); 33 MessageBox.Show(Marshal.SizeOf(rect).ToString()); 34 35 ArrayList list = new ArrayList(); 36 //list.[i]; 37 //*/ 38 } 39 40 int AAA(int i, int j, int k) 41 { 42 return (i + j + k); 43 } 44 45 //[DllImport("DLL_Z.dll", EntryPoint="TestZZ", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)] 46 [DllImport("DLL_Z.dll", EntryPoint = "TestZZ", CharSet = CharSet.Auto)] 47 public static extern int TestZZ(Int32 i, Int32 j, Int32 k, Int32 m, Int32 n); 48 49 [DllImport("DLL_Z.dll", EntryPoint = "TestCB", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 50 public static extern void TestCB([MarshalAs(UnmanagedType.FunctionPtr)] Callback_Test01 _callback); 51 52 // 回调函数指针 53 [UnmanagedFunctionPointer(CallingConvention.StdCall)] 54 public delegate int Callback_Test01(int _i, int _j, int _k, int _m, int _n); 55 56 public static int Fhh = 0; 57 // 需要访问类成员变量的回调函数实例 58 Callback_Test01 callback = 59 (i, j, k, m, n) => 60 { 61 Fhh = (i + j + k + m + n); // 这里可以直接访问 类成员变量 62 //return (i + j + k + m + n); 63 return Fhh; 64 }; 65 66 private void button2_Click(object sender, EventArgs e) 67 { 68 // 操作/使用 DLL返回的接口 69 IntfTest intfTest = null; 70 int iRtn = IntfTest_Get(ref intfTest); 71 72 unsafe 73 { 74 int iOut = 0; 75 intfTest.TestZ01(1, 2, 3, &iOut); 76 } 77 } 78 79 [DllImport("DLL_Z.dll", EntryPoint = "IntfTest_Get", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] 80 public static extern int IntfTest_Get(ref IntfTest _intfTest); 81 } 82 }
2、VC6 -- DLL
2.1、DLL_Z.cpp
1 // DLL_Z.cpp : Defines the entry point for the DLL application. 2 // 3 4 #include "stdafx.h" 5 #include "zz.h" 6 7 BOOL APIENTRY DllMain( HANDLE hModule, 8 DWORD ul_reason_for_call, 9 LPVOID lpReserved) 10 { 11 return TRUE; 12 } 13 14 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 15 // 普通导出函数 16 17 extern "C" __declspec(dllexport) int __stdcall TestZZ(int i, int j, int k, int m, int n) 18 { 19 return (i+j+k+m+n); 20 } 21 22 extern "C" __declspec(dllexport) void TestCC(int i, int j) 23 { 24 } 25 26 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 27 // exe中传入回调函数指针,并且调用 28 29 typedef int (__stdcall * Callback_Test01)(int, int, int, int, int); 30 31 extern "C" __declspec(dllexport) void __stdcall TestCB(Callback_Test01 _callback) 32 { 33 if (_callback) 34 { 35 int i = _callback(1,2,3,4,5); 36 } 37 } 38 39 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 40 // 导出 interface,供 exe使用 41 42 IntfTest *g_pIntfTest; 43 44 extern "C" __declspec(dllexport) int IntfTest_Get(IntfTest **_ppIntfTest) 45 { 46 (*_ppIntfTest) = NULL; 47 48 if ( NULL != g_pIntfTest ) 49 { 50 // 当"IntfTest*"为局部变量时,C#会自动销毁接口,使得引用计数-1,∴这里需要+1 51 // 而 首次获取"IntfTest*"时,由于类构造函数中有增加引用计数的操作,∴不需要+1 52 g_pIntfTest->AddRef(); 53 (*_ppIntfTest) = g_pIntfTest; 54 return 0; 55 } 56 57 // *** 58 IntfTest *pObj = new TintfTest(); 59 60 IUnknown *pIUnknown = NULL; 61 HRESULT hr = pObj->QueryInterface(IID_IUnknown, (void**)&pIUnknown); 62 pObj->Release(); 63 pObj = NULL; 64 65 if (FAILED(hr)) 66 { 67 return -1; 68 } 69 else 70 { 71 hr = pIUnknown->QueryInterface(IID_IntfTest, (void**)&g_pIntfTest); 72 pIUnknown->Release(); 73 pIUnknown = NULL; 74 75 if (SUCCEEDED(hr)) 76 { 77 (*_ppIntfTest) = g_pIntfTest; 78 } 79 else 80 { 81 return -2; 82 } 83 } 84 return 0; 85 }
2.2、
1 #ifndef __zzz_2016__ 2 #define __zzz_2016__ 3 4 #include5 6 // // {FCE9DCF3-9E38-441C-B10F-2BA31B57DCDC} 7 static const IID IID_IntfTest = 8 { 0xfce9dcf3, 0x9e38, 0x441c, { 0xb1, 0xf, 0x2b, 0xa3, 0x1b, 0x57, 0xdc, 0xdc } }; 9 10 11 interface IntfTest : public IUnknown 12 { 13 virtual HRESULT __stdcall TestZ01(int _i, int _j, int _k, int* _piOut) = 0; 14 virtual HRESULT __stdcall TestZ02(int _i, int _j, int _k, int _m, int* _piOut) = 0; 15 virtual HRESULT __stdcall TestZ03(int _i, int _j, int _k, int _m, int _n, int* _piOut) = 0; 16 }; 17 18 class TintfTest :public IntfTest 19 { 20 public: 21 TintfTest(); 22 23 public: 24 virtual HRESULT __stdcall TestZ01(int _i, int _j, int _k, int* _piOut); 25 virtual HRESULT __stdcall TestZ02(int _i, int _j, int _k, int _m, int* _piOut); 26 virtual HRESULT __stdcall TestZ03(int _i, int _j, int _k, int _m, int _n, int* _piOut); 27 28 private: 29 long FlCount; 30 public: 31 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppv) 32 { 33 if (iid == IID_IUnknown) 34 { 35 *ppv = static_cast (this); 36 } 37 else if (iid == IID_IntfTest) 38 { 39 *ppv = static_cast (this); 40 } 41 else 42 { 43 *ppv = NULL; 44 return E_NOINTERFACE; 45 } 46 47 AddRef(); 48 49 return S_OK; 50 } 51 52 virtual ULONG STDMETHODCALLTYPE AddRef() 53 { 54 return ++FlCount; 55 56 //return InterlockedIncrement(&FlCount); 57 } 58 59 virtual ULONG STDMETHODCALLTYPE Release() 60 { 61 if (--FlCount == 0) 62 { 63 delete this; 64 return 0; 65 } 66 return FlCount; 67 68 //if (InterlockedDecrement(&FlCount) == 0) 69 //{ 70 // delete this; 71 // return 0; 72 //} 73 //return FlCount; 74 } 75 }; 76 77 TintfTest::TintfTest(): 78 FlCount(0) 79 { 80 this->AddRef(); 81 } 82 83 HRESULT __stdcall TintfTest::TestZ01(int _i, int _j, int _k, int* _piOut) 84 { 85 *_piOut = (_i + _j + _k); 86 return S_OK; 87 } 88 89 HRESULT __stdcall TintfTest::TestZ02(int _i, int _j, int _k, int _m, int* _piOut) 90 { 91 return S_OK; 92 } 93 94 HRESULT __stdcall TintfTest::TestZ03(int _i, int _j, int _k, int _m, int _n, int* _piOut) 95 { 96 return S_OK; 97 } 98 99 100 #endif // __zzz_2016__
2.3、Z.def
1 LIBRARY DLL_Z 2 EXPORTS 3 TestZZ 4 TestCC 5 TestCB 6 IntfTest_Get
3、
3.1、C# exe 调用 普通的DLL导出函数
(1)、对于DLL导出函数TestZZ(),显示指定调用约定为 stdcall,(2)、不显示指定调用约定,
通过对 DLL的调试跟踪,可以看到 在C#自有的调用约定之下,它还是构建了stdcall的方式来调用 TestZZ(...)
ZC: 为何要在DLL中调试看汇编?∵ 在C#中调试看汇编的时候,它只能看到C#的函数call,进不了DLL的函数call 。按F11(逐语句) 进不了更进一步的函数call ,出现的效果就像 F10(逐过程)一样
3.2、C# exe 调用 DLL导出的COM型interface
貌似也是 在C#自有的调用约定之下,它还是构建了stdcall的方式来调用 接口函数(虽然它在C#的interface声明中并没有显示指定使用何种调用约定)
4、
还有疑问的话,就调试看吧,VS2013调试汇编,VC6 DLL调试汇编,都看一下吧
5、