使用WinAPI / COM接口时的调用约定

背景: 网上查到,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 #include <Unknwn.h>
  5 
  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<IntfTest*>(this);
 36         }
 37         else if (iid == IID_IntfTest)
 38         {
 39             *ppv = static_cast<IntfTest*>(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、

 

posted @ 2016-04-14 13:52  csskill  阅读(680)  评论(0编辑  收藏  举报