65.9K
CodeProject 正在变化。 阅读更多。
Home

用 .NET 编写和使用简单 COM/ATL DLL 的初学者教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (31投票s)

2009年7月18日

CPOL

6分钟阅读

viewsIcon

244777

downloadIcon

2859

文章涉及属性、方法和事件等简单问题,以及在 .NET 应用程序中使用它们

引言

在论坛讨论中,我遇到许多想编写 COM/ATL DLL 但不知道如何创建属性、方法或从组件引发事件,或者缺乏创建它们的基础知识的人。为此,我用 VC++ 6.0 写了这篇文章。由于 Visual Studio 6 现在已停止支持,许多初学者要求我使用 Visual Studio 2005/2008 编写相同的文章,因为两者之间 GUI 差异很大。

因此,我现在用 Visual Studio 2005 呈现同一篇文章,由于这是一篇新文章,我必须为读者提供一些新内容,否则他们会拒绝这篇文章。.NET 应用程序正在慢慢侵蚀 Visual C++ 应用程序的市场份额。因此,为了顺应大众需求,我将展示如何在 .NET 应用程序而不是非托管应用程序中使用 COM 组件。由于这是一篇初学者文章,我在文章中包含了一些额外的截图。请不要因为使用相同的截图而指责我。

目录

  • 创建 ATL 组件
  • 在 Visual C# 应用程序 (.NET 2005) 中使用它

创建 ATL 组件

让我们一步一步地在 Visual Studio 2005 中创建 ATL 组件

  1. 打开 Visual Studio 2005,点击 文件 | 新建 | 项目菜单,这将显示以下 文件保存对话框,您可以在其中为新项目提供名称。这里我将名称设为 SimpleATLCom,建议选择要创建项目的目标文件夹,否则它会使您的文档文件夹变得杂乱。按 确定 确认您的更改。这将创建一个空白的 ATL 组件。

    图 1:文件打开对话框

    现在,在下一个对话框(ATL 项目向导)中,选择默认设置并按完成。如果您需要 MFC 支持,请勾选“支持 MFC”复选框。

    图 2:ATL 项目向导

    现在在项目中插入 ATL 对象,我将在下一步展示。

  2. 选择 项目 | 添加类。 将出现以下对话框

    图 3:添加简单 ATL 对象

    选择 ATL|ATL 简单对象 并按下 添加按钮

  3. ATL 简单对象向导 中输入新的接口名称。当您开始键入接口名称时,您会看到 Visual Studio 开发环境会自动填充其余详细信息。同时,勾选“选项”选项卡中的“连接点”复选框,这使组件能够引发事件。

    图 4:ATL 简单对象

    图 5:选择连接点复选框
  4. 现在,如果您检查以下 IDL(接口定义语言),它将被生成并包含在您的项目中

        1.    // SimpleATLcom.idl : IDL source for SimpleATLcom 
        2.    // 
        3.    // This file will be processed by the MIDL tool to 
        4.    // produce the type library (SimpleATLcom.tlb) and marshalling code. 
        5.             
        6.    import "oaidl.idl"; 
        7.    import "ocidl.idl"; 
        8.         
        9.    [ 
        10.     object, 
        11.     uuid(8F4FEC76-F1F6-4D69-A2E8-FFF4F316C101), 
        12.     dual, 
        13.     nonextensible, 
        14.     helpstring("ISimpleCom Interface"), 
        15.     pointer_default(unique) 
        16.   ] 
        17.   interface ISimpleCom : IDispatch{ 
        18.    }; 
        19.    [ 
        20.    uuid(2CA18A17-D157-4D1E-A2C7-3D69EBD87250), 
        21.    version(1.0), 
        22.    helpstring("SimpleATLcom 1.0 Type Library") 
        23.    ] 
        24.    Library SimpleATLcomLib 
        25.    { 
        26.      importlib("stdole2.tlb"); 
        27.      [ 
        28.        uuid(85D35B5B-8DBF-4562-96B0-66A4C4718DD5), 
        29.         helpstring("_ISimpleComEvents Interface") 
        30.      ] 
        31       dispinterface _ISimpleComEvents 
        32        { 
        33.         properties: 
        34.         methods: 
        35.      }; 
        36.      [ 
        37.        uuid(A7657FC5-D63C-49C3-AEE5-D8799F412C74), 
        38.        helpstring("SimpleCom Class") 
        39.      ] 
        40.       coclass SimpleCom 
        41.       { 
        42          [default] interface ISimpleCom; 
        43.         [default, source] dispinterface _ISimpleComEvents; 
        44.       }; 
        45.    };
    • 第 11 行:这是开发环境生成的默认 UUID(唯一 ID),它使组件具有唯一标识性。
    • 第 17 行:这是我们的组件名称和 UUID,其内容可用于唯一标识组件,您可以使用此 ID 创建组件。
    • 第 31 行:我们的 dis-interface,即事件函数。
  5. 现在使用“添加属性”和“添加方法”向导添加属性和方法。这可以在类视图中找到,右键单击 ISimpleCom 接口,然后 添加 -> 添加方法或添加属性:

    图 6:添加方法/添加属性

    添加方法 Calculate,参数 Long 为 OUT, RETVAL,如下所示

    图 7:添加方法

    添加类型为 Long 的属性 ComMark,如示例所示

    图 8:添加属性

    同样,您自己添加属性 AtlMarks 为 long 和 StudentNameBSTR。抱歉,我忘记提及参数中的 IN、OUT 和 RETVAL 类型。

    • Propget – 表示从组件获取值的属性
    • PropPut – 表示将属性设置到组件的属性。这可以是可选的,如果删除它,则可以使您的属性为只读
    • Method — 执行一些计算的简单函数
    • [in] - 表示数据传入或您正在向组件设置一些值
    • [out,retval] - 符号表示使用此参数将返回数据
    • HRESULT - 标准错误报告变量
  6. 现在,以几乎相同的方式添加事件,我们在接口中添加方法。现在右键单击 _ISimpleComEvents 并添加 | 添加方法。

    图 9:添加事件
  7. 一切完成后,修改后的 IDL 文件将如下所示

        1.  interface ISimpleCom : IDispatch 
        2.  { 
        3.   [id(1), helpstring("method Calculate")] HRESULT Calculate([out,retval]
        4.           LONG* a_lTotalMarks); 
        5.   [propget, id(2), helpstring("property ComMarks")] HRESULT ComMarks([out,
        6.           retval] LONG* pVal); 
        7.   [propput, id(2), helpstring("property ComMarks")] HRESULT ComMarks([in]
        8.            LONG newVal); 
        9.   [propget, id(3), helpstring("property AtlMarks")] HRESULT AtlMarks([out,
        10.            retval] LONG* pVal); 
        11.  [propput, id(3), helpstring("property AtlMarks")] HRESULT AtlMarks([in]
        12.            LONG newVal); 
        13.  [propget, id(4), helpstring("property StudentName")] 
    					HRESULT StudentName([out,
        14.            retval] BSTR* pVal); 
        15.  [propput, id(4), helpstring("property StudentName")] 
    					HRESULT StudentName([in]
        16.            BSTR newVal); 
        17.  }; 
        18.        
        19.  dispinterface _ISimpleComEvents 
        20.  { 
        21.    properties: 
        22.    methods: 
        23.    [id(1), helpstring("method TotalMarks")] HRESULT TotalMarks([in] LONG
        24.            a_lTotalMark); 
        25.  }; 
  8. 现在将连接点添加到主接口中。为此,右键单击 CSimpleCom 并点击 添加 | 添加连接点。将出现以下截图

    图 10:选择添加连接点

    选择 > 按钮将 _ISimpleComEvents 源接口移至实现连接点,然后点击完成。一个新函数 Fire_TotalMarks 被添加到 CSimpleCom 类中,可用于触发事件。

    图 11:添加连接向导
  9. 现在编写 CSimpleCom 类中所有属性和函数的代码,即为 ComMarksATLMarksStudentName 添加类变量,用于存储来自外部世界的信息。编码后,我们的类将如下所示

            		1.   //// Declare following private variable in Header File
    		2.	long m_lComMarks,m_lAtlMarks;
    		3.		CComBSTR m_bstName;
    		4.
    		5.	////  Source File
    		6. 	STDMETHODIMP CSimpleCom::Calculate(LONG* a_lTotalMarks) 
    		7. 	{ 
    		8. 	  long lTotalMarks = m_lAtlMarks + m_lComMarks; 
    		9. 	  * a_lTotalMarks = lTotalMarks; 
    		10. 	  Fire_TotalMarks(lTotalMarks); //Fire Event 
    		11. 	  return S_OK; 
    		12.	} 
    		13. /// Return Com marks
    		14. STDMETHODIMP CSimpleCom::get_ComMarks(LONG* pVal) 
    		15.	{ 
    		16. 	* pVal = m_lComMarks; 
    		17. 	return S_OK; 
    		18. 	} 
    		19. // Store Com Marks in Local Storage
    		20. STDMETHODIMP CSimpleCom::put_ComMarks(LONG newVal) 
    		21.	{ 
    		22. 	m_lComMarks = newVal; 
    		23. 	return S_OK; 
    		24.	} 
    		25. // return ATL Marks
    		26.	STDMETHODIMP CSimpleCom::get_AtlMarks(LONG* pVal) 
    		27.	{ 
    		28.  * pVal = m_lAtlMarks; 
    		29.	 return S_OK; 
    		30.	 } 
    		31. //Store ATL marks in local storage
    		32. STDMETHODIMP CSimpleCom::put_AtlMarks(LONG newVal) 
    		33. { 
    		34.  m_lAtlMarks = newVal; 
    		35.  return S_OK; 
    		36. }
    		37.  //return Student Name
    		38. STDMETHODIMP CSimpleCom::get_StudentName(BSTR* pVal) 
    		39. { 
    		40.  *pVal = m_bstName.Copy(); 
    		41.  return S_OK; 
    		42. } 
    		43. //Store Student name in local Storage.
    		44. STDMETHODIMP CSimpleCom::put_StudentName(BSTR newVal) 
    		45. { 
    		46.  m_bstName = newVal; 
    		47.  return S_OK; 
    		48. }    

    现在 构建解决方案,构建过程完成后,您的 DLL 将自动注册。

在 Visual C# 应用程序中使用它

现在再次一步一步地在 C#.NET 中创建一个项目,并在项目中包含 COM 组件的支持。

  1. 在同一个解决方案中添加一个新的 C# 窗口应用程序项目(SimpleATLcomTest),然后点击接受默认配置以创建一个测试项目。
    图 12:添加新的 C# 项目
  2. 按照下面截图所示设计界面;添加三个 Textbox 用于向组件输入值,以及四个文本框用于检索所有值,包括计算出的值。

    图 13:测试应用程序设计
  3. 现在,通过右键单击项目名称并单击“添加引用”菜单项,为 SimpleATLCom.dll 添加引用。

    图 14:添加引用菜单

    点击 COM 选项卡,选择 SimpleATLCom.dll 将组件引用添加到项目中。

    图 15:添加引用
  4. 现在,编写以下代码以在您的项目中使用该组件。

            1.  // Include SimpleAtlComLib  
            2.  using SimpleATLcomLib; 
            3.  namespace SimpleAtlComTest  
            4.  { 
            5.  public partial class SimpleATLCom : Form 
            6.  { 
            7.  // Create Object of Main Component Interface 
            8.  ISimpleCom objSimpleCom = new SimpleComClass(); 
            9.  // Also declare Event variable 
            10. _ISimpleComEvents_Event event1 = null; 
            11.	public SimpleATLCom() 
            12.	{ 
            13.	InitializeComponent(); 
            14.	// Initialize event1 variable with object of simplecom 
            15.	event1 = (_ISimpleComEvents_Event)objSimpleCom; 
            16.
            17.	// Add Event to listen event raised by Component 
            18.	event1.TotalMarks += new _ISimpleComEvents_TotalMarksEventHandler
    			(ISimpleCom_TotalMarksEvent);
            19.	
            20.	} 
            21.	// this function will put the values back to Component 
            22.	private void btnPutValues_Click(object sender, EventArgs e) 
            23.	{ 
            24.	objSimpleCom.AtlMarks = int.Parse(txtPATLMarks.Text); 
            25.	objSimpleCom.ComMarks = int.Parse(txtPComMarks.Text); 
            26.	objSimpleCom.StudentName = txtPName.Text; 
            27.	} 
            28.	// this function will bring values stored into Component 
            29.	// and populate into TextBox present on Form 
            30.	private void btnGetDetails_Click(object sender, EventArgs e) 
            31.	{ 
            32.	txtGAtlMarks.Text = objSimpleCom.AtlMarks.ToString(); 
            33.	txtGComMarks.Text = objSimpleCom.ComMarks.ToString(); 
            34.	txtGName.Text = objSimpleCom.StudentName; 
            35.	objSimpleCom.Calculate(); 
            36.	} 
            37.	// this Event will called, when invoke method Calculate 
    		// of simpleATLCom. 
            38.	void ISimpleCom_TotalMarksEvent(int lTotalMarks) 
            39.	{ 
            40.	txtTotalMarks.Text = lTotalMarks.ToString(); 
            41.	MessageBox.Show("Total Marks is " + txtTotalMarks.Text); 
            42.	} 
            43.	} 
            44.	}     
  5. 运行测试应用程序,您将看到结果

    图 16:运行应用程序

关于下载代码

源代码包括

  • SimpleAtlCom.dll(含源代码)
  • Visual C# 中的测试项目

演示应用程序的使用

如果你愿意,可以先使用 Com DLL 和测试应用程序。别忘了将 Com DLL,即 SimpleAtlCom.dll,注册到你的电脑上。你可以使用这个命令行来注册组件。

Drive:> %sys%regsvr32 path_to_dll\SimpleAtlCom.dll

作者评论

我尽力解释了 COM DLL 的每一个简单方面。如果有什么遗漏,请随时联系我或在下面的讨论区留下您的宝贵意见。

特别感谢

  • 献给我的母亲(已故)、父亲,当然还有我的妻子
  • 献给 CodeProject.com, 为程序员互动提供平台。
  • 感谢 Leeland Clay 修正编辑错误并提供改进建议。

历史

  • 2009 年 7 月 18 日:初次发布
© . All rights reserved.