windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/as/cpi32/asmaster.cpp
2020-09-26 16:20:57 +08:00

743 lines
15 KiB
C++

#include "precomp.h"
#include <RegEntry.h>
#include <asmaster.h>
#define MLZ_FILE_ZONE ZONE_CORE
ASMaster *g_pMaster = NULL;
HRESULT WINAPI CreateASObject
(
IAppSharingNotify * pNotify,
UINT flags,
IAppSharing** ppAS
)
{
HRESULT hr = E_OUTOFMEMORY;
ASMaster * pMaster = NULL;
DebugEntry(CreateASObject);
ASSERT(ppAS);
if (g_pMaster != NULL)
{
ERROR_OUT(("CreateASObject: IAppSharing * alreadycreated; only one allowed at a time"));
hr = E_UNEXPECTED;
DC_QUIT;
}
ASSERT(!g_asMainThreadId);
ASSERT(!g_putAS);
pMaster = new ASMaster(flags, pNotify);
if (pMaster != NULL)
{
//
// Register as the groupware primary, with an event proc but no exit proc
//
if (!UT_InitTask(UTTASK_UI, &g_putUI))
{
ERROR_OUT(("Failed to register UI task"));
DC_QUIT;
}
UT_RegisterEvent(g_putUI, eventProc, g_putUI, UT_PRIORITY_NORMAL);
// Start groupware thread.
if (!DCS_StartThread(WorkThreadEntryPoint))
{
ERROR_OUT(("Couldn't start groupware thread"));
DC_QUIT;
}
// Make sure the work thread initialization is ok
if (! g_asMainThreadId)
{
ERROR_OUT(("Init failed in the work thread"));
DC_QUIT;
}
//
// Success!
//
}
hr = S_OK;
DC_EXIT_POINT:
if (!SUCCEEDED(hr))
{
if (pMaster)
{
ERROR_OUT(("CreateASObject: Init of ASMaster failed"));
pMaster->Release();
pMaster = NULL;
}
}
*ppAS = pMaster;
DebugExitHRESULT(CreateASObject, hr);
return hr;
}
ASMaster::ASMaster(UINT flags, IAppSharingNotify * pNotify) :
m_cRefs (1),
m_pNotify (pNotify)
{
DebugEntry(ASMaster::ASMaster);
if (m_pNotify)
{
m_pNotify->AddRef();
}
ASSERT(!g_pMaster);
g_pMaster = this;
//
// Set up global flags:
// * service
// * unattended
//
g_asOptions = flags;
DebugExitVOID(ASMaster::ASMaster);
}
ASMaster::~ASMaster()
{
DebugEntry(ASMaster::~ASMaster);
//
// Kill any share that's current or pending in the queue
// This will do nothing if no share is extant at the time the
// message is received.
//
if (g_asMainWindow)
{
PostMessage(g_asMainWindow, DCS_KILLSHARE_MSG, 0, 0);
}
//
// Kill off the worker thread
//
if (g_asMainThreadId)
{
PostThreadMessage(g_asMainThreadId, WM_QUIT, 0, 0);
}
//
// Clean up the UI
//
if (g_putUI)
{
UT_TermTask(&g_putUI);
}
// global variables cleanup
if (m_pNotify)
{
m_pNotify->Release();
m_pNotify = NULL;
}
if (g_pMaster == this)
{
g_pMaster = NULL;
}
DebugExitVOID(ASMaster::~ASMaster);
}
STDMETHODIMP ASMaster::QueryInterface(REFIID iid, void ** pv)
{
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) ASMaster::AddRef()
{
InterlockedIncrement(&m_cRefs);
return m_cRefs;
}
STDMETHODIMP_(ULONG) ASMaster::Release()
{
ASSERT(m_cRefs > 0);
if (::InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
return 0;
}
return m_cRefs;
}
//
// WorkThreadEntryPoint()
//
// This is the groupware code--obman, taskloader, and app sharing
//
DWORD WINAPI WorkThreadEntryPoint(LPVOID hEventWait)
{
BOOL result = FALSE;
BOOL fCMGCleanup = FALSE;
BOOL fDCSCleanup = FALSE;
MSG msg;
HWND hwndTop;
DebugEntry(WorkThreadEntryPoint);
//
// Get the current thread ID. This is used in the stop code to know
// if the previous thread is still exiting. In the run-when-windows
// starts mode, our init code is called when Conf brings up UI and our
// term code is called when Conf brings it down. We have a race condition
// because this thread is created on each init. If we create a new
// one while the old one is exiting, we will stomp over each other and
// GP-fault.
//
g_asMainThreadId = GetCurrentThreadId();
//
// Get our policies
//
// Register the call primary code, for T.120 GCC
if (!CMP_Init(&fCMGCleanup))
{
ERROR_OUT(("CMP_Init failed"));
DC_QUIT;
}
//
// Do DCS fast init; slow font enum will happen later off a posted
// message. We can still share & participate in sharing without a
// full font list...
//
fDCSCleanup = TRUE;
if (!DCS_Init())
{
ERROR_OUT(("AS did not initialize"));
DC_QUIT;
}
//
// We've successfully initialised - let the thread which created this
// one continue
//
SetEvent((HANDLE)hEventWait);
//
// Enter the main message processing loop:
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// For dialogs, it's OK to do normal message processing.
//
if (hwndTop = IsForDialog(msg.hwnd))
{
if (!IsDialogMessage(hwndTop, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
//
// Note that this message dispatch loop DOES NOT include a call to
// Translate Message. This is because we do not want it to call
// ToAscii and affect the state maintained internally by ToAscii.
// We will call ToAscii ourselves in the IM when the user is typing
// in a view and calling it more than once for a keystroke
// will cause it to return wrong results (eg for dead keys).
//
// The consequence of this is that any windows which are driven by
// this dispatch loop will NOT receive WM_CHAR or WM_SYSCHAR
// messages. This is not a problem for dialog windows belonging to
// a task using this message loop as the dialog will run its own
// dispatch loop.
//
// If it becomes necessary for windows driven by this dispatch loop
// to get their messages translated then we could add logic to
// determine whether the message is destined for a view
// before deciding whether to translate it.
//
//
// Because we don't have a translate message in our message loop we
// need to do the following to ensure the keyboard LEDs follow what
// the user does when their input is going to this message loop.
//
if (((msg.message == WM_KEYDOWN) ||
(msg.message == WM_SYSKEYDOWN) ||
(msg.message == WM_KEYUP) ||
(msg.message == WM_SYSKEYUP)) &&
IM_KEY_IS_TOGGLE(msg.wParam))
{
BYTE kbState[256];
//
// There is a chance the LEDs state has changed so..
//
GetKeyboardState(kbState);
SetKeyboardState(kbState);
}
DispatchMessage(&msg);
}
}
result = (int)msg.wParam;
//
// We emerge from the processing loop when someone posts us a WM_QUIT.
// We do ObMan specific termination then call UT_TermTask (which will
// call any exit procedures we have registered).
//
DC_EXIT_POINT:
if (fDCSCleanup)
DCS_Term();
if (fCMGCleanup)
CMP_Term();
g_asMainThreadId = 0;
DebugExitDWORD(WorkThreadEntryPoint, result);
return(result);
}
//
// IsForDialog()
// Returns if the message is intended for a window in a dialog. AppSharing
// has the host UI dialog, incoming request dialogs, and possibly
// notification message box dialogs.
//
HWND IsForDialog(HWND hwnd)
{
BOOL rc = FALSE;
HWND hwndParent;
DebugEntry(IsForDialog);
if (!hwnd)
DC_QUIT;
while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
hwndParent = GetParent(hwnd);
if (hwndParent == GetDesktopWindow())
break;
hwnd = hwndParent;
}
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_DLGMODALFRAME)
{
// This is a dialog
}
else
{
hwnd = NULL;
}
DC_EXIT_POINT:
DebugExitPTR(IsForDialog, hwnd);
return(hwnd);
}
//
// ASMaster member functions
//
//
//
//
// ASMaster::OnEvent
//
// Parameters: event event type
// param1 other parameter
// param2 other parameter
//
//
BOOL CALLBACK eventProc
(
LPVOID cpiHandle_,
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
BOOL rc;
if (g_pMaster)
{
rc = g_pMaster->OnEvent(event, param1, param2);
}
else
{
WARNING_OUT(("Received ASMaster event %d but no g_pMaster", event));
rc = FALSE;
}
return rc;
}
BOOL ASMaster::OnEvent
(
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
BOOL rc = TRUE;
DebugEntry(ASMaster::OnEvent);
if (!m_pNotify)
{
// Nothing to do
rc = FALSE;
DC_QUIT;
}
switch (event)
{
case SH_EVT_APPSHARE_READY:
m_pNotify->OnReadyToShare(param1 != 0);
break;
case SH_EVT_SHARE_STARTED:
m_pNotify->OnShareStarted();
break;
case SH_EVT_SHARING_STARTED:
m_pNotify->OnSharingStarted();
break;
case SH_EVT_SHARE_ENDED:
m_pNotify->OnShareEnded();
break;
case SH_EVT_PERSON_JOINED:
m_pNotify->OnPersonJoined((IAS_GCC_ID)param1);
break;
case SH_EVT_PERSON_LEFT:
m_pNotify->OnPersonLeft((IAS_GCC_ID)param1);
break;
case SH_EVT_STARTINCONTROL:
m_pNotify->OnStartInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_STOPINCONTROL:
m_pNotify->OnStopInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_CONTROLLABLE:
m_pNotify->OnControllable(param1 != 0);
break;
case SH_EVT_STARTCONTROLLED:
m_pNotify->OnStartControlled((IAS_GCC_ID)param1);
break;
case SH_EVT_STOPCONTROLLED:
m_pNotify->OnStopControlled((IAS_GCC_ID)param1);
break;
default:
// Unrecognized, unhandled event
rc = FALSE;
break;
}
DC_EXIT_POINT:
DebugExitBOOL(ASMaster::OnEvent, rc);
return(rc);
}
//
// ASMaster::IsSharingAvailable()
//
STDMETHODIMP_(BOOL) ASMaster::IsSharingAvailable()
{
return(g_asCanHost);
}
//
// ASMaster::CanShareNow()
//
STDMETHODIMP_(BOOL) ASMaster::CanShareNow()
{
BOOL rc = FALSE;
UT_Lock(UTLOCK_AS);
//
// We can share if
// * We can capture graphic output on this OS
// * We're in a call
//
if (g_asCanHost &&
g_asSession.callID &&
(g_s20State >= S20_NO_SHARE))
{
rc = TRUE;
}
UT_Unlock(UTLOCK_AS);
return(rc);
}
//
// ASMaster::InInShare()
//
STDMETHODIMP_(BOOL) ASMaster::IsInShare()
{
return(g_asSession.pShare != NULL);
}
//
// ASMaster::IsSharing()
//
STDMETHODIMP_(BOOL) ASMaster::IsSharing()
{
IAS_PERSON_STATUS personStatus;
::ZeroMemory(&personStatus, sizeof(personStatus));
personStatus.cbSize = sizeof(personStatus);
GetPersonStatus(0, &personStatus);
return(personStatus.AreSharing != 0);
}
//
// CanAllowControl()
// We can allow control if we're sharing and it's not prevented by policy
//
STDMETHODIMP_(BOOL) ASMaster::CanAllowControl(void)
{
return(IsSharing());
}
//
// IsControllable()
// We are controllable if our state isn't detached.
//
STDMETHODIMP_(BOOL) ASMaster::IsControllable(void)
{
IAS_PERSON_STATUS personStatus;
::ZeroMemory(&personStatus, sizeof(personStatus));
personStatus.cbSize = sizeof(personStatus);
GetPersonStatus(0, &personStatus);
return(personStatus.Controllable != 0);
}
//
// GetPersonStatus()
//
STDMETHODIMP ASMaster::GetPersonStatus(IAS_GCC_ID Person, IAS_PERSON_STATUS * pStatus)
{
return(::SHP_GetPersonStatus(Person, pStatus));
}
//
//
// ASMaster::ShareDesktop
//
//
STDMETHODIMP ASMaster::ShareDesktop(void)
{
HRESULT hr;
DebugEntry(ASMaster::ShareDesktop);
hr = E_FAIL;
if (!CanShareNow())
{
WARNING_OUT(("Share failing; can't share now"));
DC_QUIT;
}
if (SHP_ShareDesktop())
{
hr = S_OK;
}
DC_EXIT_POINT:
DebugExitHRESULT(ASMaster::ShareDesktop, hr);
return hr;
}
//
//
// ASMaster::UnshareDesktop
//
// Parameters: HWND of the window to unshare
//
//
STDMETHODIMP ASMaster::UnshareDesktop(void)
{
return(::SHP_UnshareDesktop());
}
//
// TakeControl()
//
// From viewer to host, asking to take control of host.
//
STDMETHODIMP ASMaster::TakeControl(IAS_GCC_ID PersonOf)
{
return(SHP_TakeControl(PersonOf));
}
//
// CancelTakeControl()
//
// From viewer to host, to cancel pending TakeControl request.
//
STDMETHODIMP ASMaster::CancelTakeControl(IAS_GCC_ID PersonOf)
{
return(SHP_CancelTakeControl(PersonOf));
}
//
// ReleaseControl()
//
// From viewer to host, telling host that viewer is not in control of host
// anymore.
//
STDMETHODIMP ASMaster::ReleaseControl(IAS_GCC_ID PersonOf)
{
return(SHP_ReleaseControl(PersonOf));
}
//
// PassControl()
//
// From viewer to host, when viewer is in control of host, asking to pass
// control of host to a different viewer.
STDMETHODIMP ASMaster::PassControl(IAS_GCC_ID PersonOf, IAS_GCC_ID PersonTo)
{
return(SHP_PassControl(PersonOf, PersonTo));
}
//
// AllowControl()
//
// On host side, to allow/stop allowing control at all of shared apps/desktop.
// When one starts to host, allowing control always starts as off. So
// turning on allowing control, stopping sharing, then sharing something
// else will not leave host vulnerable.
//
// When turning it off, if a viewer was in control of the host, kill control
// from the host to the viewer will occur first.
//
// The "ESC" key is an accelerator to stop allowing control, when pressed
// by the user on the host who is currently controlled.
//
STDMETHODIMP ASMaster::AllowControl(BOOL fAllow)
{
return(::SHP_AllowControl(fAllow));
}
//
// GiveControl()
//
// From host to viewer, inviting the viewer to take control of the host.
// It's the inverse of TakeControl.
//
STDMETHODIMP ASMaster::GiveControl(IAS_GCC_ID PersonTo)
{
return(SHP_GiveControl(PersonTo));
}
//
// CancelGiveControl()
//
// From host to viewer, to cancel pending GiveControl request
//
STDMETHODIMP ASMaster::CancelGiveControl(IAS_GCC_ID PersonTo)
{
return(SHP_CancelGiveControl(PersonTo));
}
//
// RevokeControl()
//
// From host to viewer, when host wishes to stop viewer from controlling him.
// AllowControl is still on, for another to possibly take control of the host.
//
// Mouse clicks and key presses other than "ESC" by the user on the controlled
// host host areaccelerators to kill control.
//
STDMETHODIMP ASMaster::RevokeControl(IAS_GCC_ID PersonTo)
{
return(SHP_RevokeControl(PersonTo));
}