您的位置:首页 > 其它

MFC调用mstscax.dll控件实现远程连接(添加消息回调)

2017-12-14 18:33 1611 查看
在之前的文章中 http://blog.csdn.net/wochendaixin/article/details/78412196 href="http://blog.csdn.net/wochendaixin/article/details/78412196" target=_blank>点击打开链接

基本已经实现了通过MFC调用微软的mstscax.dll进行远程连接,

最近因为需要完善一个工程需要在此基础上还能得到远程连接时的各种状态:开始连接、连接完成、连接中断等等事件状态。因此下面就以MFC工程下的处理为例:

工程代码下载:

实验环境:

win10 vs2017

实验流程:

(1)参考:http://blog.csdn.net/linlin003/article/details/52922741

建立MFC对话框;添加一个RDp控件,为其添加变量。

(2) 在你需要启动远程连接的地方,开始初始化和连接动作:

m_RdpControl.put_Server(_T("192.168.2.120"));//远程目标主机的IP
m_RdpControl.put_UserName(_T("JX"));//登陆用户名

m_RdpControl.put_ColorDepth(32);

//CMsRdpClientAdvancedSettings6 m_MsRdpClientAdvancedSettings = m_RdpControl.get_AdvancedSettings();
m_MsRdpClientAdvancedSettings = m_RdpControl.get_AdvancedSettings();
m_MsRdpClientAdvancedSettings.put_Compress(1);//此方法启用或禁用压缩
m_MsRdpClientAdvancedSettings.put_BitmapPeristence(1);//此方法启用或禁用位图缓存
m_MsRdpClientAdvancedSettings.put_SmartSizing(TRUE);//将远程适配
m_MsRdpClientAdvancedSettings.put_ClearTextPassword(_T("111111"));//登陆密码。 以明文形式设置密码,此方法可用于以明文形式提供连接密码
m_MsRdpClientAdvancedSettings.put_singleConnectionTimeout(30);//连接时延
m_MsRdpClientAdvancedSettings.put_EnableCredSspSupport(TRUE);//身份验证问题参考设置 :https://www.cnblogs.com/yuefei/p/4390510.html
m_RdpControl.put_ConnectingText(_T("连接中..."));//连接时显示在界面的文字
m_RdpControl.Connect();


(3) 上面的代码就基本实现了远程连接功能,接下来添加这个控件的消息响应。

方式一:

利用MFC的 EVENTSINK_MAP,具体的用法可以网上查找十分清晰。

但这里最麻烦的是如何去得到RDP控件的相关事件。各种状态一一对应的事件ID是什么

我也是找了大半天也是一无所获,最后偶然利用MFC中的添加 MsTscAxEvents接口的类之后才发现了各种事件的ID:这里贴出如下:

//事件ID

void OnConnecting()
{
InvokeHelper(0x1, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void OnConnected()
{
InvokeHelper(0x2, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void OnLoginComplete()
{
InvokeHelper(0x3, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void OnDisconnected(long discReason)
{
static BYTE parms[] = VTS_I4;
InvokeHelper(0x4, DISPATCH_METHOD, VT_EMPTY, NULL, parms, discReason);
}
void OnEnterFullScreenMode()
{
InvokeHelper(0x5, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void OnLeaveFullScreenMode()
{
InvokeHelper(0x6, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}


根据上面的事件ID 我就简单选用了几个放在EVENT_MAP()中:如下:

BEGIN_EVENTSINK_MAP(CMfcRDPcontrolDlg, CDialog)

ON_EVENT(CMfcRDPcontrolDlg, IDC_MSTSCAX1, 4, CMfcRDPcontrolDlg::OnDisconnectedMstscax1, VTS_I4)
ON_EVENT(CMfcRDPcontrolDlg, IDC_MSTSCAX1, 1, CMfcRDPcontrolDlg::OnConnectingMstscax1, VTS_NONE)
ON_EVENT(CMfcRDPcontrolDlg, IDC_MSTSCAX1, 2, CMfcRDPcontrolDlg::OnConnectedMstscax1, VTS_NONE)
ON_EVENT(CMfcRDPcontrolDlg, IDC_MSTSCAX1, 3, CMfcRDPcontrolDlg::OnLoginCompleteMstscax1, VTS_NONE)
ON_EVENT(CMfcRDPcontrolDlg, IDC_MSTSCAX1, 22, CMfcRDPcontrolDlg::OnLogonErrorMstscax1, VTS_I4)

END_EVENTSINK_MAP()


这样你便可以在各个函数中做出相应的处理:例如:
void CMfcRDPcontrolDlg::OnConnectedMstscax1()

{

AfxMessageBox(_T("您已经连接到远程"));

}

方式二:
利用EVENT事件可以简单有效的得到这个控件的所有状态变化从而相应的做出处理。但是假如一个工程包含多个控件时你怎么知道哪一个状态发生改变呢?(当然我没有去做这种实验),
我又尝试了另一方法,利用com组件的回调。
这种方法是查网上资料时借鉴的: http://www.guanggua.com/question/43362388-vmconnect-do-not-work-with-port-2179.html
LPUNKNOWN lpUnk = m_RdpControl.GetControlUnknown();
lpUnk->QueryInterface(__uuidof(MSTSCLib::IMsRdpClient), (void**)& pInterface);

//关联 事件
pCTscEventSink = new CTscEventSink();
BOOL b = pCTscEventSink->Attach(pInterface);


简单地说,就是从我们 添加在工程中的RDP控件里,找到一个 MSTSCLib::IMsRdpClient 接口 pInterface:
然后,用这个接口来进行事件的关联。这一块涉及com组件的知识,我看的迷糊只是搬运了一下上面那个网址的处理。他封装了一个有关事件接口的类 CTscEventSink,通过调用类的Attach()成员函数就可以把事件关联到接口(这里没看懂 = 。= 三分靠搜索,七分靠猜测。)

在这个网页里封装的CTscEventSink其中并没有添加太多的消息处理,我结合上面的那一段事件的ID,稍微丰满了一下这个类:

class CTscEventSink : public IMsTscAxEvents
{
public:
CTscEventSink()
{
_ulRefs = 1;
m_dwEvtCookie = 0;
}

~CTscEventSink()
{
}

//绑定 事件
BOOL    Attach(IMsTscAx* pTscAx)
{
CComPtr<IConnectionPointContainer>    cpCPCont;
HRESULT hr;

hr = pTscAx->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&cpCPCont);
if (FAILED(hr))
return FALSE;

hr = cpCPCont->FindConnectionPoint(__uuidof(IMsTscAxEvents), &m_cpConnect);
if (FAILED(hr))
{
return FALSE;
}

hr = m_cpConnect->Advise(this, &m_dwEvtCookie);
if (FAILED(hr))
{
m_dwEvtCookie = 0;
return FALSE;
}

return TRUE;
}

//解除绑定
void    Detach()
{
if ((m_dwEvtCookie) && (m_cpConnect))
m_cpConnect->Unadvise(m_dwEvtCookie);
if (m_cpConnect)
m_cpConnect = NULL;
}

//各种情况事件的处理,在invoke里分情况调用

void OnLoginComplete()
{
AfxMessageBox(_T("Login complete!"));
}

void OnLogonError(
long lError)
{
AfxMessageBox(_T("OnLogonError"));

}

void OnDisconnected(
long discReason)
{
switch (discReason)
{
case 0x3:
{
AfxMessageBox(_T("该用户在别处登录,您已经被强制下线!"));
break;
}
default:
AfxMessageBox(_T("远程连接失败,请重试!"));
break;
}
}

void Onconnected()
{
AfxMessageBox(_T("远程连接成功!"));
}

//IUnknown Methods
STDMETHOD(QueryInterface) (REFIID riid, LPVOID * ppv)
{
if (riid == __uuidof(IMsTscAxEvents))
{
*ppv = (IMsTscAxEvents *)this;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}

STDMETHOD_(ULONG, AddRef) (void)
{
InterlockedIncrement((LONG*)&_ulRefs);
return _ulRefs;
}

STDMETHOD_(ULONG, Release) (void)
{
ULONG ulRefs = _ulRefs;
if (InterlockedDecrement((LONG*)&_ulRefs) == 0)
{
delete this;
return 0;
}
return _ulRefs;
}

//IDispatch Methods

STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo)
{
return E_NOTIMPL;
}

STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
return E_NOTIMPL;
}

STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
LCID lcid, DISPID FAR* rgdispid)
{
return E_NOTIMPL;
}

STDMETHOD(Invoke)(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pdispparams,
VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo,
UINT FAR* puArgErr)
{
//MessageBoxW(NULL, _T("invoke"), _T("提示"), MB_OK);

//得到事件,调用对应功能
VARIANT x;
unsigned int y = 0;
HRESULT hr = 0;

switch (dispidMember)
{
case 1: {	break; }
case 2:
{
Onconnected();
break;
}
case 3:
{
OnLoginComplete();
break;
}
case 4:
{
x.vt = VT_I4;
x.pdispVal = NULL;
hr = DispGetParam(pdispparams, 0, VT_I4, &x, &y);//
OnDisconnected(x.intVal);
break;
}
default:
break;
}
return S_OK;
}

protected:
ULONG        _ulRefs;
DWORD                        m_dwEvtCookie;
CComPtr<IConnectionPoint>    m_cpConnect;

};


到此基本的事件都可以在:Invoke中进行处理了;
但是最后一个疑问,怎么从Invoke的调用中,得到数据值。
就比如当你的连接被另一个人顶掉了,是会返回一个错误原因的,但怎么去拿这个错误原因,怎么分析这个错误出现对应的实际状况。我只是找了
case 4:
{
x.vt = VT_I4;
x.pdispVal = NULL;
hr = DispGetParam(pdispparams, 0, VT_I4, &x, &y);//
OnDisconnected(x.intVal);
break;
}


可是其它的真是找不到了。

=============结束=======
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: