1300 223 482
CSA
Cheaper Citrix Licensing



Licensing Guarantee

XAMobileLogoff - Citrix XenApp administration in the palm of your hand.

XAMobileLogoff is a mobile device enabled utility to logoff Citrix or Terminal Server Sessions from all servers within a Citrix XenApp 6.5 Farm. This application has been designed to run as a published application delivered via the Citrix Receiver for Android mobile devices.

You must install the Citrix Mobility Pack as detailed at http://support.citrix.com/proddocs/topicxenapp65-mobility-packps-mobility-pack-install.html

This utility is FREEWARE and was written by Warren Simondson of Ctrl-Alt-Del IT Consultancy, Australia.

www.ctrl-alt-del.com.au

This program and the code may not be reproduced for profit.

SDK's used in this program:

Citrix Server WFAPI SDK - http://community.citrix.com/display/xa/Download+SDKS
Citrix MOBILITY SDK - http://community.citrix.com/display/xa/Download+SDKS
Microsoft Windows 2008 SDK - http://msdn.microsoft.com/downloads

IN NO EVENT WILL CTRL-ALT-DEL IT CONSULTANCY BE LIABLE TO YOU FOR ANY GENERAL, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR OTHER DAMAGES ARISING OUT OF THIS PRODUCT.

Comments

Your comments or questions are more than welcome for this topic. Comments can be posted on the Citrix Community Mobility SDK forum or you can use our Ctrl-Alt-Del IT Consultancy web form to ask questions directly.
We hope that this page is of great assistance to you.

The Application

System Requirements

Download
Download the Executable for XAMobileLogoff here (VS10 C++ compiled):
XAMobileLogoff.zip

Instructions for Use

  1. Download XAMobileLogoff.zip from Ctrl-Alt-Del IT Consultancy
  2. Unzip the file into the desired directory on your Citrix XenApp 6.5 Server. The program is a single executable and is not an install package so it can be run immediately after copying to your system
  3. Ensure Microsoft Visual C++ 2010 Redistributable Package is installed.
  4. Ensure Citrix Mobility Pack is installed.
  5. Publish XAMobileLogoff.exe via the Citrix Access Console, selecting the approriate users to delivery the application to. NOTE: Users who wish to use XAMobileLogoff.exe must have query and logon/logoff rights as defined in the ICA protocol. This can be defined in the RDS management tool.
  6. Once the application is published, connect your mobile device to the Citrix Receiver configure to point to your Web Interface site or Service site. Launch the application as show below.

XAMobileLogoff Example

Designed to make the administration of logging off users simple, this little program will provide a quick, mobile method for performing such a task. Using the Citrix Receiver on your mobile device, the mobility of this utility allows for on the spot user control with the ability to SMS notifications to the end user on completion. Launch the application from the Citrix Receiver for your mobile device and the rest is easy.

XAMOBILELOGOFF has been designed to run on virtually all Mobile devices, with some features not available on some devices.

Just enter the username as prompted by the program and choose the function you want.

Functions include:

  • Logoff user and Phone
  • Logoff user and SMS
XAMobileLogoff Example
XAMobileLogoff Example

Enter a username using the mobile device keyboard which appears immedaitely you select the entry box.

When you select Logoff user and SMS, you will be presented with the SMS screen to enter the number and message you wish to send the user when they are successfully logged off.

XAMobileLogoff Example
XAMobileLogoff Example

You will be notified if the user was successfully logged off or not. Easy as that!

The Code

System Requirements

Download
Download the source code for XAMobileLogoff here (VS10 C++ compiled):
XAMobileLogoff_src.zip

Tips of Programming with the Mobility SDK
The following are some tips that I found may help everyone as they write their first app using the Citrix Mobility SDK. These tips are based on coding your application in Win32 or MFC using Micrsoft Visual C++.

  • Don't use CButton controls - Use only CStatic controls with SS_Notify enabled on the control. Cbutton colours, properties and operations are very hard to dynamically modify which is a big requirement for making a mobile device aware application. To resize buttons is relatively easy, but to make it look and feel like a mobile application is a little tricky with Cbutton controls. A CStatic Text control is more flexible in terms of visual flexibility and it can still perform like a normal button control with SS_Notify enabled.
  • Treat the dialog box like a single column page - don't overload the central dialog box or main page with controls. Place controls one under the other to have a linear structre that can be adapted dynamically to different mobile device screen sizes.
  • Make single controls do different things based on what the screen is displaying at the time of selection. One button can be used to perform multiple events based on the condition, so instead of programming multiple buttons, create one button that responses differently according to the curren operation. See the routine OnStnClickedInfoBox() in the XAMobileLogoff source code. Button events can be executed based on the text property of a control, the visibility of the control or event the location of a control.

Key Areas
The key area to this application and future ones that I write using the Citrix Mobility SDK, will be the procedure SETOBJECTPOSITION as used in XAMobileLogoff. This subroutine is the primary code that allows the application window to be dynamically resized/ redrawn based on the screen size of the mobile device. In the case of XAMobileLogoff, I have five (5) controls on within the window. Four (4) are CStatic controls (three (3) set as push buttons and one (1) as a label), and one (1) is a CEdit control for text input.
This subroutine, when called on initialisation of the dialog box, will firstly get the dimensions of the current mobile device screen as presented through the Citrix Receiver, then, based on the five (5) controls placed directly under each other, will calculate the placement of the five controls, as well as the dimensions of the five controls, to evenly size and space the controls on the available screen. This routine not only works on the mobile device, but a starndard windows session so you could essentially wrote a global application which sizes synamically to any device not just mobile, but desktop as well. This would reduce the need to purposely design an app solely for mobile use.
Additionally SETOBJECTPOSITION will set the font size and type of control, again based on the size and position of the client device.

The code listed below outlines the sub routine.

void CXAMobileLogoffDlg::SETOBJECTPOSITION()
{
WINDOWPLACEMENT wpc;

int ScreenSizeX; //screen size width
int ScreenSizeY; //screen size height
long x1; //control positions
long y1;
long x2;
long y2;

CFont pFont1; // large font size
CFont pFont2; // medium font size
CFont pFont3; // small font size

// if we don't need to see the cursor/ pointer
// ShowCursor(FALSE);
// LOCK MOBILE DEVICE ORIENTATION BEFORE ENTRY
Mobile_Device_Calls(2);

//make dialog box fit to whole screen

//screen size vars
//get screen size of device
// width
ScreenSizeX = GetSystemMetrics(SM_CXSCREEN);
//example static: ScreenSizeX = 800;
// height
ScreenSizeY = GetSystemMetrics(SM_CYSCREEN);
//example static: ScreenSizeY = 600;

//remove the title bar and the borders - just to be clean
LONG ptrStyles = GetWindowLong(this->m_hWnd, GWL_STYLE);
ptrStyles &= ~WS_TILEDWINDOW;
SetWindowLong(this->m_hWnd, GWL_STYLE, ptrStyles);

// Occupy the whole screen
SetWindowPos(&wndTopMost, 0, 0, ScreenSizeX, ScreenSizeY, SWP_SHOWWINDOW);

//Base X location set based on screen size - for all controls
x1= 10;
x2 = ScreenSizeX - 10;

/*****************************************
* Quick calculation explanation:
*
* X is a set value and is the width of the screen with a border of 10 pixels either side.
* Therefore a control will be the width of (X-20) and will be centred on the screen on the X co-ordinate
*
* Y is a varable value based on which control it is addressing. In this example there are 5 controls
* directly under each other all of the same shape. The height of each control has a maximum of 90% the height
* of the screen divided by 5 (as there are 5 controls). So if the screen is 100 pixels high, then each of the
* 5 controls on the screen can be a maximum of 18 pixels = ((100 / 5) * 0.90)
*
******************************************/

//Start from bottom control and work up
//Quit button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 1); //1st Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but1 = (CStatic *)GetDlgItem(CTXT_QUIT);
//turn on notify for static text boxes to be clickable
but1->ModifyStyle( 0, SS_NOTIFY);
but1->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but1->SetWindowPlacement(&wpc);

//info box text control location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 2); //2nd Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *Lbl1 = (CStatic *)GetDlgItem(LBL_INFO_BOX);
Lbl1->ModifyStyle( 0, SS_NOTIFY ); //turn on notify for static tect boxes to be clickable
Lbl1->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
Lbl1->SetWindowPlacement(&wpc);

//SMS button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 3); //3rd Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but2 = (CStatic *)GetDlgItem(CTXT_RSTSMS);
but2->ModifyStyle( 0, SS_NOTIFY);
but2->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but2->SetWindowPlacement(&wpc);

//Phone button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 4); //4th Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but3 = (CStatic *)GetDlgItem(CTXT_RSTPH);
but3->ModifyStyle( 0, SS_NOTIFY);
but3->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but3->SetWindowPlacement(&wpc);

//input username location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 5); //5th Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CEdit *TXTB = (CEdit *)GetDlgItem(TXT_USERNAME);
TXTB->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
TXTB->SetWindowPlacement(&wpc);

// set font size for control box
// Initializes a CFont object with the specified characteristics.
//pFont1 large font adjusted for y axis
VERIFY(pFont1.CreateFont(
((ScreenSizeY / 5) * 0.5), // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//pFont2 medium font adjusted for y axis
VERIFY(pFont2.CreateFont(
((ScreenSizeY / 5) * 0.4), // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//pFont3 small font/ small ht and wdth/ underlined - adjusted for y axis
VERIFY(pFont3.CreateFont(
((ScreenSizeY / 5) * 0.3), // nHeight
(ScreenSizeX / 50), // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
TRUE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//now set fonts on each control
GetDlgItem(TXT_USERNAME)->SetFont(&pFont1);
GetDlgItem(CTXT_RSTPH)->SetFont(&pFont2);
GetDlgItem(CTXT_RSTSMS)->SetFont(&pFont2);
GetDlgItem(LBL_INFO_BOX)->SetFont(&pFont3);
GetDlgItem(CTXT_QUIT)->SetFont(&pFont2);

pFont1.Detach();
pFont2.Detach();
pFont3.Detach();
}

Another key area to this application and future ones that I write using the Citrix Mobility SDK, will be the procedure OnCtlColor as used in XAMobileLogoff. This subroutine is an event handler that us used to overide the colour of the control and its text. So basically it makes a CStatic or CEdit contol turn what ever colour you want. this routine does not work on the CButton control hence my tip for not using CButton in creating a mobile app. If you want to make the app look and feel like a mobile app and not a windows app, then this is the way to do it.

The code listed below outlines the sub routine.

HBRUSH CXAMobileLogoffDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
switch(nCtlColor)
{
case CTLCOLOR_EDIT:
m_hPaintBrush = ::CreateSolidBrush ( RGB(255,255,255 )); // white control background colour
pDC->SetBkColor(RGB(255,255,255)); // white background colour
pDC->SetTextColor(RGB(0, 0, 0)); // black text colour
return m_hPaintBrush;
break;

case CTLCOLOR_STATIC:
//all static text controls will be black with white text
m_hPaintBrush = ::CreateSolidBrush ( RGB(0,0,0 )); // black control background colour
pDC->SetBkColor(RGB(0,0,0)); // black background colour
pDC->SetTextColor(RGB(255, 0, 0)); // red text colour

//if we wanted to target a specific text control and make it a different colour, we can do this
if(pWnd->GetDlgCtrlID() == LBL_INFO_BOX) {
m_hPaintBrush = ::CreateSolidBrush ( RGB(0,0,255 )); // blue control background colour
pDC->SetBkColor(RGB(0,0,255)); // blue text background colour
pDC->SetTextColor(RGB(255, 255, 255)); // white text colour
} //if

if((pWnd->GetDlgCtrlID() == CTXT_RSTSMS) || (pWnd->GetDlgCtrlID() == CTXT_RSTPH)) {
pDC->SetTextColor(RGB(255, 255, 0)); // yellow text colour
} //if
return m_hPaintBrush;
break;

case CTLCOLOR_MSGBOX:
pDC->SetBkColor(RGB(255,0,0)); // red background colour
pDC->SetTextColor(RGB(255,255,255)); // white text colour
pDC->SetBkMode(TRANSPARENT);
return m_hPaintBrush;
break;
case CTLCOLOR_DLG:
pDC->SetBkColor(RGB(255,0,0)); // black bk color
return m_hPaintBrush;
break;

default:
return CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
} //switch

}

Finally the code for XAMobileLogoffDlg.ccp outlines the core of the routines, including the WTSAPI command to find and logoff the user entered into the application.

The code listed below outlines XAMobileLogoffDlg.ccp in its entirity.

// XAMobileLogoffDlg.cpp : implementation file
/********************************************************************
* XAMobileLogoff is a mobile device enabled utility to logoff Citrix or Terminal Server
* Sessions from all servers within a Citrix XenApp 6.5 Farm.
*
* You must install the Citrix Mobility Pack as detailed at
* http://support.citrix.com/proddocs/topic/xenapp65-mobility-pack/ps-mobility-pack-install.html
*
* This utility is FREEWARE and was written by Warren Simondson of
* Ctrl-Alt-Del IT Consultancy, Australia.
*
* www.ctrl-alt-del.com.au
*
* This program and the code may not be reproduced for profit.
*
* SDK's used in this program:
*
* Citrix Server WFAPI SDK - http://community.citrix.com/display/xa/Download+SDKS
* Citrix MOBILITY SDK - http://community.citrix.com/display/xa/Download+SDKS
* Microsoft Windows 2008 SDK - http://msdn.microsoft.com/downloads
*
* IN NO EVENT WILL CTRL-ALT-DEL IT CONSULTANCY BE LIABLE TO YOU FOR ANY GENERAL,
* SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR OTHER DAMAGES ARISING OUT OF
* THIS PRODUCT.
************************************************************************/

#include "stdafx.h"
#include "XAMobileLogoff.h"
#include "XAMobileLogoffDlg.h"
#include
#include
#include

#include
#include

#include // for NetGet[Any]DCName()
#include // for NERR_Success
#include // for WKSTA_INFO_100
#include

//globals
#define PHONE_NUMBER "" //start with blank phone number - could be made to enter phone number in a dialog box and send to phone
#define MAX_VALUE_NAME 256
#define BUFSIZE 65536

BOOL FOUNDUSER; //Global BOOL for Determining if a username entered in the app was found in the farm

//global type definition for mobile device screen orientation
typedef struct
{
CMP_ORIENTATION_POSITION orientation;
LPCSTR name;
} ORIENTATION_NAME, *PORIENTATION_NAME;

#define OrientationToName(x) { x, #x}
#define OrientationEndList() { CMP_ORIENTATION_UNKNOWN, NULL}

ORIENTATION_NAME orientationNames[] =
{
OrientationToName(CMP_ORIENTATION_UNKNOWN),
OrientationToName(CMP_ORIENTATION_PORTRAIT),
OrientationToName(CMP_ORIENTATION_PORTRAIT_UPSIDE_DOWN),
OrientationToName(CMP_ORIENTATION_LANDSCAPE_LEFT),
OrientationToName(CMP_ORIENTATION_LANDSCAPE_RIGHT),
OrientationEndList()
};

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

/********************************************************************
*
* CXAMobileLogoffDlg dialog - definition of the dialog variables and controls
*
********************************************************************/

CXAMobileLogoffDlg::CXAMobileLogoffDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CXAMobileLogoffDlg::IDD, pParent)
, m_LBL_INFO_BOX(_T(""))
, m_TXT_USERNAME(_T(""))
, m_CTXT_QUIT(_T(""))
, m_CTXT_RSTSMS(_T(""))
, m_CTXT_RSTPH(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CXAMobileLogoffDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, LBL_INFO_BOX, m_LBL_INFO_BOX);
DDX_Text(pDX, TXT_USERNAME, m_TXT_USERNAME);
DDX_Text(pDX, CTXT_QUIT, m_CTXT_QUIT);
DDX_Text(pDX, CTXT_RSTSMS, m_CTXT_RSTSMS);
DDX_Text(pDX, CTXT_RSTPH, m_CTXT_RSTPH);
}

BEGIN_MESSAGE_MAP(CXAMobileLogoffDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_ERASEBKGND() //for background colour setting
ON_WM_CTLCOLOR() //for control colour setting
ON_EN_KILLFOCUS(TXT_USERNAME, &CXAMobileLogoffDlg::OnEnKillfocusUsername)
ON_EN_SETFOCUS(TXT_USERNAME, &CXAMobileLogoffDlg::OnEnSetfocusUsername)
ON_STN_CLICKED(LBL_INFO_BOX, &CXAMobileLogoffDlg::OnStnClickedInfoBox)
ON_STN_CLICKED(CTXT_QUIT, &CXAMobileLogoffDlg::OnStnClickedQuit)
ON_STN_CLICKED(CTXT_RSTSMS, &CXAMobileLogoffDlg::OnStnClickedRstsms)
ON_STN_CLICKED(CTXT_RSTPH, &CXAMobileLogoffDlg::OnStnClickedRstph)
END_MESSAGE_MAP()


/********************************************************************
*
* Global Procedures
*
********************************************************************/


/********************************************************************
*
* random
*
* Used with randomDWORD function -
* generate four byte random number for unique ID of sms and phone
* ENTRY:
* unsigned n - n must be < RAND_MAX
*
* EXIT:
* returns a random number in range [0, n]
*
* With thanks to:
* Superkoko and codeguru.com
*
********************************************************************/

unsigned random(unsigned n) {
const int min_reject=RAND_MAX-RAND_MAX%n;
int r;
unsigned i=0;
while ((r=rand()) >= min_reject && i<128) ++i;
return r%n;
}

/********************************************************************
*
* randomDWORD
*
* Used with random function -
* generate four byte random number for unique ID of sms and phone
* ENTRY:
* unsigned n - n must be < RAND_MAX
*
* EXIT:
* returns a random number in range [0, n]
* With thanks to:
* Superkoko and codeguru.com
*
********************************************************************/
DWORD randomDWORD() {
return DWORD(random(256)) | DWORD(random(256)<<8)
|DWORD(random(256)<<16) | DWORD(random(256)<<24);
}

/********************************************************************
*
* IsNum
*
* Check if string is a number
* ENTRY:
* string
*
* EXIT:
* integer - 0 = not number, 1 = is a number
********************************************************************/
int
IsNum( LPTSTR lpNumber)
{
int i, charLen, score;

score = 0;
charLen = _tcslen(lpNumber);

for ( i=0; i < charLen; i++ ) {
if ((lpNumber[i] >= '0') && (lpNumber[i] <= '9')) {
score += 1;
}
} //for
if (score == charLen) {
return 1;
} else {
return 0;
}

}

/********************************************************************
*
* Mobile_Device_Calls
*
* Perform calls to mobile device evoking local device operations
* NOTE: Most CMP functions have been included in this routine but
* only selected ones are used for XAMobileLogoff. This is to
* provide a global routine for future projects.
*
* ENTRY:
* Mobile_Action (input)
* type of action to perfrom
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::Mobile_Device_Calls(int Mobile_Action)
{
//VAR DEFS
int pixwidth;
int pixheight;

LPTSTR SMS_TEXT; //string for SMS message
DWORD dwMSMS_TEXT;

CMPRESULT rc;
HANDLE hCMP = NULL;

CMP_DISPLAY_SETTINGS displaySettings; //DISPLAY
CMP_ORIENTATION_DATA orientationData; //ORIENTATION
CMP_KEYBOARD_STATE kybdState; //KEYBOARD

// initialize for STA (Single Thread Apartment) in COM
rc = CMPInitialize(FALSE);

// Open a handle to the mobile device
rc = CMPOpen(&hCMP);

if(CMP_SUCCESS(rc))
{
// open the link between the two sides
rc = CMPOpenSession(hCMP);

if(CMP_SUCCESS(rc))
{

switch ( Mobile_Action ) {
case 1 : //display settings
// this function for for example purposes and not used in XMobileLogoff
// It could be used to get the screen size of the mobile device
// for creating you application window
memset(&displaySettings, 0, sizeof(displaySettings));

// best practice to initialize the length of the structure
displaySettings.Length = sizeof(CMP_DISPLAY_SETTINGS);

rc = CMPGetDisplaySettings(hCMP, &displaySettings);

if(CMP_SUCCESS(rc))
{
pixwidth = displaySettings.PixelWidth;
pixheight = displaySettings.PixelHeight;
//other information obtained could be:
//displaySettings.Length - Display Settings structure length
//displaySettings.ColorDepth - Color depth
//displaySettings.DeviceOrientation - Orientation
//displaySettings.WidthMilliInches, displaySettings.HeightMilliInches - Physical width, height MilliInches
//displaySettings.HorizontalPixelsPerInch, displaySettings.VerticalPixelsPerInch -PPI X,Y
}

break; //display settings

case 2 : //orientation LOCK
//in this instance we want to lock the orientation to portrait whilst using the app
memset(&orientationData, 0, sizeof(orientationData));
// Now lock the orientation to PORTRAIT
rc = CMPSetOrientation(hCMP, CMP_ORIENTATION_PORTRAIT, CMP_ORIENTATION_FLAG_LOCK);

break; //orientation LOCK

case 3 : //orientation UNLOCK
//in this instance we want to unlock the orientation after pressing quit on the app
memset(&orientationData, 0, sizeof(orientationData));
// unlock the orientation
rc = CMPSetOrientation(hCMP, CMP_ORIENTATION_LANDSCAPE_LEFT, CMP_ORIENTATION_FLAG_FOLLOW_SENSOR);

break; //orientation UNLOCK

case 4 : //sms
//in this instance we want to launch an sms from the device
//set the text string

dwMSMS_TEXT = _tcslen(_T("Your Session has been logged off")) + 1;
SMS_TEXT = (LPTSTR) LocalAlloc(0, (dwMSMS_TEXT + 1) * sizeof(TCHAR));
_tcscpy(SMS_TEXT, _T("Your Session has been logged off"));

// Start the SMS process by popping up the SMS screen with the phone number already populated
rc = CMPSendSMS(hCMP, PHONE_NUMBER, randomDWORD(), SMS_TEXT);
LocalFree(SMS_TEXT);
break; //sms

case 5: //phone call
// Start the phone call process by popping up the dialer with the number already populated
rc = CMPStartCall(hCMP, PHONE_NUMBER, randomDWORD());


break; //phone call

case 6: //show keyboard
memset(&kybdState, 0, sizeof(kybdState));

// Show keyboard
// Flags change how ShowKeyboard works. The flags are defined in CMP_KEYBOARD_FLAGS
//CMP_KYBD_FLAG_PHYSICAL =0x0001, // readonly
//CMP_KYBD_FLAG_VISIBLE =0x0002, // readonly
//CMP_KYBD_FLAG_EXT_FLAG =0x0004 = 4 dec
//CMP_KYBD_FLAG_USE_RECT =0x0008 = 8 dec
//CMP_KYBD_FLAG_AUTO_CORRECT =0x0010 = 16 dec
//CMP_KYBD_FLAG_AUTO_CAPITAL =0x0020 = 32 dec
//CMP_KYBD_FLAG_RETURN_KEY_TYPE =0x0040 = 64 dec
kybdState.KybdFlags = 0;
kybdState.KybdType = CMP_KYBD_TYPE_STANDARD;
kybdState.KybdFlags = CMP_KYBD_FLAG_AUTO_CORRECT | CMP_KYBD_FLAG_AUTO_CAPITAL | CMP_KYBD_FLAG_RETURN_KEY_TYPE;
kybdState.KybdAutoCaps;
kybdState.KybdReturnKey = CMP_KYBD_RETURN_KEY_DONE;

// Show the keyboard with the specific settings
rc = CMPShowKeyboard(hCMP, &kybdState);

break; //show keyboard

case 7: //hide keyboard
// Hide the keyboard with the specific settings
rc = CMPHideKeyboard(hCMP);

break; //hide keyboard

case 8: //notify user
// send a vibration notification with text to the mobile device
rc = CMPNotifyUser(hCMP, randomDWORD(), CMP_NOTIFICATION_FLAG_VIBRATE | CMP_NOTIFICATION_FLAG_TEXT, _T("You have been notified."));

break; //notify user

default:
//do nothing
;
} //switch

// close our connection
CMPCloseSession(hCMP);

} //if CMP_SUCCESS

// release our handle
CMPClose(hCMP);
} //if CMP_SUCCESS

// uninitialize COM
CMPUninitialize();
}

/********************************************************************
*
* SETOBJECTPOSITION
*
* Display the dialog box in the available window. Determine size of device window and extends the dialog box
* to that size. It then adjusts each control within the dialog bog to suit the given size of the screen.
* Works on mobile devices and standard monitors
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/

void CXAMobileLogoffDlg::SETOBJECTPOSITION()
{
WINDOWPLACEMENT wpc;

int ScreenSizeX; //screen size width
int ScreenSizeY; //screen size height
long x1; //control positions
long y1;
long x2;
long y2;

CFont pFont1; // large font size
CFont pFont2; // medium font size
CFont pFont3; // small font size

// if we don't need to see the cursor/ pointer
// ShowCursor(FALSE);

// LOCK MOBILE DEVICE ORIENTATION BEFORE ENTRY
Mobile_Device_Calls(2);

//make dialog box fit to whole screen

//screen size vars
//get screen size of device
// width
ScreenSizeX = GetSystemMetrics(SM_CXSCREEN);
//example static: ScreenSizeX = 800;
// height
ScreenSizeY = GetSystemMetrics(SM_CYSCREEN);
//example static: ScreenSizeY = 600;

//remove the title bar and the borders - just to be clean
LONG ptrStyles = GetWindowLong(this->m_hWnd, GWL_STYLE);
ptrStyles &= ~WS_TILEDWINDOW;
SetWindowLong(this->m_hWnd, GWL_STYLE, ptrStyles);

// Occupy the whole screen
SetWindowPos(&wndTopMost, 0, 0, ScreenSizeX, ScreenSizeY, SWP_SHOWWINDOW);

//Base X location set based on screen size - for all controls
x1= 10;
x2 = ScreenSizeX - 10;

/*****************************************
* Quick calculation explanation:
*
* X is a set value and is the width of the screen with a border of 10 pixels either side.
* Therefore a control will be the width of (X-20) and will be centred on the screen on the X co-ordinate
*
* Y is a varable value based on which control it is addressing. In this example there are 5 controls
* directly under each other all of the same shape. The height of each control has a maximum of 90% the height
* of the screen divided by 5 (as there are 5 controls). So if the screen is 100 pixels high, then each of the
* 5 controls on the screen can be a maximum of 18 pixels = ((100 / 5) * 0.90)
*
******************************************/

//Start from bottom control and work up
//Quit button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 1); //1st Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but1 = (CStatic *)GetDlgItem(CTXT_QUIT);
//turn on notify for static text boxes to be clickable
but1->ModifyStyle( 0, SS_NOTIFY);
but1->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but1->SetWindowPlacement(&wpc);

//info box text control location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 2); //2nd Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *Lbl1 = (CStatic *)GetDlgItem(LBL_INFO_BOX);
Lbl1->ModifyStyle( 0, SS_NOTIFY ); //turn on notify for static tect boxes to be clickable
Lbl1->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
Lbl1->SetWindowPlacement(&wpc);

//SMS button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 3); //3rd Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but2 = (CStatic *)GetDlgItem(CTXT_RSTSMS);
but2->ModifyStyle( 0, SS_NOTIFY);
but2->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but2->SetWindowPlacement(&wpc);

//Phone button location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 4); //4th Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CStatic *but3 = (CStatic *)GetDlgItem(CTXT_RSTPH);
but3->ModifyStyle( 0, SS_NOTIFY);
but3->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but3->SetWindowPlacement(&wpc);

//input username location set based on screen size
y1 = ScreenSizeY - ((ScreenSizeY / 5) * 5); //5th Control from bottom of screen
y2 = y1 + ((ScreenSizeY / 5) * 0.90);

CEdit *TXTB = (CEdit *)GetDlgItem(TXT_USERNAME);
TXTB->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
TXTB->SetWindowPlacement(&wpc);

// set font size for control box
// Initializes a CFont object with the specified characteristics.
//pFont1 large font adjusted for y axis
VERIFY(pFont1.CreateFont(
((ScreenSizeY / 5) * 0.5), // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//pFont2 medium font adjusted for y axis
VERIFY(pFont2.CreateFont(
((ScreenSizeY / 5) * 0.4), // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//pFont3 small font/ small ht and wdth/ underlined - adjusted for y axis
VERIFY(pFont3.CreateFont(
((ScreenSizeY / 5) * 0.3), // nHeight
(ScreenSizeX / 50), // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
TRUE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
NULL)); // lpszFacename

//now set fonts on each control
GetDlgItem(TXT_USERNAME)->SetFont(&pFont1);
GetDlgItem(CTXT_RSTPH)->SetFont(&pFont2);
GetDlgItem(CTXT_RSTSMS)->SetFont(&pFont2);
GetDlgItem(LBL_INFO_BOX)->SetFont(&pFont3);
GetDlgItem(CTXT_QUIT)->SetFont(&pFont2);

pFont1.Detach();
pFont2.Detach();
pFont3.Detach();
}

/********************************************************************
*
* SETBUTTONS
*
* Turn Control on or off on the screen
*
* ENTRY:
* BOOL TOGGLE - true or false
*
* EXIT:
* nothing
*
********************************************************************/

void CXAMobileLogoffDlg::SETBUTTONS(int IDDCtrl, int TYPCtrl, BOOL TOGGLE)
{
CStatic *CTXTB;
CEdit *TXTB;

switch ( TYPCtrl ) {

case 1 : //CStatic
CTXTB = (CStatic *)GetDlgItem(IDDCtrl);
CTXTB->ShowWindow(TOGGLE);
break;

case 2 : //cedit
TXTB = (CEdit *)GetDlgItem(IDDCtrl);
TXTB->ShowWindow(TOGGLE);
break;
} //switch

}

/********************************************************************
*
* SETSTATUSTEXT
*
* Display a status of current process or result in the info box static text control
*
* ENTRY:
* MESSAGESTR (input)
* test string with message part
* MESSTYPE (input)
* type of message depending on query
*
* EXIT:
* nothing
*
********************************************************************/

void CXAMobileLogoffDlg::SETSTATUSTEXT(LPTSTR MESSAGESTR1, LPTSTR MESSAGESTR2, int MESSTYPE)
{
DWORD dwMESS;
LPTSTR szMESS;

UpdateData(TRUE);
//firstly hide all of the buttons that don't need to be shown at this time
SETBUTTONS(CTXT_RSTPH, 1, FALSE);
SETBUTTONS(CTXT_RSTSMS, 1, FALSE);
SETBUTTONS(CTXT_QUIT, 1, FALSE);
SETBUTTONS(TXT_USERNAME, 1, FALSE);

switch ( MESSTYPE ) {

case 1 :
dwMESS = _tcslen(MESSAGESTR2) + 1 + _tcslen(MESSAGESTR1);
szMESS = (LPTSTR) LocalAlloc(0, (dwMESS + 1) * sizeof(TCHAR));
_tcscpy(szMESS, _T(MESSAGESTR2));
_tcscat(szMESS, _T(MESSAGESTR1));
break;

case 2 :
dwMESS = _tcslen(MESSAGESTR2)+1;
szMESS = (LPTSTR) LocalAlloc(0, (dwMESS + 1) * sizeof(TCHAR));
_tcscpy(szMESS, _T(MESSAGESTR2));
break;

} //switch

//set info box to display status of query
m_LBL_INFO_BOX =_T(szMESS);
LocalFree(szMESS);

//refresh screen to display changes
UpdateData(FALSE);

}

/********************************************************************
*
* GetDomain
*
* Get The name of the domain
*
* ENTRY:
* nothing
*
* EXIT:
* domain name
*
********************************************************************/

LPTSTR CXAMobileLogoffDlg::GetDomainName()
{
TCHAR lpDomainName[MAX_VALUE_NAME] = TEXT("WORKGROUP"); // default in case call fails...
DWORD dwDomainName = sizeof(lpDomainName);

WKSTA_INFO_100 *pWorkstationInfo = NULL;

if ( NetWkstaGetInfo( NULL, 100, (LPBYTE *)&pWorkstationInfo ) ==
NERR_Success ) {
#ifdef UNICODE
_tcscpy( lpDomainName, pWorkstationInfo->wki100_langroup);
#else // multibyte char set - definition in project properties - config properties - general - character set.
wcstombs( lpDomainName, (LPWSTR)(pWorkstationInfo->wki100_langroup),
sizeof(lpDomainName) );
#endif // UNICODE
} else {
SETSTATUSTEXT(NULL, _T("Could not locate Domain.\r\nPlease contact your administrator."), 2);
}

// Free the allocated memory.
if (pWorkstationInfo != NULL) {
NetApiBufferFree(pWorkstationInfo);
}

return lpDomainName;
}

/********************************************************************
*
* QuerySession
*
* Query and display session data
*
* ENTRY:
* hServer (input)
* handle to server
* SessionId (input)
* queried session id
* InfoClass (input)
* WTS_INFO_CLASS
* qryusername (input)
* username to query
*
*
* EXIT:
* bool - if username is a match with session username
*
*******************************************************************/

BOOL
CXAMobileLogoffDlg::QuerySession( HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS InfoClass, LPTSTR qryusername)
{
LPTSTR pSessionInfo;
DWORD ByteCount;
int result;

if ( !WTSQuerySessionInformation( hServer,
SessionId,
InfoClass,
&pSessionInfo,
&ByteCount ) ) {

//TS Query Session Information failed
return 0;
}

switch ( InfoClass ) {

case WTSUserName :

result = _tcsicmp(pSessionInfo, qryusername);
if (result == 0) { //the result was a match
WTSFreeMemory( pSessionInfo );
return 1;
}
break;
} //switch

WTSFreeMemory( pSessionInfo );
return 0;
} // QuerySession()


/********************************************************************
*
* LogoffSession
*
* Logoff specified session
*
*
*
* ENTRY:
* hServer (input)
* handle to server
*
* EXIT:
* nothing
*
*
*******************************************************************/

void
CXAMobileLogoffDlg::LogoffSession( HANDLE hServer, DWORD SessionId )
{
if ( !WTSLogoffSession( hServer, SessionId, FALSE ) )
{
//Session ID not found.
return;
} else {
//Session ID found and logged off.
SETSTATUSTEXT(NULL, _T("Session Found.\r\nUser Succesfully logged off."), 2);

} //if

}

/********************************************************************
*
* EnumerateSessions
*
* Display a list of ICA/RDP sessions on the specfied server
*
* ENTRY:
* hServer (input)
* handle to server
*
* EXIT:
* nothing
*
********************************************************************/

void
CXAMobileLogoffDlg::EnumerateSessions( LPTSTR lpServerName, LPTSTR lpUsername, HANDLE hServer, int LogoffType)
{
PWTS_SESSION_INFO pSessionInfo;
DWORD Count;
DWORD i;
DWORD argSessionID; //session id entered
int result;
int foundID; //found session id

TCHAR* tmpmsg;

SETSTATUSTEXT(lpServerName, _T("Scanning .... "), 1);


if ( !WTSEnumerateSessions( hServer,
0, // reserved
1, // version 1
&pSessionInfo,
&Count ) ) {
//there was a problem enumerating session on the given server
SETSTATUSTEXT(lpServerName, _T("Could not Enumerate Sessions ..."), 1);
return;
}

foundID=0;

for ( i=0; i < Count; i++ ) {
if (LogoffType == 1) {
//logoff all sessions
if (pSessionInfo[i].SessionId < 65535) {
LogoffSession( hServer,pSessionInfo[i].SessionId);
} //if
} //if
if (LogoffType == 2) {
//logoff all disc sessions
if (pSessionInfo[i].State == 4) {
LogoffSession( hServer,pSessionInfo[i].SessionId);
} //if
} //if
if (LogoffType == 3) {
//logoff only selected sessions off selected server
//now see if session id is a real number
argSessionID = _ttol(lpUsername);
if (IsNum(lpUsername) == 1) {
//variable is a session number
if (pSessionInfo[i].SessionId == argSessionID){
//the result was a match
//Logging off selected User
LogoffSession( hServer,pSessionInfo[i].SessionId);
foundID=1;
} //if check for sessionid
} else {
//entry is a possible username
if (QuerySession(hServer, pSessionInfo[i].SessionId, WTSUserName, lpUsername)) {
//the result was a match and the username was found on the server - now log them off
LogoffSession( hServer, pSessionInfo[i].SessionId);
foundID=1;
} //if check for username
} //if isnum

} //if
} //for
if ((foundID==0) && (LogoffType == 3)) {
// The user was not found on the given server. Move to next search until complete.
SETSTATUSTEXT(lpServerName, _T("The User could not be located on "), 1);
} else {
// The user was found on the given server, but this will only set the global flag, to check other servers.
FOUNDUSER = TRUE;
} //if
WTSFreeMemory( pSessionInfo );
} // EnumerateSessions()


/********************************************************************
*
* EnumerateServers
*
* Display a list of Citrix/RDS servers within the specified Windows
* NT domain
*
* ENTRY:
* pDomainName (input)
* Pointer to Windows NT domain name (or NULL for current
* domain)
*
* EXIT:
* nothing
*
*******************************************************************/

void
CXAMobileLogoffDlg::EnumerateServers( LPTSTR pDomainName, LPTSTR pUsername, LPTSTR lpCServerName, int LogoffType)
{
PWTS_SERVER_INFO pServerInfo;
DWORD Count;
DWORD i;

HANDLE hServer;

int pos; //postion of wildcard in string
int len; //length of servername
int result; //result integer for comparison code

TCHAR tmp1[MAX_VALUE_NAME]; //temporary string
LPTSTR argCompare1; // compare vaild entry from console

//init vars
argCompare1 = _T("*");

//reset the found user flag to false to start fresh search on username
FOUNDUSER = FALSE;


/********************************************************************
* PLEASE NOTE:
* The WTSEnumerateServers function was no longer working in Windows 2008.
* The WTSEnumerateServers function relies on NETBIOS to be functional in
* the domain. Windows 2008 server disables the computer browser service on
* the DC by default now, so Netbios is rendered useless. Enabling the computer
* browser service on the DC only will return full functionality of the WTSAPI
* functions. This is a great fix, but as Microsoft have said that they will
* no longer support Netbios in the future, I'm sure we'll have to revise our
* development strategy in TS environment. For the record as well, those that
* use the NETSERVEREnum function to do the same as WTSEnumerateServers, this
* also requires the Computer Browser Service to be enabled on a Windows 2008 DC.
* This is also the case for the Citrix WFAPI SDK - WFEnumerateServers
*
* The best replacement for this I have found is WNetOpenEnum().
*
*******************************************************************/

if ( !WTSEnumerateServers( pDomainName,
0, // reserved
1, // version 1
&pServerInfo,
&Count ) ) {
SETSTATUSTEXT(NULL, _T("Could not enumerate servers in Domain.\r\nPress here to continue..."),2);
return;
}

//check for wildcard in server name
pos = _tcscspn(lpCServerName, argCompare1);
len = _tcslen(lpCServerName);
if (pos //wildcard found
//copy section of argument for searching
_tcsncpy(tmp1, lpCServerName, pos);

//logoff all servers matching wildcard
for ( i=0; i < Count; i++ ) {
//now enumerate sessions on that server
//Case insensitive comparison of command line argument
result = 1;
result = _tcsnicmp( pServerInfo[i].pServerName,tmp1, pos );
if( result == 0) {
hServer = WTSOpenServer( pServerInfo[i].pServerName );

if ( hServer ) {
//_tprintf( TEXT("\nProcessing %s server... \n\n"), pServerInfo[i].pServerName);
switch ( LogoffType ) {
case 1 :
//logoff all sessions on server
EnumerateSessions( pServerInfo[i].pServerName, _T("*NULL*"), hServer, LogoffType);
break;
case 2 :
//logoff all disconnected sessions on server
EnumerateSessions( pServerInfo[i].pServerName, _T("*NULL*"), hServer, LogoffType);
break;
case 3 :
//logoff username
EnumerateSessions( pServerInfo[i].pServerName, pUsername, hServer, LogoffType);
break;
}
WTSCloseServer( hServer );
} else {
SETSTATUSTEXT(pServerInfo[i].pServerName,_T("Could not open "),1);
} //if hserver
} //if result
} //for

} //if pos
WTSFreeMemory( pServerInfo );
} // EnumerateServers()

/********************************************************************
*
* ClickedCtrl
*
* Perform an action based on which button was clicked.
*
* ENTRY:
* int nCTRL - which control was clicked on the dilog box
*
* EXIT:
* nothing
*
*******************************************************************/
void CXAMobileLogoffDlg::ClickedCtrl(int nCTRL)
{
//logoff the user from the farm and prepare an SMS or phone message ready to send
LPTSTR INPUT_username;
LPTSTR Server_Name;
int result;

//get username that is inputed withihn the app
//if a * is entered by itself, this will force a logoff of all users

GetDlgItemText(TXT_USERNAME, m_TXT_USERNAME);
INPUT_username = m_TXT_USERNAME.GetBuffer();
m_TXT_USERNAME.ReleaseBuffer();

//check for blank username entry
result = _tcsicmp(_T(INPUT_username),_T(""));
if( result == 0) {
SETSTATUSTEXT(NULL, _T("No Username Entered.\r\nPress here to continue..."),2);
} else {
//a string has been entered into the username text box - now search if the user is listed in the
//server farm.

//the server name will be a wild card, ensuring that it checks all RDS/ Citrix servers in the domain.
Server_Name = _T("*");

EnumerateServers( GetDomainName() , INPUT_username, Server_Name,3);
if (FOUNDUSER) {
//the user was found and logged off
SETSTATUSTEXT(NULL, _T("The User was successfully logged off.\r\nPress here to continue..."), 2);
Mobile_Device_Calls(nCTRL);
} else {
//the user was not found
SETSTATUSTEXT(NULL, _T("The User was not found on any servers.\r\nPress here to continue..."), 2);
} //if founduser
} //if result

//refresh screen to display changes
UpdateData(FALSE);
}


/********************************************************************
*
* CXAMobileLogoffDlg message handlers
*
********************************************************************/


/********************************************************************
*
* OnInitDialog()
*
* prepare the drawing of the dialog box on initial startup
*
* ENTRY:
* nothing
*
* EXIT:
* BOOL - sucess/failure
*
********************************************************************/
BOOL CXAMobileLogoffDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

//create paint brush for control colours - used to set colour for each control on dialog box
m_hPaintBrush = ::CreateSolidBrush(RGB(0, 0, 0));

//set default content for controls
m_LBL_INFO_BOX =_T("Freeware provided by Ctrl-Alt-Del IT Consultancy\r\nwww.ctrl-alt-del.com.au");
m_TXT_USERNAME = _T("");
m_CTXT_QUIT =_T("Quit");
m_CTXT_RSTSMS =_T("Logoff User \r\nand SMS");
m_CTXT_RSTPH =_T("Logoff User \r\nand Phone");

//initialise global vars
FOUNDUSER = FALSE;

//set up screen size and pos
SETOBJECTPOSITION();

//refresh screen to display changes
UpdateData(FALSE);

return TRUE; // return TRUE unless you set the focus to a control
}
/********************************************************************
*
* OnEraseBkgnd
*
* set the background colour of the dialog box to black (0,0,0)
*
* ENTRY:
* dialog control
*
* EXIT:
* BOOL - sucess/failure
*
********************************************************************/

BOOL CXAMobileLogoffDlg::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(0, 0, 0)); // dialog background color - black (0,0,0), white (255,255,255)
CBrush *pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
return bRes;
}

/********************************************************************
*
* OnCtlColor
*
* set the colours of the controls to the designated colour
*
* NOTE: CTLCOLOR_BRN does not respond to OnCtlColor
* Therefore no colour can be assigned to pushbuttons. Avoid using CButtons in this case.
* Instead use static text control to operate and look like buttons using a modal frame.
* Ensure that a static text control has SS_Notify enabled.
*
* For this control colour changer to work, you must declare ON_WM_CTLCOLOR()
* in the message map at the beginning of this code section e.g.
* BEGIN_MESSAGE_MAP(CXAMobileLogoffDlg, CDialogEx)
* ...
* ON_WM_CTLCOLOR() //for control colour setting
* ...
* END_MESSAGE_MAP()
*
* Also declare the following in XAMobileLogoffDlg.h
*
* protected:
* HICON m_hIcon;
* ...
* afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
* ENTRY:
* dialog control, current window, control colour
*
* EXIT:
* HBRUSH
*
********************************************************************/

HBRUSH CXAMobileLogoffDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
switch(nCtlColor)
{
case CTLCOLOR_EDIT:
m_hPaintBrush = ::CreateSolidBrush ( RGB(255,255,255 )); // white control background colour
pDC->SetBkColor(RGB(255,255,255)); // white background colour
pDC->SetTextColor(RGB(0, 0, 0)); // black text colour
return m_hPaintBrush;
break;

case CTLCOLOR_STATIC:
//all static text controls will be black with white text
m_hPaintBrush = ::CreateSolidBrush ( RGB(0,0,0 )); // black control background colour
pDC->SetBkColor(RGB(0,0,0)); // black background colour
pDC->SetTextColor(RGB(255, 0, 0)); // red text colour

//if we wanted to target a specific text control and make it a different colour, we can do this
if(pWnd->GetDlgCtrlID() == LBL_INFO_BOX) {
m_hPaintBrush = ::CreateSolidBrush ( RGB(0,0,255 )); // blue control background colour
pDC->SetBkColor(RGB(0,0,255)); // blue text background colour
pDC->SetTextColor(RGB(255, 255, 255)); // white text colour
} //if

if((pWnd->GetDlgCtrlID() == CTXT_RSTSMS) || (pWnd->GetDlgCtrlID() == CTXT_RSTPH)) {
pDC->SetTextColor(RGB(255, 255, 0)); // yellow text colour
} //if
return m_hPaintBrush;
break;

case CTLCOLOR_MSGBOX:
pDC->SetBkColor(RGB(255,0,0)); // red background colour
pDC->SetTextColor(RGB(255,255,255)); // white text colour
pDC->SetBkMode(TRANSPARENT);
return m_hPaintBrush;
break;
case CTLCOLOR_DLG:
pDC->SetBkColor(RGB(255,0,0)); // black bk color
return m_hPaintBrush;
break;

default:
return CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
} //switch

}

/********************************************************************
*
* OnPaint
*
* draw the dialog box
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/

void CXAMobileLogoffDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
} //if
}

/********************************************************************
*
* OnQueryDragIcon
*
* The system calls this function to obtain the cursor to display while the user drags
* the minimized window.
*
* ENTRY:
* nothing
*
* EXIT:
* HCURSOR
*
********************************************************************/

HCURSOR CXAMobileLogoffDlg::OnQueryDragIcon()
{
return static_cast(m_hIcon);
}

/********************************************************************
*
* OnEnKillfocusUsername
*
* hide keyboard after moving from username control
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnEnKillfocusUsername()
{
//Hide keyboard
Mobile_Device_Calls(7);
}

/********************************************************************
*
* OnEnSetfocusUsername
*
* show keyboard when entering username
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnEnSetfocusUsername()
{
//see if the quit button is visible. If it is not then don't allow user to activate the device keyboard
//during the time of processing the logoff request.
DWORD dw;

UpdateData(TRUE);
//get state of control
CStatic * btn1 = (CStatic *)GetDlgItem(CTXT_QUIT);
dw = btn1->GetStyle();
if( dw & WS_VISIBLE ) {
// The control is at the default state - allow keyboard to show whilst entering username
//show keyboard when entering username
Mobile_Device_Calls(6);
} else {
//hide keyboard
Mobile_Device_Calls(7);
}
btn1->Invalidate();

}

/********************************************************************
*
* OnStnClickedInfoBox
*
* Performs action after infobox text button is pressed once.
* NOTE: Cstatic text control must have SS_NOTIFY set to TRUE
*
* This control will perform multiple function based on the content of the dialog box
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnStnClickedInfoBox()
{
//see if the quit button is visible. If it is not then this control is in status mode
//indicating the another process is being performed within this app.
DWORD dw;

UpdateData(TRUE);
//get state of control
CStatic * btn1 = (CStatic *)GetDlgItem(CTXT_RSTPH);
dw = btn1->GetStyle();
if( dw & WS_VISIBLE ) {
// The control is at the default state - just showing the hyperlink to the CAD website
// hyperlink to website - I have put this feature in to see if the XenApp application
// will redirect content such as html to the client device and launch via a local web browser.
// Content redirection must of course be turned on in Citrix policies.
ShellExecute(0, "open", "http://www.ctrl-alt-del.com.au", "", "", SW_SHOWNORMAL);
} else {
// The control is at the status state - showing the result of the last process and waiting
// for a response

//show all of the buttons again
SETBUTTONS(CTXT_RSTPH, 1, TRUE);
SETBUTTONS(CTXT_RSTSMS, 1, TRUE);
SETBUTTONS(CTXT_QUIT, 1, TRUE);
SETBUTTONS(TXT_USERNAME, 1, TRUE);

//once a response is given, reset the app contents
m_TXT_USERNAME = _T("");
m_LBL_INFO_BOX =_T("Freeware provided by Ctrl-Alt-Del IT Consultancy\r\nwww.ctrl-alt-del.com.au");
}
btn1->Invalidate();

//refresh screen to display changes
UpdateData(FALSE);
}

/********************************************************************
*
* OnStnClickedQuit
*
* UNLOCK MOBILE DEVICE ORIENTATION BEFORE EXIT
* NOTE: Cstatic text control must have SS_NOTIFY set to TRUE
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnStnClickedQuit()
{
Mobile_Device_Calls(7); //hide keyboard
// UNLOCK MOBILE DEVICE ORIENTATION BEFORE EXIT
Mobile_Device_Calls(3);
EndDialog(IDOK); //exit app
}

/********************************************************************
*
* OnStnClickedRstsms
*
* logoff the user from the farm and prepare an SMS message ready to send
* NOTE: Cstatic text control must have SS_NOTIFY set to TRUE
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnStnClickedRstsms()
{
//hide keyboard
Mobile_Device_Calls(7);

//logoff the user from the farm and prepare an SMS message ready to send
ClickedCtrl(4);
}

/********************************************************************
*
* OnStnClickedRstph
*
* logoff the user from the farm and launch phone pad
* NOTE: Cstatic text control must have SS_NOTIFY set to TRUE
*
* ENTRY:
* nothing
*
* EXIT:
* nothing
*
********************************************************************/
void CXAMobileLogoffDlg::OnStnClickedRstph()
{
//hide keyboard
Mobile_Device_Calls(7);
//logoff the user from the farm and launch phone pad
ClickedCtrl(5);
}

Comments

Your comments or questions are more than welcome for this topic. Comments can be posted on the Citrix Community Mobility SDK forum or you can use our Ctrl-Alt-Del IT Consultancy web form to ask questions directly.
We hope that this page is of great assistance to you.