📄 activex.cpp
字号:
params.cNamedArgs = 0;
MSTSCLib::IMsTscAxEvents ** sinks = GetSinks();
for(size_t i = 0; i < m_EventSinksCount; ++ i)
sinks[i]->Invoke(eventId, IID_NULL, 0, DISPATCH_METHOD, ¶ms, retval, NULL, NULL);
}
typedef void (RdpClient::* AsyncEventCallback)
(
DISPID eventId,
VARIANTARG * rgvarg,
unsigned int cArgs,
VARIANTARG * retVal
);
void CleanupEventArgumentsCallback
(
DISPID eventId,
VARIANTARG * rgvarg,
unsigned int cArgs,
VARIANTARG * retVal
)
{
assert((rgvarg == NULL) == (cArgs == 0));
for(unsigned int i = 0; i < cArgs; ++ i)
VariantClear(&rgvarg[i]);
if(retVal)
VariantClear(retVal);
}
// synchronous call from inside the apartment that owns the object
void FireEventInsideApartment
(
DISPID eventId,
VARIANTARG * rgvarg = NULL,
unsigned int cArgs = 0,
VARIANTARG * retval = NULL,
AsyncEventCallback callback = NULL
)
{
assert(InsideApartment());
if(retval == NULL && callback)
{
VARIANTARG localRetval = { };
retval = &localRetval;
}
InvokeSinks(eventId, rgvarg, cArgs, retval);
if(callback)
(this->*callback)(eventId, rgvarg, cArgs, retval);
}
struct EventArguments
{
DISPID eventId;
VARIANTARG * rgvarg;
unsigned int cArgs;
VARIANTARG * retval;
AsyncEventCallback callback;
};
struct RedirectArguments
{
uint32 flags;
uint32 server_len;
wchar_t * server;
uint32 cookie_len;
char * cookie;
uint32 username_len;
wchar_t * username;
uint32 domain_len;
wchar_t * domain;
uint32 password_len;
wchar_t * password;
};
enum
{
RDPC_WM_ = WM_USER,
RDPC_WM_SYNC_EVENT,
RDPC_WM_ASYNC_EVENT,
RDPC_WM_DISCONNECT,
RDPC_WM_REQUEST_CLOSE,
RDPC_WM_REDIRECT,
};
static VOID CALLBACK DisconnectAPC(ULONG_PTR)
{
// no need to do anything. The interruption will be enough
}
bool HandleEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
{
result = 0;
switch(uMsg)
{
/* Regular event to be dispatched to the container's sink */
case RDPC_WM_SYNC_EVENT:
assert(InSendMessage());
case RDPC_WM_ASYNC_EVENT:
{
const EventArguments * eventArgs = reinterpret_cast<EventArguments *>(lParam);
assert(eventArgs);
FireEventInsideApartment
(
eventArgs->eventId,
eventArgs->rgvarg,
eventArgs->cArgs,
eventArgs->retval,
eventArgs->callback
);
if(uMsg == RDPC_WM_ASYNC_EVENT)
delete eventArgs;
}
break;
/* The protocol thread is about to die: prepare for disconnection */
case RDPC_WM_DISCONNECT:
{
assert(m_Connected);
assert(InsideApartment());
assert(InSendMessage());
// Unblock the protocol thread and wait for it to terminate
ReplyMessage(0);
JoinProtocolThread();
// Finish disconnecting
PerformDisconnect(static_cast<long>(wParam));
}
break;
case RDPC_WM_REDIRECT:
{
assert(InSendMessage());
assert(lParam);
assert(m_Connected);
assert(m_protocolState.redirect);
RedirectArguments * redirectArgs = reinterpret_cast<RedirectArguments *>(lParam);
// BUGBUG: this is extremely messy and more prone to out-of-memory than it should be
LPSTR lpszNewServer = NULL;
LPSTR lpszNewCookie = NULL;
BSTR strNewUsername = NULL;
BSTR strNewDomain = NULL;
BSTR strNewPassword = NULL;
HRESULT hr = S_OK;
for(;;)
{
// Allocate the new properties
hr = E_OUTOFMEMORY;
// FIXME: convert the hostname to Punycode, not the ANSI codepage
lpszNewServer = AllocLpsz(redirectArgs->server, redirectArgs->server_len / sizeof(OLECHAR));
if(lpszNewServer == NULL && redirectArgs->server_len)
break;
lpszNewCookie = AllocLpsz(redirectArgs->cookie, redirectArgs->cookie_len);
if(lpszNewCookie == NULL && redirectArgs->cookie_len)
break;
strNewUsername = SysAllocStringLen(redirectArgs->username, redirectArgs->username_len / sizeof(OLECHAR));
if(strNewUsername == NULL && redirectArgs->username_len)
break;
strNewDomain = SysAllocStringLen(redirectArgs->domain, redirectArgs->domain_len / sizeof(OLECHAR));
if(strNewDomain == NULL && redirectArgs->domain_len)
break;
strNewPassword = SysAllocStringLen(redirectArgs->password, redirectArgs->password_len / sizeof(OLECHAR));
if(strNewPassword == NULL && redirectArgs->password_len)
break;
hr = S_OK;
break;
}
// Success
if(SUCCEEDED(hr))
{
// set the new properties
ReplaceProperty(m_Server, lpszNewServer);
ReplaceProperty(m_LoadBalanceInfo, lpszNewCookie);
ReplaceProperty(m_UserName, strNewUsername);
ReplaceProperty(m_Domain, strNewDomain);
ReplaceProperty(m_ClearTextPassword, strNewPassword);
}
// Failure
else
{
// free the buffers
FreeLpsz(lpszNewServer);
FreeLpsz(lpszNewCookie);
SysFreeString(strNewUsername);
SysFreeString(strNewDomain);
SysFreeString(strNewPassword);
// signal the error
m_protocolState.disconnect_reason = 262;
m_protocolState.redirect = False;
result = -1;
}
}
break;
// BUGBUG: this could potentially disconnect an unrelated connection established later...
case RDPC_WM_REQUEST_CLOSE:
{
assert(!InSendMessage());
if(m_Connected)
{
// Ask confirmation to the container in case we are logged in
if(m_loggedIn && !FireConfirmClose())
break;
// For reentrancy (OnConfirmClose could deviously call Disconnect)
if(m_protocolThread == NULL)
break;
// Terminate the protocol thread. It will fire the Disconnected event on exit
TerminateProtocolThread();
}
}
break;
default:
return false;
}
// If the calling thread is blocked, unblock it ASAP
if(InSendMessage())
ReplyMessage(result);
return true;
}
// synchronous call from outside the apartment
void FireEventOutsideApartment
(
DISPID eventId,
VARIANTARG * rgvarg = NULL,
unsigned int cArgs = 0,
VARIANTARG * retval = NULL,
AsyncEventCallback callback = NULL
)
{
assert(!InsideApartment());
EventArguments syncEvent = { eventId, rgvarg, cArgs, retval, callback };
SendMessage(m_controlWindow, RDPC_WM_SYNC_EVENT, 0, reinterpret_cast<LPARAM>(&syncEvent));
}
// asynchronous call from outside the apartment
HRESULT FireEventOutsideApartmentAsync
(
DISPID eventId,
VARIANTARG * rgvarg = NULL,
unsigned int cArgs = 0,
VARIANTARG * retval = NULL,
AsyncEventCallback callback = NULL
)
{
assert(!InsideApartment());
EventArguments * asyncEvent = new EventArguments();
if(asyncEvent == NULL)
return E_OUTOFMEMORY;
asyncEvent->eventId = eventId;
asyncEvent->rgvarg = rgvarg;
asyncEvent->cArgs = cArgs;
asyncEvent->retval = NULL;
if(!PostMessage(m_controlWindow, RDPC_WM_ASYNC_EVENT, 0, reinterpret_cast<LPARAM>(asyncEvent)))
{
delete asyncEvent;
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
// Specific events
void FireConnecting()
{
// Source: protocol
FireEventOutsideApartment(1);
}
void FireConnected()
{
// Source: protocol
FireEventOutsideApartment(2);
}
void FireLoginComplete()
{
// Source: protocol
FireEventOutsideApartment(3);
}
void FireDisconnected(long reason)
{
// Source: protocol. Special handling
SendMessage(m_controlWindow, RDPC_WM_DISCONNECT, reason, 0);
}
void FireEnterFullScreenMode()
{
// Source: UI window
FireEventInsideApartment(5);
}
void FireLeaveFullScreenMode()
{
// Source: UI window
FireEventInsideApartment(6);
}
HRESULT FireChannelReceivedData(char (& chanName)[CHANNEL_NAME_LEN + 1], void * chanData, unsigned int chanDataSize)
{
// BUGBUG: what to do when we run out of memory?
OLECHAR wchanName[ARRAYSIZE(chanName)];
std::copy(chanName + 0, chanName + ARRAYSIZE(chanName), wchanName);
BSTR bstrChanName = SysAllocString(wchanName);
if(bstrChanName == NULL)
return E_OUTOFMEMORY;
BSTR bstrChanData = SysAllocStringByteLen(NULL, chanDataSize);
if(bstrChanData == NULL)
{
SysFreeString(bstrChanName);
return E_OUTOFMEMORY;
}
CopyMemory(bstrChanData, chanData, chanDataSize);
VARIANTARG args[2] = { };
args[1].vt = VT_BSTR;
args[1].bstrVal = bstrChanName;
args[0].vt = VT_BSTR;
args[0].bstrVal = bstrChanData;
// Source: protocol
HRESULT hr = FireEventOutsideApartmentAsync(7, args, ARRAYSIZE(args), NULL, &RdpClient::CleanupEventArgumentsCallback);
if(FAILED(hr))
CleanupEventArgumentsCallback(7, args, ARRAYSIZE(args), NULL);
return hr;
}
void FireRequestGoFullScreen()
{
// Source: UI window
FireEventInsideApartment(8);
}
void FireRequestLeaveFullScreen()
{
// Source: UI window
FireEventInsideApartment(9);
}
void FireFatalError(long errorCode)
{
VARIANTARG arg = { };
arg.vt = VT_I4;
arg.lVal = errorCode;
// Source: protocol
FireEventOutsideApartment(10, &arg, 1);
}
void FireFatalErrorFromApartment(long errorCode)
{
VARIANTARG arg = { };
arg.vt = VT_I4;
arg.lVal = errorCode;
// Source: control
FireEventInsideApartment(10, &arg, 1);
}
void FireWarning(long warningCode)
{
VARIANTARG arg = { };
arg.vt = VT_I4;
arg.lVal = warningCode;
// Source: protocol
FireEventOutsideApartment(11, &arg, 1);
}
void FireRemoteDesktopSizeChange(long width, long height)
{
VARIANTARG args[2] = { };
args[1].vt = VT_I4;
args[1].lVal = width;
args[0].vt = VT_I4;
args[0].lVal = height;
// Source: UI window
FireEventInsideApartment(12, args, ARRAYSIZE(args));
}
void FireIdleTimeoutNotification()
{
// Source: input thread
FireEventOutsideApartment(13);
}
void FireRequestContainerMinimize()
{
// Source: UI window
FireEventInsideApartment(14);
}
bool FireConfirmClose()
{
VARIANTARG retval = { };
VARIANT_BOOL allowClose = VARIANT_TRUE;
retval.vt = VT_BYREF | VT_BOOL;
retval.pboolVal = &allowClose;
// Source: control
FireEventInsideApartment(15, NULL, 0, &retval);
return allowClose != VARIANT_FALSE;
}
HRESULT FireReceivedTSPublicKey(void * publicKey, unsigned int publicKeyLength)
{
assert(m_Connected);
if(!m_NotifyTSPublicKey)
return S_OK;
BSTR bstrPublicKey = SysAllocStringByteLen(NULL, publicKeyLength);
if(bstrPublicKey == NULL)
return E_OUTOFMEMORY;
CopyMemory(bstrPublicKey, publicKey, publicKeyLength);
VARIANT_BOOL continueLogon = VARIANT_TRUE;
VARIANTARG arg = { };
VARIANTARG retval = { };
arg.vt = VT_BSTR;
arg.bstrVal = bstrPublicKey;
retval.vt = VT_BYREF | VT_BOOL;
retval.pboolVal = &continueLogon;
// Source: protocol
FireEventOutsideApartment(16, &arg, 1, &retval);
return continueLogon ? S_OK : S_FALSE;
}
LONG FireAutoReconnecting(long disconnectReason, long attemptCount)
{
LONG continueStatus = MSTSCLib::autoReconnectContinueAutomatic;
VARIANTARG args[2] = { };
VARIANTARG retval = { };
args[1].vt = VT_I4;
args[1].lVal = disconnectReason;
args[0].vt = VT_I4;
args[0].lVal = attemptCount;
retval.vt = VT_BYREF | VT_I4;
retval.plVal = &continueStatus;
// Source: protocol
FireEventOutsideApartment(17, args, ARRAYSIZE(args), &retval);
return continueStatus;
}
void FireAuthenticationWarningDisplayed()
{
// Source: protocol
FireEventOutsideApartment(18);
}
void FireAuthenticationWarningDismissed()
{
// Source: protocol
FireEventOutsideApartment(19);
}
/* Actual IUnknown implementation */
HRESULT queryInterface(REFIID riid, void ** ppvObject)
{
IUnknown * pvObject = NULL;
using namespace MSTSCLib;
if(riid == IID_IUnknown)
pvObject = static_cast<IUnknown *>(&m_inner);
else if(riid == IID_IConnectionPointContainer)
pvObject = static_cast<IConnectionPointContainer *>(this);
else if(riid == IID_IDataObject)
pvObject = static_cast<IDataObject *>(this);
else if(riid == IID_IObjectSafety)
pvObject = static_cast<IObjectSafety *>(this);
else if(riid == IID_IOleControl)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -