Last Date: 2015-09-17
Last Date: 2015-10-10
Revision:3我们需要“ctiveX Control Test Container”来测试我们的程序。
//#include <QAxObject> //#include <QDebug> QAxObject *_rtxObject = new QAxObject(); if (!_rtxObject->setControl("{1AC261EA-1D71-4EDA-89AC-E9677EE1F341}")) { qDebug() << "加载ATL Control对象失败。"; return -1; } QVariantList params; params << 3 << 5; //flash->dynamicCall("LoadMovie(long,string)",0,"d:/b.swf"); QString qtResult = _rtxObject->dynamicCall("Add(int, int)", params).toString(); qDebug() << "3+5=" << qtResult;
第三部份:COM组件如何回调client方法的示例!
方法一:参考资料[7]《Adding an Event (ATL Tutorial, Part 5》IDE会为你自动添加事件接口代码
这里要注意下面两个步骤,因为容易疏漏所以这里在写一遍。
[a]在Class View下展开XXXLib后,右键单击_IXXXEvents添加method,实现在idl文件中添加method.
假设method name为“eventNotify”
[b]在Class View下,右键单击CXXX,选择“Add Connection Point...”,建立“eventNotify”的实现“Fire_eventNotify”。
方法二:(不推荐,因为,手动添加代码容易出错)
参考了资料[3],但是其中两步步骤又有不同,下面是具体我的步骤。#pragma once template<class T> class CProxy_IMySimpleATLEvents : public ATL::IConnectionPointImpl<T, &__uuidof(_IMySimpleATLEvents)> { public: HRESULT Fire_MyEvent(LONG op) { HRESULT hr = S_OK; T * pThis = static_cast<T *>(this); int cConnections = m_vec.GetSize(); for (int iConnection = 0; iConnection < cConnections; iConnection++) { pThis->Lock(); CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection); pThis->Unlock(); IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p); if (pConnection) { CComVariant avarParams[1]; avarParams[0] = op; avarParams[0].vt = VT_I4; CComVariant varResult; DISPPARAMS params = { avarParams, NULL, 1, 0 }; hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL); } } return hr; } };
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestATLControl { class Program { void MyCallback(int op) { Console.WriteLine(op); } static void Main(string[] args) { Program program = new Program(); TestATLCallbackLib.MySimpleATL obj = new TestATLCallbackLib.MySimpleATL(); obj.MyEvent += program.MyCallback; obj.TestCallback(123); } } }
第四部分:自定义数据类型
如何自定义数据类型?
第一步:修改idl文件,在library关键词的外面加入下面的代码
typedef [ uuid(C21871AF-33EB-11D4-A13A-BE2573A1120F), version(1.0), helpstring("A Demo UDT variable for CSharp projects") ] struct UDTVariable { [helpstring("Special case variant")] VARIANT Special; [helpstring("Name of the variable")] BSTR Name; [helpstring("Value of the variable")] long Value; } UDTVariable; typedef [ uuid(C21871FF-33EB-11D4-A13A-BE2573A1120F), version(1.0), helpstring("A Demo UDT Holding an Array of Named Variables") ] struct UDTArray { [helpstring("array of named variables")] SAFEARRAY(UDTVariable) NamedVars; } UDTArray;
struct UDTVariable; struct UDTArray;这样C#能找到这些定义。
KagulaAxForTestLib.UDTVariable udtv = new KagulaAxForTestLib.UDTVariable(); udtv.Name = "0123456789abc"; udtv.Value = 123; Console.WriteLine(udtv.Name); Console.WriteLine("10 times: "+he.GetUDT(ref udtv).Value);具体参考资料[9]
const IID UDTVariable_IID = { 0xC21871AF, 0x33EB, 0x11D4, { 0xA1, 0x3A, 0xBE, 0x25, 0x73, 0xA1, 0x12, 0x0F } };
// KagulaHavingEvent.cpp : Implementation of CKagulaHavingEvent #include "stdafx.h" #include "KagulaHavingEvent.h" //在这里定义UDTVariable_IID仅仅是为了创建数组用。 const IID UDTVariable_IID = { 0xC21871AF, 0x33EB, 0x11D4, { 0xA1, 0x3A, 0xBE, 0x25, 0x73, 0xA1, 0x12, 0x0F } }; // CKagulaHavingEvent STDMETHODIMP CKagulaHavingEvent::IndirectCall(BSTR cstrMsg, LONG* retVal) { Fire_eventNotify(cstrMsg,retVal); return S_OK; } STDMETHODIMP CKagulaHavingEvent::GetUDT(UDTVariable* input, UDTVariable* retVal) { retVal->Value = input->Value*10; return S_OK; } STDMETHODIMP CKagulaHavingEvent::UDTSequence(LONG start, LONG length, SAFEARRAY ** SequenceArr) { if( !SequenceArr ) return( E_POINTER ); if( length <= 0 ) { HRESULT hr = Error( _T("Length must be greater than zero") ); return( hr ); } if( *SequenceArr != NULL ) { ::SafeArrayDestroy( *SequenceArr ); *SequenceArr = NULL; } ////////////////////////////////////////////////// //here starts the actual creation of the array ////////////////////////////////////////////////// IRecordInfo *pUdtRecordInfo = NULL; HRESULT hr = GetRecordInfoFromGuids( LIBID_KagulaAxForTestLib, 1, 0, 0, UDTVariable_IID, &pUdtRecordInfo ); if( FAILED( hr ) ) { HRESULT hr2 = Error( _T("Can not create RecordInfo interface for UDTVariable") ); return( hr ); //Return original HRESULT hr2 is for debug only } SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; rgsabound[0].cElements =length; *SequenceArr = ::SafeArrayCreateEx( VT_RECORD, 1, rgsabound, pUdtRecordInfo ); pUdtRecordInfo->Release(); //do not forget to release the interface if( *SequenceArr == NULL ) { HRESULT hr = Error( _T("Can not create array of UDTVariable structures") ); return( hr ); } ////////////////////////////////////////////////// //the array has been created ////////////////////////////////////////////////// SequenceByElement(start, length, *SequenceArr ); return S_OK; } HRESULT CKagulaHavingEvent::SequenceByElement( long start, long length, SAFEARRAY *SequenceArr ) { long lBound = 0; VARIANT a_variant; UDTVariable a_udt;//在idl文件中添加UDTVariable结构定义后,build idl文件后,这里就可以用了。 HRESULT hr = SafeArrayGetLBound( SequenceArr, 1, &lBound ); if( FAILED( hr ) ) return( hr ); BSTR strDefPart = ::SysAllocString( L"Named " ); ::VariantInit( &a_variant ); for( long i = lBound; i<length; i++, start++ ) { //Value字段 a_udt.Value = start; //i holds the sequence value //Special字段 if( i & 1 ) { a_udt.Special.vt = VT_R8; a_udt.Special.dblVal = double( start ); a_udt.Special.dblVal += 0.5; } else { a_udt.Special.vt = VT_I4; a_udt.Special.lVal = start; } //Name字段 hr = ::VariantChangeType( &a_variant, &a_udt.Special, 0, VT_BSTR ); hr = ::VarBstrCat( strDefPart, a_variant.bstrVal, &a_udt.Name ); //放到数值里 hr = ::SafeArrayPutElement( SequenceArr, &i, (void*)&a_udt ); if( FAILED(hr) ) return( hr ); ::VariantClear( &a_variant ); //frees the Name string } ::SysFreeString( strDefPart ); return S_OK; }
Array array = he.UDTSequence(0, 9); foreach (KagulaAxForTestLib.UDTVariable item in array) { Console.WriteLine("item.Name=[" + item.Name + "],item.Value=[" + item.Value + "],item.Special=[" + item.Special + "]"); }
如何在MFC App中以最简单的方式调用Com服务?
Step1: 对于不是完整的ActiveX控件,打开class view。
[Add Class From Typelib]->[Add class from Registry]->[Available type libraries]
选择Cat8637AxLib,Interfaces中选择你想要导入的interface。
Last Step:
加入新建的头文件引用"CKagulaLogger.h"
void CTestAxInMFCDlg::OnBnClickedBtnLog() { CKagulaLogger *logger = new CKagulaLogger(); //下面的参数来自于idl文件KagulaLogger的uuid,虽然我们在Typelib class wizard中选择的是IKagulaLogger if (logger->CreateDispatch(L"{0648AEB4-CE9A-4FBE-BFB2-60E79F51CFD3}")) { logger->Init(L"d:\\a.log", L"d:\\a.bak.log", 1024 * 8); logger->Trace(L"来自C++ com client的中文信息"); logger->Info(L"this is trace info"); logger->Error(L"this is error info"); logger->ReleaseDispatch(); } delete logger; }
STDMETHODIMP CSimpleATLDLGController::InvokeDialog(void) { CSimpleATLDialog atlDLg; atlDLg.DoModal(); return S_OK; }
如何响应外界的消息,参考资料[7]
{ QVariantList params; params << QStringLiteral("ss这是来自QT5的信息"); _rtxObject->dynamicCall("Trace(string)", params); } { QVariantList params; QString qsStr = QString::fromLocal8Bit("中文测试"); params << qsStr; _rtxObject->dynamicCall("Trace(string)", params); }
[4]使用ConvertBSTRToString内存泄露问题
//使用_com_util::ConvertBSTRToString转数据类型,需要把临时指针delete掉,否则会内存泄露。 //BSTR level //#include <comutil.h> //#pragma comment(lib, "comsuppw.lib") char *p =_com_util::ConvertBSTRToString(level); string sLevel = p; delete p;