向ATL DLL中传递C++对象

本文介绍了通过COM接口传递C++对象的方法。阐述了向ATL的DLL中传递C++对象参数的条件,给出创建ATL DLL服务器、添加MFC类等步骤,还介绍了使用CArchive对象进行序列化和反序列化,以及创建COM接口、测试应用程序等内容。
部署运行你感兴趣的模型镜像

类型:翻译
出处:http://www.codeguru.com
时间:2004-09-27

 


译者:王先生(MrWang2000)
Email : yuanzhaowang@sohu.com
    或yuanzhaowang@163.com

实例下载地址  Download source files - 116 Kb

  几个星期以前,我拼命的寻找一个能够通过COM接口传递C++对象的例子,
但是,没有找到.这就是我发表这篇文章的原因

  向ATL的DLL中传递一个C++对象参数并不是非常之难,
但是,当然也会有点难度,也很有趣.

  在开始一个工程以前,确信你的客户机和服务器组件都是由适应C++的程序的,
第二,你必须知道怎样设置你的客户机和服务器

接口的局限性

  COM技术要求客户机和服务器高度的分离,这是通过接口实现的,
但是问题出在:接口的方法中只提供了有限个参数数据类型,
如果这个接口是基于IDispatch的,参数类型的可选范围就更加受到限制了,
由于这些局限性,C++对象只有在满足以下条件时才能够传递:

1,客户机和服务器都是由VC++编写
2,它们必须共享对象的定义(比如 头文件)
3,传递应用程序设计的简单的对象
4,你的应用程序可能需要运行在一个分布式环境下。你希望COM的远程活动,本地/远程活动是透明的,安全的

我建议,在开始工作之前,先顺序的看一下各个标题

现在,我列出实例,并作以下事情:

1,创建一个ATL DLL服务器
2,添加一个MFC类,从CObject类派生
3,在类的头部使用 DECLARE_SERIAL 宏
4,在类的中间使用 IMPLEMENT_SERI 宏
5,覆盖Serialize() 方法

// 你的 CSimpleObj 类应该像这样:
class CSimpleObj : public CObject
{
  DECLARE_SERIAL( CSimpleObj )
public:
// 构造函数和析构函数
  CSimpleObj();
  virtual ~CSimpleObj();
// 设置内部字符串数据
  void SetString( CString csData );
// 用来向存档文件串行输入数据(序列化)
  virtual void Serialize(CArchive& ar);
// 现实字符串数据
  void Show();
private:
  CString m_strData;// 内部字符串数据
};

// 把这个数据对象写入到文档中
void CSimpleObj::Serialize(CArchive& ar)
{
  CObject::Serialize( ar );

if (ar.IsLoading())
{
// 从档案文件提取数据
  ar >> m_strData;
}
else
{
// 把数据存入档案文件
  ar << m_strData;
}
}

// 显示对象数据的方法
void CSimpleObj::Show()
{
  AfxMessageBox(m_strData);
}

//把字符串数据保存到一个变量中
void CSimpleObj::SetString(CString csData)
{
  m_strData = csData;
}


6,现在,下一步就是用一个CArchive对象来进行序列化和反序列化(载入和存储对象),
我用了一个叫CBlob的新类来实现的


class CBlob
{
public:
  CBlob() {};
  virtual ~CBlob() {};

// 从一个 CObject对象中提取数据并载入到一个 SAFEARRAY对象中.
  SAFEARRAY* Load( CObject *pObj );
// 重新创建一个SAFEARRAY对象
  BOOL Expand( CObject * &pObj, SAFEARRAY *pVar );
private:
};

// 从一个 CObject对象中提取数据并用它构建一个 SAFEARRAY对象.

SAFEARRAY* CBlob::Load( CObject *pObj)
{
  CMemFile memfile; // 内存文件

// 定义一个用来标记档案文件是读取还是存储的标志
  long lMode = CArchive::store | CArchive::bNoFlushOndelete;
// 用内存文件创建档案文件
  CArchive ar(&memfile, lMode );

// m_pDocument 不使用
  ar.m_pDocument = NULL;
// 序列化对象到档案文件中
  ar.WriteObject(pObj);
// 关闭档案文件--现在,数据在内存文件中
  ar.Close();

// 取得内存文件的长度(以字节为单位)

  long llen = memfile.GetLength();

// 释放缓冲区 关闭文件
  unsigned char *pMemData = memfile.Detach();

// 设定safearray
  SAFEARRAY *psa;

// 创建safearray对象存取流数据
  psa = SafeArrayCreateVector( VT_UI1, 0, llen );

// 指向字节数组的指针
  unsigned char *pData = NULL;

// 取得一个 safe array的指针. 锁定数组.
  SafeArrayAccessData( psa, (void**)&pData );

// 拷贝内存文件到 safearray
  memcpy( pData, pMemData, llen );

// 清理缓冲区
  delete pMemData;

// 锁定对 safearray的访问
  SafeArrayUnaccessData(psa);

// 返回一个在这分配的SAFEARRAY的指针
  return psa;
}

// 重新创建一个SAFEARRAY对象
BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
{
  CMemFile memfile; // 反序列化的内存文件
  long lLength; // 字节数
  char *pBuffer; // 缓冲区指针

// 锁定数组数据的访问
  SafeArrayAccessData( psa, (void**)&pBuffer );

// 取得数组中元素个数. 是字节数
  lLength = psa->rgsabound->cElements;

// 连接缓冲区到内存文件
  memfile.Attach((unsigned char*)pBuffer, lLength);

// 从缓冲区头部开始
  memfile.SeekToBegin();

// 创建一个连接到内存文件上的档案文件
  CArchive ar(&memfile, CArchive::load | CArchive::bNoFlushOndelete);

// 不使用文档指针
  ar.m_pDocument = NULL;

// 填充对象 取得指针
  rpObj = ar.ReadObject(0);

// 关闭档案文件
  ar.Close();

// 注意: 当SAFEARRAY被毁坏时 pBuffer 被释放
// 释放缓冲区 关闭文件
  pBuffer = (char*) memfile.Detach();

// 释放safearray 缓冲区
  SafeArrayUnaccessData( psa );

  return TRUE;
}

  在这里 ,我使用SAFEARRAY是因为它对我们来说是最好的选择,
它可以包含一些复杂的多维数组,但是,
这个例子我们只使用了非常简单的数组,SAFEARRAY数据
  有一个问题:MIDL认不出这个数据类型,
在下一篇文章中我将讲述最简单的方法:使用 VARIANT数据类型

下一步如下:
1,创建一个COM接口
2,创建一个SAFEARRAY对象
3,在IDL文件中定义 [helpstring("method SetArray")]
HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring("method GetArray")]
HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData);
4,创建一个基于MFC的客户机来测试该应用程序

你的IDL文件应该象这样:

interface IBolbData : IUnknown
{
  [helpstring("method SetArray")] HRESULT SetArray([in]SAFEARRAY
    (unsigned char) pData);
  [helpstring("method GetArray")] HRESULT GetArray([out/*,retval*/]
    SAFEARRAY(unsigned char) *pData);
};

// 设定对象
STDMETHODIMP CBolbData::SetArray(SAFEARRAY *pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())

// 创建CSimpleObj的亚元指针
  CSimpleObj *dummy=NULL;
// 创建 blob 对象 用来填充、反序列化
  CBlob blob;
// 使用 safearray 创建亚元对象
  blob.Expand( (CObject*&)dummy, pData );
  dummy->Show(); // 调用显示函数测试对象
  delete dummy; //删除指针
  return S_OK;
}

// 创建对象 并发送给客户机.
STDMETHODIMP CBolbData::GetArray(SAFEARRAY **pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())
// 创建对象并发送给服务器
  CSimpleObj *pMyOb = new CSimpleObj();

//设定字符串数据
  pMyOb->SetString( "A SAFEARRAY from the server!" );

// 创建blob来序列化对象
  CBlob blob;

// 将对象载入blob
  *pData = blob.Load( pMyOb );
// 删除pMyOb指针
  delete pMyOb;

  return S_OK;
}

最后,写一个有两个按钮的基于对话框的 MFC 应用程序 并添加如下代码:
  void CClientDlg::OnOK()
{
// 从CLSID串创建COM智能指针
try
{
  IBolbDataPtr pI( "Server.BolbData.1" );
  SAFEARRAY *psa ;

// 从服务器取得 safearray
  pI->GetArray( &psa );

// 创建指针
  CSimpleObj *dummy=NULL;

// blob 对象
  CBlob blob;

//使用blob 扩展 safearray 到一个对象里
  blob.Expand( (CObject *&)dummy, psa );
//通过调用一个对象的方法来测试它
  dummy->Show();

// 删除对象
  delete dummy;
}
// 通过智能指针处理任意 COM 异常
catch (_com_error e)
{
// 显示错误信息
  AfxMessageBox( e.ErrorMessage() );
}
}

void CClientDlg::OnLoad()
{
try
{
// 从CLSID 串创建智能指针
  IBolbDataPtr pI( "Server.BolbData.1" );
  SAFEARRAY *psa ;

// 创建送给服务器的对象
  CSimpleObj *pMyOb = new CSimpleObj();

// 设置字符串数据
  pMyOb->SetString( "The client sent a SAFEARRAY!" );

// 创建 blob 用来序列化对象
  CBlob blob;

// 将对象载入到 blob
  psa = blob.Load( pMyOb );

//删除对象
  delete pMyOb;

  pI->SetArray( psa );

}
catch (_com_error e)
{
// 显示错误信息
  AfxMessageBox( e.ErrorMessage() );
}
}


总结

  这篇文章包含了很多的主题:例如 怎样使用序列化,
怎样使用 SAFEARRAY,和怎样通过接口传递C++对象。
我要感谢William Rubin,他的文章对我帮助很大,
我曾经计划把这个主题解释的更详细,但由于时间不足我无法完成,
然而我会不断的更新这篇文章,在这期间,请不用客气的跟我联系

 

http://www.codeguru.com/code/legacy/atl/passing.zip

 

 

 

 

 

 

 

 


 

您可能感兴趣的与本文相关的镜像

Wan2.2-T2V-A5B

Wan2.2-T2V-A5B

文生视频
Wan2.2

Wan2.2是由通义万相开源高效文本到视频生成模型,是有​50亿参数的轻量级视频生成模型,专为快速内容创作优化。支持480P视频生成,具备优秀的时序连贯性和运动推理能力

<think>好的,我现在需要处理用户关于Linux进程管理的面试问题总结请求。首先,用户提供了三个引用内容,我需要仔细查看这些引用,提取相关信息,并组织成结构清晰的回答。 首先看引用[1],里面提到了ps命令的各种参数,比如a、X、f、e、l、u等,还有排序进程的方法。这些是进程查看和监控的基础知识点,必须包含在回答中。另外,用户可能关心如何根据CPU或内存使用排序进程,这在面试中常见。 接下来是引用[3],关于僵尸进程的形成原因和解决方法。这部分内容需要详细解释,因为僵尸进程是面试中的高频问题。需要说明僵尸进程的定义,父进程如何处理,以及如何避免或清理僵尸进程。 虽然引用[2]主要关于Linux目录结构,但可能与进程管理关联不大,不过还是要注意是否有相关内容。比如,是否有进程相关的配置文件存放在/etc目录下,或者日志在/var目录下,但用户的问题集中在进程管理,所以可能不需要深入这部分。 用户的需求是获取面试相关的知识点总结,所以需要分点整理,结构清晰。常见的面试问题可能包括进程状态、查看和管理进程的命令、前后台进程切换、僵尸进程和孤儿进程的区别、进程间通信方式等。 需要确保覆盖以下内容: 1. 进程基本概念:状态、PID、PPID等。 2. 常用命令:ps、top、htop、kill、pkill、pgrep、jobs、bg/fg。 3. 进程优先级和nice值调整。 4. 僵尸进程和孤儿进程的处理。 5. 进程间通信(IPC)方法。 6. 守护进程的概念和创建。 7. 性能监控工具如vmstat、iostat等,虽然用户没提到,但可能相关。 需要结合引用[1]中的ps参数示例,比如ps aux、ps -ef,以及排序命令ps aux --sort=%mem或%cpu。同时,引用[3]中的僵尸进程需要详细说明,包括如何检测和解决,比如通过wait调用或终止父进程。 另外,用户提供的引用中有关于ps命令的参数,可能需要解释每个参数的作用,例如a显示所有进程,u显示用户信息,f显示进程树等。这些参数在面试中常被问到,需要正确解释。 可能还需要补充一些用户引用中没有提到的内容,例如top命令的交互操作(如按P按内存排序)、信号的使用(如kill -9、SIGTERM等),以及进程的前后台切换(&、jobs、fg、bg)。 需要确保回答结构清晰,分点列出,每个部分有重点,并适当举例。例如,在解释ps命令时,给出常用组合命令;在讲解僵尸进程时,说明如何通过ps查看僵尸状态(Z状态)等。 最后,生成相关问题时要围绕进程管理的常见面试问题,比如进程状态转换、命令使用场景、问题排查方法等。</think>### Linux进程管理面试知识点总结 #### 一、基础概念 1. **进程状态** Linux进程状态包括:运行中(R)、可中断睡眠(S)、不可中断睡眠(D)、僵尸(Z)、停止(T)等[^3]。 可通过`ps aux`查看进程状态,例如僵尸进程会标记为`Z`。 2. **PID与PPID** - 每个进程有唯一**进程ID(PID)**,父进程ID为**PPID**。 - 使用`ps -ef`查看进程父子关系,`pstree`以树状图显示层级。 --- #### 二、进程管理命令 1. **查看进程** - `ps`命令常用组合: - `ps aux`:显示所有进程的详细信息(用户、CPU/内存占用等)[^1] - `ps -ef`:显示完整格式的进程列表 - `ps -eLf`:查看线程信息 - 按资源排序: ```bash ps aux --sort=-%cpu # 按CPU降序 ps aux --sort=%mem # 按内存升序 ``` 2. **动态监控** - `top`:实时查看系统资源占用,支持交互操作(按`P`/`M`排序CPU/内存) - `htop`(增强版):支持颜色高亮和鼠标操作 3. **进程终止** - `kill -9 PID`:强制终止进程(发送SIGKILL信号) - `pkill -f "进程名"`:根据名称批量终止进程 --- #### 三、进程调度与控制 1. **前台/后台切换** - `&`:后台运行程序(如`python script.py &`) - `jobs`:查看后台任务列表 - `fg %n`:将后台任务n切换到前台 - `bg %n`:恢复暂停的后台任务 2. **优先级调整** - `nice -n 值 命令`:启动时设置优先级(范围-20~19,值越小优先级越高) - `renice -n 值 -p PID`:修改运行中进程的优先级 --- #### 四、僵尸进程与孤儿进程 1. **僵尸进程(Zombie)** - **成因**:子进程退出后,父进程未调用`wait()`回收其资源[^3]。 - **检测**:`ps aux | grep &#39;Z&#39;` - **解决**:终止父进程(僵尸进程会由init进程接管并清理) 2. **孤儿进程** - 父进程先于子进程退出,子进程由init进程(PID=1)接管,不会造成资源泄漏。 --- #### 五、高级话题 1. **进程间通信(IPC)** 常见方式:管道(`|`)、信号(如`kill`)、共享内存、消息队列、Socket。 2. **守护进程(Daemon)** - 特点:脱离终端长期运行(如httpd、mysqld) - 创建步骤:`fork()`子进程 → 脱离会话组`setsid()` → 修改工作目录`chdir("/")` 3. **性能分析工具** - `vmstat`:监控系统整体资源(CPU、内存、IO) - `strace`:跟踪进程系统调用 - `lsof -p PID`:查看进程打开的文件描述符 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值