COM组件的接口类添加Event

本文介绍了在COM组件中如何实现连接点与事件接口。详细讲述了如何定义与实现调度接口,以及如何通过向导和手动方式添加连接点,实现自定义事件输出。此外,还涉及了如何重用其他类型库中的连接点接口。

自己获取有无event的区别是,在向导中是否勾选:连接点。

红色为添加部分:

import "oaidl.idl";
import "ocidl.idl";
 
[
     object,
     uuid(C74F7F62-D315-4BF6-9422-9B80D68DB4FA),
     dual,
     nonextensible,
     helpstring("ISample 接口"),
     pointer_default(unique)
]
interface ISample : IDispatch{
};
[
     uuid(48D59498-7F95-4C2B-B3C3-B5DA3B407025),
     version(1.0),
     helpstring("EventSource 1.0 类型库")
]
library EventSourceLib
{
     importlib("stdole2.tlb");
     [
         uuid(87A653F3-F08F-4C1F-B091-5F8FAA894E3C),
         
     ]
     [
 uuid(def121c4-a87e-484c-bb84-48404e74e490)
helpstring("_ISampleEvents 的 UUID")
     ]
     dispinterface _ISampleEvents
     {
         properties:
         methods:
     };
     [
         uuid(6CC7B493-5F8E-4C08-B66D-D9E5FD2342E0),
         helpstring("Sample Class")
     ]
     coclass Sample
     {
         [defaultinterface ISample;
         [default, source] dispinterface _ISampleEvents;
     };
};
红色的就是本组件首选的事件接口。ISampleEvents接口目前没有提供方法和属性。
 
类的声明如下:
class ATL_NO_VTABLE CSample :
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CSample, &CLSID_Sample>,
     public ISupportErrorInfo,
     public IConnectionPointContainerImpl<CSample>,
     public CProxy_ISampleEvents<CSample>,
public IDispatchImpl<ISample, &IID_ISample, &LIBID_EventSourceLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
 
 
IConnectionPointContainerImpl和CProxy_ISampleEvents<CSample>两个父类是关键。
 
BEGIN_COM_MAP(CSample)
     COM_INTERFACE_ENTRY(ISample)
     COM_INTERFACE_ENTRY(IDispatch)
     COM_INTERFACE_ENTRY(ISupportErrorInfo)
     COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()
 
COM MAP宏表明了对象支持IConnectionPointContainer接口
 
BEGIN_CONNECTION_POINT_MAP(CSample)
     CONNECTION_POINT_ENTRY(__uuidof(_ISampleEvents))
END_CONNECTION_POINT_MAP()
 
上面这个宏将_ISampleEvents接口的IID保存到连接点映射表中。


编译IDL即可

------------------------------------------------------------------------------------------

为对象添加连接点

Visual Studio .NET 2003

ATL 教程说明了如何创建支持连接点的控件、如何添加事件,然后是如何实现连接点。ATL 用 IConnectionPointImpl 类实现连接点。

若要实现连接点,有两种选择:

  • 通过为控件或对象添加连接点,实现自己的输出事件源。
  • 重用其他类型库中定义的连接点接口。

在上述任何一种情况下,“实现连接点向导”都使用类型库来做这项工作。

为控件或对象添加连接点

  1. 在 .idl 文件的库块中定义调度接口。当您用“ATL 控件向导”创建控件时,如果启用了连接点支持,则已经创建了调度接口。如果在创建控件时没有启用连接点支持,则必须将调度接口手动添加到 .idl 文件中。以下是一个调度接口的示例。输出接口不需要是调度接口,但是许多脚本语言(如 VBScript 和 JScript)都要求这一点,因此该示例使用了两个调度接口:
    library DProjLib
    {
       importlib("stdole32.tlb");
       importlib("stdole2.tlb");
       [
          uuid(57BC50F0-780B-11d1-8C44-0060083E866C),
          helpstring("Buddy Events")
       ]
    dispinterface DBuddyEvents
       properties:
       methods:
    };
    

    使用 uuidgen.exe 或 guidgen.exe 实用工具生成 GUID。

  2. 在项目的 .idl 文件中,将调度接口作为 [default,source] 接口添加到对象的 coclass 中。同样,如果在创建控件时启用了连接点支持,则“ATL 控件向导”将创建 [default,source] 项。若要手动添加此项,请添加下面以粗体显示的行:
    coclass Buddy
    {
       [default] interface IBuddy;
       [default,source] dispinterface DBuddyEvents;
    };
    

    有关示例请参见 Circ ATL 示例中的 .idl 文件。

  3. 使用“类视图”将方法和属性添加到事件接口。右击“类视图”中的类,指向快捷菜单上的“添加”,然后单击“添加连接点”。
  4. 在“实现连接点向导”的“源接口”列表框中,选择“项目的接口”。如果为控件选择接口并按“确定”,您将:
    • 生成一个头文件,其中包含一个事件委托类,该类实现将为事件进行输出调用的代码。
    • 将一项添加到连接点映射。

    您还会看到计算机上的所有类型库的列表。仅当希望实现这些其他类型库之一中的完全相同的输出接口时,才应使用该类型库来定义连接点。

重用其他类型库中定义的连接点接口

  1. 在“类视图”中,右击实现 BEGIN_COM_MAP 宏的类,指向快捷菜单上的“添加”,然后单击“添加连接点”。
  2. 在“实现连接点向导”中,选择一个类型库和该类型库中的一个接口,然后单击“添加”。
  3. 编辑 .idl 文件,执行下列任一操作:
    • 从 .idl 文件中复制其事件源正被使用的对象的调度接口。
    • 对该类型库使用 importlib 指令,如下所示。此示例摘自一个使用 Circ 控件中的连接点的 .idl 文件。
      [
         uuid(38BBFD91-7575-11D1-8C3C-0060083E866C),
         version(1.0),
         helpstring("16190 1.0 Type Library")
      ]
      library MY16190Lib
      {
         importlib("stdole32.tlb");
         importlib("stdole2.tlb");
         importlib("D:\\ATL25\\samples\\circ\\circ.tlb");   //**** add this line 
      
         [
            uuid(38BBFD9E-7575-11D1-8C3C-0060083E866C),
            helpstring("Atl16190Ctl Class")
         ]
         coclass Atl16190Ctl
         {
            [default] interface IAtl16190Ctl;
            [default,source] dispinterface _CircEvents;      //**** and this one
         };
      };

<think>根据用户查询,用户想了解如何定义组件接口的方法或规范。结合之前的引用内容,特别是引用[2]中提到的文件组织结构和命名规范,以及引用[1]中的API设计规范,我们可以总结出组件接口定义的一些关键点。 组件接口定义通常包括以下几个方面: 1. 组件命名规范 2. 组件的输入(props)和输出(events)定义 3. 组件的插槽(slots)定义 4. 组件的类型定义(使用TypeScript时) 5. 组件的文档注释 具体规范如下: 1. **组件命名规范**: - 全局组件:使用PascalCase命名,例如:`MyButton`。 - 业务组件:同样使用PascalCase命名,并且建议放在对应的业务目录下,如`views/user/components/UserProfile.vue`。 2. **组件的props定义规范**: - 使用TypeScript接口定义props的类型。 - 为每个prop指定类型、默认值和验证规则。 - 使用驼峰式命名(camelCase)定义props,但在模板中使用时推荐使用kebab-case(短横线分隔)。 3. **组件events定义规范**: - 使用自定义事件(`emits`)来声明组件可以触发的事件。 - 事件名使用kebab-case命名(因为HTML属性是大小写不敏感的,所以推荐使用短横线分隔)。 4. **组件的slots定义规范**: - 使用具名插槽,并为其提供说明文档。 - 作用域插槽应定义清楚其作用域属性。 5. **类型定义**: - 在TypeScript项目中,将组件的props、emits和插槽的类型定义放在单独的类型文件中(如`types/ComponentTypes.ts`)或在组件文件中使用`interface`定义。 6. **文档注释**: - 为组件、props、emits、slots等添加详细的注释,便于其他开发者使用。 示例(使用Vue 3的Composition API和TypeScript): ```vue <script setup lang="ts"> import { defineProps, defineEmits } from 'vue'; // 定义Props接口 interface Props { // 用户姓名 name: string; // 用户年龄,可选 age?: number; // 是否激活 isActive?: boolean; } const props = defineProps<Props>(); // 定义Emits const emit = defineEmits<{ // 当用户被选中时触发 (e: 'select', id: number): void; // 当用户信息更新时触发 (e: 'update:name', value: string): void; }>(); </script> <template> <!-- 组件模板 --> </template> ``` 对于大型项目,建议将组件的类型定义抽取到单独的文件中,例如: 在`types/UserCard.ts`中定义: ```typescript export interface UserCardProps { name: string; age?: number; isActive?: boolean; } ``` 然后在组件中引入: ```vue <script setup lang="ts"> import { defineProps } from 'vue'; import type { UserCardProps } from '@/types/UserCard'; defineProps<UserCardProps>(); </script> ``` 此外,参考引用[1]中的接口设计规范,组件接口也应该考虑: - 避免精度丢失(如对于大整数,使用字符串传递) - 敏感数据脱敏(如密码等敏感信息不应通过props传递) - 拓展性(预留一些扩展属性,如使用`$attrs`传递额外的属性) 总结:组件接口定义需要遵循命名规范、类型安全、文档清晰等原则,同时考虑可维护性和扩展性。 §§相关问题§§ 1. 如何为Vue组件编写单元测试以确保接口的稳定性? 2. 在大型项目中如何管理组件接口的版本兼容性? 3. 如何设计组件的插槽以提供最大的灵活性?</think>### 组件接口定义方法与规范 组件接口定义是前端工程化的核心环节,需遵循以下规范(基于引用[1][2]的最佳实践): #### 一、基础规范 1. **命名规范** - 组件文件使用 `PascalCase`(如 `UserProfile.vue`) - 接口方法使用 `camelCase`(如 `getUserInfo()`) - 事件命名采用 `kebab-case`(如 `@update:user-data`)[^2] 2. **类型定义** 在 `types/` 目录定义接口类型(TypeScript): ```typescript // types/UserType.ts export interface UserProfile { id: string; // 避免大整数精度丢失[^1] name: string; age?: number; // 可选属性 avatar: string; } ``` #### 二、Props 定义规范 ```vue <script setup lang="ts"> import type { UserProfile } from '@/types/UserType' defineProps({ // 必填属性 userId: { type: String, required: true }, // 带默认值的可选属性 theme: { type: String as () => 'light' | 'dark', default: 'light' }, // 复杂对象类型 profile: { type: Object as PropType<UserProfile>, default: () => ({ name: 'Guest' }) } }) </script> ``` #### 三、事件接口规范 1. **自定义事件** ```typescript const emit = defineEmits<{ (e: 'update:name', value: string): void (e: 'delete', id: string, confirm: boolean): void }>() ``` 2. **事件命名原则** - 数据更新事件:`update:<propName>` - 动作事件:`confirm` / `cancel` / `submit` - 避免原生事件名(如 `click`) #### 四、API 通信规范 1. **独立 API 层** 在 `api/` 目录封装接口(参考引用[2]): ```typescript // api/user.ts import axios from '@/utils/axios' import type { UserProfile } from '@/types/UserType' export const fetchUser = async (id: string): Promise<UserProfile> => { const { data } = await axios.get(`/users/${id}`, { headers: { 'X-Client-IP': 'xxx' } // IP白名单校验[^1] }) return data } ``` 2. **安全规范** - 敏感数据脱敏(如银行卡号显示为 `**** **** **** 1234`)[^1] - 接口限流(单个IP每分钟不超过60次)[^1] - 请求日志记录(关键操作存日志)[^1] #### 五、组件扩展性设计 1. **插槽扩展** ```vue <template> <div class="card"> <slot name="header" :user="userData" /> <slot :profile="userProfile" /> </div> </template> ``` 2. **属性继承** 使用 `useAttrs()` 继承未声明属性: ```vue <template> <div v-bind="attrs"> <!-- 内容 --> </div> </template> ``` #### 六、文档规范 1. **类型注释** ```typescript /** * 用户卡片组件 * @prop {String} userId - 用户ID (必填) * @event update:name - 更新用户名事件 * @slot default - 主内容区域插槽 */ ``` 2. **版本记录** 使用 `CHANGELOG.md` 记录接口变更: ```markdown ## [1.2.0] - 2023-10-15 ### Added - 新增 `theme` 属性支持暗色模式 ### Changed - `profile` 对象结构变更(兼容旧版本) ``` ### 最佳实践总结 | 分类 | 规范要点 | 示例 | |------------|-----------------------------|--------------------------| | 命名 | PascalCase 组件名 | `UserProfile.vue` | | Props | 类型声明 + 默认值 | `type: String, default: ''` | | 事件 | kebab-case + 作用域参数 | `@update:user-data` | | API | 独立封装 + 错误处理 | `api/user.ts` | | 类型定义 | 集中管理 | `types/UserType.ts` | | 文档 | JSDoc 注释 | `@prop {String} userId` |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值