ComInterop - 平台互操作

Posted on 2006-06-24 01:03  xrye  阅读(902)  评论(0编辑  收藏  举报

在博客园注册了半年,也潜水了半年。今天第一次写随笔,希望大家包涵。

相信很多朋友都有过平台调用方面的经验,但是从网上搜索的资料来看,好像不是很乐观。
根据我自己在使用C++, C#混合调用过程中的心得,小结成本文,希望可以给感兴趣或需要的朋友一些启示。
因为我只对C#语言稍微熟悉一点,所以本文提到的.Net平台都以C#为例。

1. 在C#语言环境中使用C++ dll文件中的函数: 使用DllImport。
这个在msdn中有很详细的介绍,包括数据封送处理,以及C++/C#的基本类型对照等等。

2.在C++语言环境中使用托管C#组件。

2.1 包装类。

C#中的Regex提供了强大的功能,我们想在C++代码中使用它,为此我们需要给Regex进行包装,定义了如下RegExp类。

RegExp.cs

using System;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

namespace DotNetLib
{

    [Guid(
"0F0D2A66-0262-47b5-A7F9-8BFFB8A13F90"),ComVisible(true)]
    
public interface iRegExp
    {
        
string Replace(string input, string pattern, string replacement);
        
string[] Split(string input, string pattern);
    }

    
/// <summary>
    
/// Wrapper of Regex class to provide 
    
/// it's powerful function of dealing with text.
    
/// This will generate a COM class which can be
    
/// used by various platforms or languages.
    
/// </summary>
    [Guid("2634BEA5-17FF-40ba-A837-12D30BAD38D5"),ClassInterface(ClassInterfaceType.None),ComSourceInterfaces(typeof(iRegExp)),ComVisible(true)]
    
public class RegExp: iRegExp
    {
        
public RegExp()
        {
        }

        
#region iRegExp Members

        
//[return: MarshalAs(UnmanagedType.LPWStr)]
        public string Replace(string input, string pattern, string replacement)
        {
            
return Regex.Replace(input,pattern,replacement);
        }

        
public string[] Split(string input, string pattern)
        {
            
return Regex.Split(input,pattern);
        }


        
#endregion
    }
}

将该文件编译成Dll文件,然后使用Regasm进行注册,会产生相应的类型库文件,我们假定为DotNetLib.tlb. 至此我们的包装工作完成。

2.2 映射类。

因为我们是通过Com调用的,所以要进行很多数据转换工作,为了避免这些细节,我们定义一个映射类,使得用户使用该类就像使用真正的Regex类一样。

ComInterface.h

#pragma once
#include 
<comutil.h>

class ComInterface
{
public:
    ComInterface(REFCLSID rCLSID);
    ComInterface(IUnknown
* piUnknown);
    
~ComInterface(void);

protected:
    IUnknown
*   m_piUnknown;

private:
    IDispatch
*  m_piDispatch;
    
static bool m_bCoInitialize;
};


ComInterface.cpp

#include "StdAfx.h"
#include 
"ComInterface.h"
#include 
<comutil.h>

bool ComInterface::m_bCoInitialize = false;

ComInterface::ComInterface(REFCLSID rCLSID)
{
    m_piUnknown 
= NULL;
    m_piDispatch 
= NULL;

    
if (!m_bCoInitialize)
        CoInitialize(NULL);

    HRESULT hr 
= CoCreateInstance(rCLSID,NULL,CLSCTX_INPROC_SERVER,
        IID_IUnknown,(
void**)&m_piUnknown);
    ATLASSERT(SUCCEEDED(hr));

    hr 
= m_piUnknown->QueryInterface(IID_IDispatch,(void**)&m_piDispatch);
    ATLASSERT(SUCCEEDED(hr));
}

ComInterface::ComInterface(IUnknown
* piUnknown)
{
    m_piUnknown 
= piUnknown;
    m_piDispatch 
= NULL;

    HRESULT hr 
= m_piUnknown->QueryInterface(IID_IDispatch,(void**)&m_piDispatch);
    ATLASSERT(SUCCEEDED(hr));
}

ComInterface::
~ComInterface(void)
{
    
if (m_piDispatch)
        m_piDispatch
->Release();

    
if (m_piUnknown)
        m_piUnknown
->Release();
    
    
if (m_bCoInitialize)
    {
        m_bCoInitialize 
= false;
        CoUninitialize();
    }
}


RegExp.h

#pragma once
#include 
"atlsafe.h"
#include 
"ComInterface.h"
#import 
"E:\\MyDocument\\MyProjects\\ComInteropTest\\DotNetLib\\lib\\DotNetLib.tlb" named_guids
using namespace DotNetLib;

class CRegExp : public ComInterface
{
public:
    CRegExp();
    
virtual ~CRegExp();
    CString Replace(
const CString& strInput, const CString& strParttern,
        const CString& strReplacement);
    
bool Split(const CString& strInput, const CString& strParttern,
        SAFEARRAY
** ppSafeArray);

private:
    iRegExp
* m_piRegExp;
};


RegExp.cpp

#include "StdAfx.h"
#include 
".\RegExp.h"

CRegExp::CRegExp()
: ComInterface(CLSID_RegExp)
{
    HRESULT hr 
= m_piUnknown->QueryInterface(IID_iRegExp,(LPVOID*)&m_piRegExp);
    ATLASSERT(SUCCEEDED(hr));
}

CRegExp::
~CRegExp()
{
    
if (m_piRegExp)
        m_piRegExp
->Release();
}

CString CRegExp::Replace(
const CString& strInput, const CString& strParttern,
    
const CString& strReplacement)
{
    CString strResult;

    
if (m_piRegExp)
    {
        strResult 
= m_piRegExp->Replace(_bstr_t(strInput),_bstr_t(strParttern),
            _bstr_t(strReplacement)).GetBSTR();
    }
    
else
        strResult  
= _T("");

    
return strResult;
}

bool CRegExp::Split(const CString& strInput, const CString& strParttern,
    SAFEARRAY
** ppSafeArray)
{
    
if (m_piRegExp)
    {
        
*ppSafeArray = m_piRegExp->Split(_bstr_t(strInput),_bstr_t(strParttern));

        
return true;
    }
    
else
        
return false;
}


至此,映射类也定义完成了。我们可以做个简单的测试:
  CRegExp exp;
  CString strTest = _T("he is a good boy, he is very like his father");
  strTest = exp.Replace(strTest, _T("he"), _T("she"));
  strTest = exp.Replace(strTest, _T("boy", _T("girl"));
  strTest = exp.Replace(strTest, _T("his"), _T("her"));
  strTest = exp.Replace(strTest, _T("father"), _T("mother"));
// ok, now strTest should be "she is a good girl, she is very like her mother".

包装的方案比较笨拙,代码的执行效率也不高,但是目前我还没有找到更好的办法。 如果有这方面的达人,希望补充一下。