用 .NET 编写和使用简单 COM/ATL DLL 的初学者教程
文章涉及属性、方法和事件等简单问题,以及在 .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 组件
- 打开 Visual Studio 2005,点击 文件 | 新建 | 项目菜单,这将显示以下 文件保存对话框,您可以在其中为新项目提供名称。这里我将名称设为
SimpleATLCom
,建议选择要创建项目的目标文件夹,否则它会使您的文档文件夹变得杂乱。按 确定 确认您的更改。这将创建一个空白的 ATL 组件。图 1:文件打开对话框现在,在下一个对话框(ATL 项目向导)中,选择默认设置并按完成。如果您需要 MFC 支持,请勾选“支持 MFC”复选框。
图 2:ATL 项目向导现在在项目中插入 ATL 对象,我将在下一步展示。
- 选择 项目 | 添加类。 将出现以下对话框图 3:添加简单 ATL 对象
选择 ATL|ATL 简单对象 并按下 添加按钮。
- 在 ATL 简单对象向导 中输入新的接口名称。当您开始键入接口名称时,您会看到 Visual Studio 开发环境会自动填充其余详细信息。同时,勾选“选项”选项卡中的“连接点”复选框,这使组件能够引发事件。图 4:ATL 简单对象图 5:选择连接点复选框
-
现在,如果您检查以下 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,即事件函数。
- 现在使用“添加属性”和“添加方法”向导添加属性和方法。这可以在类视图中找到,右键单击 ISimpleCom 接口,然后 添加 -> 添加方法或添加属性:图 6:添加方法/添加属性
添加方法 Calculate,参数 Long 为 OUT, RETVAL,如下所示
图 7:添加方法添加类型为 Long 的属性 ComMark,如示例所示
图 8:添加属性同样,您自己添加属性 AtlMarks 为 long 和 StudentName 为 BSTR。抱歉,我忘记提及参数中的 IN、OUT 和 RETVAL 类型。
Propget
– 表示从组件获取值的属性PropPut
– 表示将属性设置到组件的属性。这可以是可选的,如果删除它,则可以使您的属性为只读Method
— 执行一些计算的简单函数[in]
- 表示数据传入或您正在向组件设置一些值[out,retval]
- 符号表示使用此参数将返回数据HRESULT
- 标准错误报告变量- 现在,以几乎相同的方式添加事件,我们在接口中添加方法。现在右键单击
_ISimpleComEvents
并添加 | 添加方法。图 9:添加事件 -
一切完成后,修改后的 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. };
- 现在将连接点添加到主接口中。为此,右键单击 CSimpleCom 并点击 添加 | 添加连接点。将出现以下截图图 10:选择添加连接点
选择 > 按钮将 _ISimpleComEvents 源接口移至实现连接点,然后点击完成。一个新函数 Fire_TotalMarks 被添加到 CSimpleCom 类中,可用于触发事件。
图 11:添加连接向导 -
现在编写
CSimpleCom
类中所有属性和函数的代码,即为ComMarks
、ATLMarks
和StudentName
添加类变量,用于存储来自外部世界的信息。编码后,我们的类将如下所示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 组件的支持。
- 在同一个解决方案中添加一个新的 C# 窗口应用程序项目(
SimpleATLcomTest
),然后点击接受默认配置以创建一个测试项目。图 12:添加新的 C# 项目 -
按照下面截图所示设计界面;添加三个
Textbox
用于向组件输入值,以及四个文本框用于检索所有值,包括计算出的值。图 13:测试应用程序设计 -
现在,通过右键单击项目名称并单击“添加引用”菜单项,为 SimpleATLCom.dll 添加引用。
图 14:添加引用菜单点击 COM 选项卡,选择 SimpleATLCom.dll 将组件引用添加到项目中。
图 15:添加引用 -
现在,编写以下代码以在您的项目中使用该组件。
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. }
- 运行测试应用程序,您将看到结果图 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 日:初次发布