1045 lines
22 KiB
C++
1045 lines
22 KiB
C++
#include "precomp.h"
|
|
|
|
#include <RegEntry.h>
|
|
#include <oprahcom.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_putOM);
|
|
ASSERT(!g_putAL);
|
|
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 fOMCleanup = FALSE;
|
|
BOOL fALCleanup = 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
|
|
//
|
|
|
|
g_asPolicies = 0;
|
|
|
|
if (g_asOptions & AS_SERVICE)
|
|
{
|
|
//
|
|
// No old whiteboard, no how, for RDS
|
|
//
|
|
g_asPolicies |= SHP_POLICY_NOOLDWHITEBOARD;
|
|
}
|
|
else
|
|
{
|
|
RegEntry rePol(POLICIES_KEY, HKEY_CURRENT_USER);
|
|
|
|
//
|
|
// Is old whiteboard disabled?
|
|
//
|
|
if (rePol.GetNumber(REGVAL_POL_NO_OLDWHITEBOARD, DEFAULT_POL_NO_OLDWHITEBOARD))
|
|
{
|
|
WARNING_OUT(("Policy disables Old Whiteboard"));
|
|
g_asPolicies |= SHP_POLICY_NOOLDWHITEBOARD;
|
|
}
|
|
|
|
//
|
|
// Is application sharing disabled completely?
|
|
//
|
|
if (rePol.GetNumber(REGVAL_POL_NO_APP_SHARING, DEFAULT_POL_NO_APP_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy disables App Sharing"));
|
|
g_asPolicies |= SHP_POLICY_NOAPPSHARING;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Only grab AS policies if AS is even allowed
|
|
//
|
|
if (rePol.GetNumber(REGVAL_POL_NO_SHARING, DEFAULT_POL_NO_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from sharing"));
|
|
g_asPolicies |= SHP_POLICY_NOSHARING;
|
|
}
|
|
|
|
if (rePol.GetNumber(REGVAL_POL_NO_MSDOS_SHARING, DEFAULT_POL_NO_MSDOS_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from sharing command prompt"));
|
|
g_asPolicies |= SHP_POLICY_NODOSBOXSHARE;
|
|
}
|
|
|
|
if (rePol.GetNumber(REGVAL_POL_NO_EXPLORER_SHARING, DEFAULT_POL_NO_EXPLORER_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from sharing explorer"));
|
|
g_asPolicies |= SHP_POLICY_NOEXPLORERSHARE;
|
|
}
|
|
|
|
if (rePol.GetNumber(REGVAL_POL_NO_DESKTOP_SHARING, DEFAULT_POL_NO_DESKTOP_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from sharing desktop"));
|
|
g_asPolicies |= SHP_POLICY_NODESKTOPSHARE;
|
|
}
|
|
|
|
if (rePol.GetNumber(REGVAL_POL_NO_TRUECOLOR_SHARING, DEFAULT_POL_NO_TRUECOLOR_SHARING))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from sharing in true color"));
|
|
g_asPolicies |= SHP_POLICY_NOTRUECOLOR;
|
|
}
|
|
|
|
if (rePol.GetNumber(REGVAL_POL_NO_ALLOW_CONTROL, DEFAULT_POL_NO_ALLOW_CONTROL))
|
|
{
|
|
WARNING_OUT(("Policy prevents user from letting others control"));
|
|
g_asPolicies |= SHP_POLICY_NOCONTROL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Register the call primary code, for T.120 GCC
|
|
if (!CMP_Init(&fCMGCleanup))
|
|
{
|
|
ERROR_OUT(("CMP_Init failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (!(g_asPolicies & SHP_POLICY_NOOLDWHITEBOARD))
|
|
{
|
|
if (!OMP_Init(&fOMCleanup))
|
|
{
|
|
ERROR_OUT(("Couldn't start ObMan"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (!ALP_Init(&fALCleanup))
|
|
{
|
|
ERROR_OUT(("Couldn't start AppLoader"));
|
|
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...
|
|
//
|
|
if (!(g_asPolicies & SHP_POLICY_NOAPPSHARING))
|
|
{
|
|
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 (fALCleanup)
|
|
ALP_Term();
|
|
|
|
if (fOMCleanup)
|
|
OMP_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_PAUSEDINCONTROL:
|
|
m_pNotify->OnPausedInControl((IAS_GCC_ID)param1);
|
|
break;
|
|
|
|
case SH_EVT_UNPAUSEDINCONTROL:
|
|
m_pNotify->OnUnpausedInControl((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;
|
|
|
|
case SH_EVT_PAUSEDCONTROLLED:
|
|
m_pNotify->OnPausedControlled((IAS_GCC_ID)param1);
|
|
break;
|
|
|
|
case SH_EVT_UNPAUSEDCONTROLLED:
|
|
m_pNotify->OnUnpausedControlled((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_asSession.hwndHostUI != NULL);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// 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_asSession.hwndHostUI &&
|
|
g_asSession.callID &&
|
|
(g_asSession.attendeePermissions & NM_PERMIT_SHARE) &&
|
|
(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)
|
|
{
|
|
if (g_asPolicies & SHP_POLICY_NOCONTROL)
|
|
return(FALSE);
|
|
|
|
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::IsWindowShareable()
|
|
//
|
|
STDMETHODIMP_(BOOL) ASMaster::IsWindowShareable(HWND hwnd)
|
|
{
|
|
return(CanShareNow() && HET_IsWindowShareable(hwnd));
|
|
}
|
|
|
|
|
|
//
|
|
// ASMaster::IsWindowShared()
|
|
//
|
|
STDMETHODIMP_(BOOL) ASMaster::IsWindowShared(HWND hwnd)
|
|
{
|
|
return(HET_IsWindowShared(hwnd));
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// ASMaster::Share
|
|
//
|
|
// Parameters: HWND of the window to share. This can be any (valid) HWND.
|
|
//
|
|
//
|
|
STDMETHODIMP ASMaster::Share(HWND hwnd, IAS_SHARE_TYPE uAppType)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DebugEntry(ASMaster::Share);
|
|
|
|
hr = E_FAIL;
|
|
|
|
if (!CanShareNow())
|
|
{
|
|
WARNING_OUT(("Share failing; can't share now"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If this is the desktop, check for a policy against just it.
|
|
//
|
|
if (hwnd == ::GetDesktopWindow())
|
|
{
|
|
if (g_asPolicies & SHP_POLICY_NODESKTOPSHARE)
|
|
{
|
|
WARNING_OUT(("Sharing desktop failing; prevented by policy"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
switch (uAppType)
|
|
{
|
|
case IAS_SHARE_DEFAULT:
|
|
case IAS_SHARE_BYPROCESS:
|
|
case IAS_SHARE_BYTHREAD:
|
|
case IAS_SHARE_BYWINDOW:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ERROR_OUT(("IAppSharing::Share - invalid share type %d", uAppType));
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if (SHP_Share(hwnd, uAppType))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitHRESULT(ASMaster::Share, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// ASMaster::Unshare
|
|
//
|
|
// Parameters: HWND of the window to unshare
|
|
//
|
|
//
|
|
STDMETHODIMP ASMaster::Unshare(HWND hwnd)
|
|
{
|
|
return(::SHP_Unshare(hwnd));
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// ASMaster::LaunchHostUI()
|
|
//
|
|
//
|
|
STDMETHODIMP ASMaster::LaunchHostUI(void)
|
|
{
|
|
return(SHP_LaunchHostUI());
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// ASMaster::GetShareableApps
|
|
//
|
|
// Generates a list of HWND's into <validAppList>
|
|
// These objects are allocated dynamically, so must be deleted by the
|
|
// caller.
|
|
//
|
|
//
|
|
STDMETHODIMP ASMaster::GetShareableApps(IAS_HWND_ARRAY **ppHwnds)
|
|
{
|
|
if (!CanShareNow())
|
|
return(E_FAIL);
|
|
|
|
return(HET_GetAppsList(ppHwnds) ? S_OK : E_FAIL);
|
|
}
|
|
|
|
|
|
STDMETHODIMP ASMaster::FreeShareableApps(IAS_HWND_ARRAY * pMemory)
|
|
{
|
|
HET_FreeAppsList(pMemory);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// PauseControl()
|
|
//
|
|
// On host, to temporarily allow local user to do stuff without breaking
|
|
// control bond. We put the viewer on hold.
|
|
//
|
|
STDMETHODIMP ASMaster::PauseControl(IAS_GCC_ID PersonInControl)
|
|
{
|
|
return(SHP_PauseControl(PersonInControl, TRUE));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// UnpauseControl()
|
|
//
|
|
// On host, to unpause control that has been paused. We take the viewer
|
|
// off hold.
|
|
//
|
|
STDMETHODIMP ASMaster::UnpauseControl(IAS_GCC_ID PersonInControl)
|
|
{
|
|
return(SHP_PauseControl(PersonInControl, FALSE));
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// StartStopOldWB
|
|
//
|
|
extern "C"
|
|
{
|
|
BOOL WINAPI StartStopOldWB(LPCTSTR szFile)
|
|
{
|
|
LPTSTR szCopyOfFile;
|
|
|
|
ValidateUTClient(g_putUI);
|
|
|
|
if (g_asPolicies & SHP_POLICY_NOOLDWHITEBOARD)
|
|
{
|
|
WARNING_OUT(("Not launching old whiteboard; prevented by policy"));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Because we're posting a message effectively, we have to make a
|
|
// copy of the string. If we ever have "SendEvent", we won't have
|
|
// that problem anymore.
|
|
//
|
|
if (szFile)
|
|
{
|
|
int cchLength;
|
|
BOOL fSkippedQuote;
|
|
|
|
// Skip past first quote
|
|
if (fSkippedQuote = (*szFile == '"'))
|
|
szFile++;
|
|
|
|
cchLength = lstrlen(szFile);
|
|
szCopyOfFile = (LPTSTR)::GlobalAlloc(GPTR, (cchLength+1)*sizeof(TCHAR));
|
|
if (!szCopyOfFile)
|
|
{
|
|
ERROR_OUT(("Can't make file name copy for whiteboard launch"));
|
|
return(FALSE);
|
|
}
|
|
|
|
lstrcpy(szCopyOfFile, szFile);
|
|
|
|
//
|
|
// NOTE:
|
|
// There may be DBCS implications with this. Hence we check to see
|
|
// if we skipped the first quote; we assume that if the file name
|
|
// starts with a quote it must end with one also. But we need to check
|
|
// it out.
|
|
//
|
|
// Strip last quote
|
|
if (fSkippedQuote && (cchLength > 0) && (szCopyOfFile[cchLength - 1] == '"'))
|
|
{
|
|
TRACE_OUT(("Skipping last quote in file name %s", szCopyOfFile));
|
|
szCopyOfFile[cchLength - 1] = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
szCopyOfFile = NULL;
|
|
}
|
|
|
|
UT_PostEvent(g_putUI, g_putAL, NO_DELAY,
|
|
AL_INT_STARTSTOP_WB, 0, (UINT_PTR)szCopyOfFile);
|
|
return(TRUE);
|
|
}
|
|
}
|