1300 223 482
CSA
Cheaper Citrix Licensing



DABCC.com

XAQCODESCAN - Citrix XenApp bar code scanner in the palm of your hand.

XAQCodeScan is a mobile device enabled utility that executes from within a Citrix XenApp 6.5 session running through Citrix Receiver on the mobile device and uses the mobile device camera to scan a QR CODE barcode type and decode it to a readable format, then displaying the output to the screen of the mobile device.
QR Code (abbreviated from Quick Response Code) is the trademark for a type of matrix barcode (or two-dimensional code) first designed for the automotive industry. More recently, the system has become popular outside the industry due to its fast readability and large storage capacity compared to standard UPC barcodes. The code consists of black modules (square dots) arranged in a square pattern on a white background. http://en.wikipedia.org/wiki/QR_code
Other barcode types could be used with the correct SDK implemented.

XAQCodeScan Example

QRCODE:
www.ctrl-alt-del.com.au

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:

QRcode barcode decoding sdk (evaluation version only used) - http://www.aipsys.com/
Citrix MOBILITY SDK - http://community.citrix.com/display/xa/Download+SDKS
Microsoft Windows 2008 SDK - http://msdn.microsoft.com/downloads

Special Note Regarding aipsys decode SDK - This is a trial version only.
The trial version has these limitations:
With 2D barcode encoder and decoder SDK, some of the input element will be replaced with char '*' before encoding, and some of the output element will be replaced with '*' after decoding.
The Trial version have 30 days' evaluation time, you must remove it from your computer and your application after expiration.

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 XAQCodeScan here (VS10 C++ compiled):
XAQCodeScan.zip

Before you begin
You can generate your own QRCODE by downloading XRenQRCode here. XRen QRCode is a simple application that helps you encode simple text or information into a Quick Response code and viceversa. This way, you can easily generate this barcode type and use it in order to store personal information. This is a Freeware product that is not written by Ctrl-Alt-Del ® IT Consultancy.
Use the XRenQRCode software to generate your own QRCODE, save it as a .JPG, and print it on a page for testing XAQCodeScan from your mobile device. You will need at least one QRCODE when using XAQCodeScan.

Instructions for Use

  1. Download XAQCodeScan.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 XAQCodeScan.exe via the Citrix Access Console, selecting the approriate users to delivery the application to.
  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.
  7. Please note: For the camera to work correctly you must enable access to the SD card on the mobile device via the citrix receiver otherwise the error code 0x0000002c will occur. The error code 0x0000002C equates to CMP_ERROR_CLIENT_DRIVE_UNAVAILABLE in the cmpenum.h include file. This error code means that that the device is currently not supporting client drive mapping (CDM). By default, this is what the Citrix Receiver does. In order to change the default, you need to run Receiver and select settings. Follow these settings for your OS version:
    • Android: Press the menu button and then select "Session Options". The first item should be "SD card access". Change this setting to either Read or Full access. Read access should be enough but you might want Full access if you want the application to store files on the Android SD card. The reason for this is that Receiver saves the image to the SD card for the application to pick up using CDM.
    • IOS: Press the settings button. Select Advanced and then turn on "Shared Documents" (aka client drive mapping) which is needed to transfer the pictures from the receiver to the Citrix server.
    Also make sure the Mobility option and client drive mapping are enabled in the Citrix Group Policy.
    Thanks to Jeff Muir of Citrix Systems for this.
  8. Additional Note: Turn off that annnoying Special keys option whilst you're in the Citrix Receiver Options.
  9. Please Note: obviously the camera of the mobile device is important here and the quality of the picture taken is important , otherwise the barcode will not be able to be read by the software. However the picture taken by a mobile device could end up being around 1 - 3 Mb in size on average, which could take some time to travel from the phone back to the XenApp Server. To date there is no way that you can reduce the size of this image via the Citrix Mobilty SDK.

XAQCodeScan Example

Designed to quickly and easily scan and decode QRCODE barcode types from Citrix XenApp 6.5 and your mobile device (with Camera), this little program will provide a simple, mobile method for performing such a task. Using the Citrix Receiver on your mobile device and the camera installed on the device, the mobility of this utility allows for rapid reading of QRCODE barcode types that can then be sent to a remote database via Citrix XenApp 6.5 stored anywhere in the world. Launch the application from the Citrix Receiver for your mobile device and the rest is easy.

XAQCODESCAN has been designed to run on virtually all Mobile devices, with some features not available on some devices. A camera must be available on the device.
Just have your QRCODE barcode ready to read. The bardcode should be printed on paper or some form of physical format. Scanning a QRCODE barcode from an LCD Monitor will provide errors in the decode process.

  • Launch you application via the citrix Receiver on your mobile device.
  • When the application is available, Press the "Scan QRCODE image" button.
XAQCodeScan Example
XAQCodeScan Example
  • After pressing the button, the device's camera will appear. Take a photo of the QRCODE barcode and when you are happy that the picture is clear and contains all of the QRCODE barcode, press Done on the Camera. You will be returned to XAQCodeScan where the image will begin processing.
  • Once processed, the application will display the image of the barcode which you took with your camera and the result of that decoded image. If the image successfully decoded, a readable format of the barcode will be displayed.

Special Note Regarding aipsys decode SDK - This is a trial version only and has these limitations:
With 2D barcode encoder and decoder SDK, some of the input element will be replaced with char '*' before encoding, and some of the output element will be replaced with '*' after decoding.

XAQCodeScan Example
XAQCodeScan Example

If the image is blurred, distored or the barcode if not fully visible in the picture, an error will be displayed to the user. The user can quit the application after the decode process by pressing the QUIT button.

Tips when taking the photo:

  • Don't take a blurred image. Ensure it is in focus.
  • Images can contain other items and will decode success fully, just ensure the barcode is not blocked.
XAQCodeScan Example
XAQCodeScan Example
  • Provided the image is clear, a QRCODE barcode can be taken from most angles, except up side down.
  • The image should be taken directly pointing the camera at the barcode.
  • Images taken the wrong way up however will not decode correctly.
XAQCodeScan Example
XAQCodeScan Example
  • Images taken from a distance will work correctly provided they are clear.
  • Even images taken from a distance in dim light will decode provide they are clear of obsticles.
XAQCodeScan Example
XAQCodeScan Example
  • Images that are obscured will not decode and return an error.

The Code

System Requirements

Download
Download the source code for XAQCodeScan here (VS10 C++ compiled):
XAQCodeScan_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++.


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


// XAQCodeScanDlg.cpp : implementation file
/********************************************************************
* XAQCodeScan is a mobile device enabled utility that executes from within
* a Citrix XenApp 6.5 session running through Citrix Receiver on the mobile
* device and uses the mobile device camera to scan a QR CODE
* barcode type and decode it to a readable format, then displaying the output
* to the mobile device of the screen.
*
* QR Code (abbreviated from Quick Response Code) is the trademark for a type
* of matrix barcode (or two-dimensional code) first designed for the automotive
* industry. More recently, the system has become popular outside the industry
* due to its fast readability and large storage capacity compared to standard UPC
* barcodes. The code consists of black modules (square dots) arranged in a square
* pattern on a white background.
* http://en.wikipedia.org/wiki/QR_code
*
* Other barcode types could be used with the correct SDK implemented.
*
* 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:
*
* QRcode barcode decoding sdk (evaluation version only used) - http://www.aipsys.com/
* Citrix MOBILITY SDK - http://community.citrix.com/display/xa/Download+SDKS
* Microsoft Windows 2008 SDK - http://msdn.microsoft.com/downloads
*
* Special Note Regarding aipsys decode SDK - This is a trial version only
* The trial version has these limitations:
* With 2D barcode encoder and decoder SDK, some of the input element
* will be replaced with char '*' before encoding, and some of the output
* element will be replaced with '*' after decoding.
* The Trial version have 30 days' evaluation time, you must remove it from
* your computer and your application after expiration.
*
* 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 "XAQCodeScan.h"
#include "XAQCodeScanDlg.h"
#include "afxdialogex.h"
#include "PictureCtrl.h"
#include
#include
#include //citrix mobility SDK
#include //aipsys QRCODE SDK

using namespace std;


//globals definitions
#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

//global vars
LPTSTR fileloc[8192]; //global location of photo (jpg) storage on server

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


// CXAQCodeScanDlg dialog


CXAQCodeScanDlg::CXAQCodeScanDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CXAQCodeScanDlg::IDD, pParent)
, m_CTXT_QUIT(_T(""))
, m_CTXT_QSCAN(_T(""))
, m_CTXT_DECODE(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CXAQCodeScanDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, CTXT_Quit, m_CTXT_QUIT);
DDV_MaxChars(pDX, m_CTXT_QUIT, 254);
DDX_Text(pDX, CTXT_QSCAN, m_CTXT_QSCAN);
DDV_MaxChars(pDX, m_CTXT_QSCAN, 50);
DDX_Text(pDX, CTXT_DECODE, m_CTXT_DECODE);
DDV_MaxChars(pDX, m_CTXT_DECODE, 254);
DDX_Control(pDX, CTXT_QCODE, m_CTXT_QCODE);
}

BEGIN_MESSAGE_MAP(CXAQCodeScanDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_ERASEBKGND() //for background colour setting
ON_WM_CTLCOLOR() //for control colour setting
ON_STN_CLICKED(CTXT_Quit, &CXAQCodeScanDlg::OnStnClickedQuit)
ON_STN_CLICKED(CTXT_QSCAN, &CXAQCodeScanDlg::OnStnClickedQscan)
ON_WM_TIMER()
END_MESSAGE_MAP()

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

/********************************************************************
*
* Load_Qcode
*
* Loads Qcode image and displays it in CTXT_QCODE control
* ENTRY:
* LPTSTR - IMAGE_File_Name (input)
* type of action to perfrom
*
* EXIT:
* Nothing
*
********************************************************************/
int CXAQCodeScanDlg::Load_Qcode(LPTSTR IMAGE_File_Name)
{
//Load from a Byte Stream
CFile picFile;

if(picFile.Open(IMAGE_File_Name, CFile::modeRead | CFile::typeBinary))
{
BYTE* pBuffer = new BYTE[(unsigned int)picFile.GetLength()];
if(pBuffer != NULL)
{
picFile.Read(pBuffer, (UINT)picFile.GetLength());

//Load the Image to the control on the screen
m_CTXT_QCODE.Load(pBuffer, (size_t)picFile.GetLength());

delete pBuffer;
}
picFile.Close();
UpdateData(FALSE);
return 1;
} else {
return 0;
}

}

/********************************************************************
*
* Decode_Qcode
*
* Decodes Qcode image and return the decoded string
* ENTRY:
* LPTSTR - IMAGE_File_Name (input)
* type of action to perfrom
*
* EXIT:
* LPTSTR - decode string
*
********************************************************************/
int CXAQCodeScanDlg::Decode_Qcode(LPTSTR IMAGE_File_Name)
{
/********************************************************************************
* Special Note Regarding aipsys decode SDK - This is a trial version only *
* The trial version has these limitations: *
* With 2D barcode encoder and decoder SDK, some of the input element *
* will be replaced with char '*' before encoding, and some of the output *
* element will be replaced with '*' after decoding. *
* The Trial version have 30 days' evaluation time, you must remove it from *
* your computer and your application after expiration. *
********************************************************************************/

Result *p = NULL;
int nSize;
char buf[8192];
int i;

UpdateData(FALSE);
//call the decode module from the aipsys SDK - http://www.aipsys.com/
p =_QRCodeDecodeImageFile((UTF8_STRING)IMAGE_File_Name,&nSize);

if (p)
{
//the image was read correctly and will now be decoded to plain text
Result *pTemp = p;

for (i = 0; i < nSize; i++)
{
memset(buf,0x0,8192);
memcpy(buf,pTemp->pData,pTemp->nSize);
m_CTXT_DECODE = _T((UTF8_STRING)buf);
pTemp++;
}

_QRCodeFree(p,nSize);
return 1;

} else {
//the image was not read correctly - image may be blurry, not containing all of the barcode, or pixelated.
return 0;
} //if p
UpdateData(FALSE);
}

/********************************************************************
*
* 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);
}


/********************************************************************
*
* 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 CXAQCodeScanDlg::Mobile_Device_Calls(int Mobile_Action)
{

//procedure specific vars
CMPRESULT rc;
HANDLE hCMP = NULL;

//sms call vars
LPTSTR SMS_TEXT; //string for SMS message
DWORD dwMSMS_TEXT;

//picture call vars
UINT32 size;
CMP_PICTURE_STATE state;
CMP_UNIQUE_ID pictureId; //Unique picture id

UINT32 retsize;
LPTSTR filename[MAX_PATH];
LPTSTR tmp1[MAX_PATH]; //temporary string
LPTSTR tmp2[MAX_PATH]; //temporary string
LPTSTR strCompare1; // comparison string for file path
LPTSTR newLocation[8192]; //string to new location of file on server side
int pos;
int result;
BOOL copySuccess;

int load_result;
int decode_result;
int pic_state;

//display and orientation vars
int pixwidth;
int pixheight;
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

case 9: //take picture
/**************************************************************************
* Please note: For the camera to work correctly you must enable access to *
* the SD card on the mobile device via the citrix receiver otherwise the *
* error code 0x0000002c will occur. The error code 0x0000002C equates to *
* CMP_ERROR_CLIENT_DRIVE_UNAVAILABLE in the cmpenum.h include file. *
* This error code means that that the device is currently not supporting *
* client drive mapping (CDM). By default, this is what the Android *
* Receiver does. In order to change the default, you need to run Receiver *
* and select the settings. Press the menu button and then select *
* "Session Options". The first item should be "SD card access". *
* Change this setting to either Read or Full access. Read access should be*
* enough but you might want Full access if you want the application to *
* store files on the Android SD card. The reason for this is that Receiver*
* saves the image to the SD card for the application to pick up using CDM.*
* Also make sure the Mobility option and client drive mapping are enabled *
* in the Citrix Group Policy. *
* Thanks to Jeff Muir of Citrix Systems for this. *
**************************************************************************/

//init vars
load_result = 0;
decode_result = 0;
pic_state = 0;
strCompare1 = _T("\\");

//generate random CMP_UNIQUE_ID pictureId
pictureId = randomDWORD();

//DEBUG HELP: to prove and check if pictureId is changed each call
//char buffer2[65];
//_itoa_s( pictureId, buffer2, 65, 10 );
//MessageBox(buffer2);

// request a picture to be taken
rc = CMPTakePicture(hCMP, pictureId, CMP_IMAGE_FORMAT_JPEG);

if(CMP_SUCCESS(rc))
{
// FIND OUT IF PICTURE WAS TAKEN
//loop until the state of the picture is ready

do {

CMPGetPictureState(hCMP, pictureId, &size, &state);
/*Picture state values - there are only 2 - unlike what is in documentation which ays there are more
enum CMP_PICTURE_STATE
{ CMP_PICTURE_STATE_UNAVAILABLE = 0,
CMP_PICTURE_STATE_READY = 1
} ;
*/

//initialise strings
memset( &filename, 0, sizeof(filename) );
memset( &tmp1, 0, sizeof(tmp1) );
memset( &tmp2, 0, sizeof(tmp2) );


switch (state) {
case CMP_PICTURE_STATE_READY:

// the filename is UTF-8 and represents the full path and file name on the client device of the picture
memcpy (filename,strCompare1,strlen(strCompare1)+1);
rc = CMPGetPictureFilename(hCMP, pictureId, (UTF8_STRING)filename, sizeof(filename), &retsize);

if(CMP_SUCCESS(rc))
{
//get filename and copy it to a temp string
_tcscpy_s((UTF8_STRING)tmp1, sizeof(filename), (UTF8_STRING)filename);
/*now reverse the temp string, find where the first \ is and extract the filename in reverse
then turn it the right way around to get just the filename of the picture
e.g. picture will be seen on device as \\client\c$\dcim\12232232323.jpg
reverse it - gpj.32323223221\micd\$c\tneilc\\
find the first instance of \ and copy the string up to that position
to temp string
*/

_tcsrev((UTF8_STRING)tmp1);
pos = _tcscspn((UTF8_STRING)tmp1, strCompare1);
_tcsncpy((UTF8_STRING)tmp2, (UTF8_STRING)tmp1, pos);
_tcsrev((UTF8_STRING)tmp2 );

//make sure filename not empty
result = _tcsicmp( (UTF8_STRING)tmp2, _T(""));
if (result != 0) {
//file name not blank so continue
memset(&newLocation, 0, sizeof(newLocation));

//put the new path and the file name in one string ready for transfer
//set new picture location to user temp environment variable %temp% or _tgetenv("temp"));
//this wil alow image to be dleted if GPO settings clear %temp% on logoff
_tcscpy_s((UTF8_STRING)newLocation, sizeof(newLocation), _tgetenv("temp"));
_tcscat_s((UTF8_STRING)newLocation, sizeof(newLocation), _T("\\"));
_tcscat_s((UTF8_STRING)newLocation, sizeof(newLocation), (UTF8_STRING)tmp2);

UpdateData(FALSE);
//firstly set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_QSCAN, 1, TRUE);
SETBUTTONS(CTXT_DECODE, 1, TRUE);
SETBUTTONS(CTXT_Quit, 1, TRUE);
m_CTXT_QSCAN =_T("Image Loading...");
m_CTXT_DECODE =_T("Decode pending...");
UpdateData(FALSE);

// copy file and allow for the existing destination file to be overwritten
copySuccess = CopyFile((UTF8_STRING)filename, (UTF8_STRING)newLocation, FALSE);

if(copySuccess != FALSE)
{
//remove the picture from the device to keep everythng clean.
DeleteFile((UTF8_STRING)filename);
//load image ready to decode
//Please Note: obviously the camera of the mobile device is important here and
//the quality of the picture taken is important , otherwise the barcode will not
//be able to be read by the software. However the picture taken by a mobile
//device could end up being around 1 - 3 Mb in size on average, which could take
//some time to travel from the phone back to the XenApp Server. To date there is no
//way that you can reduce the size of this image via the Citrix Mobilty SDK.

//successfully copied now load the image on the screen for viewing purposes.
load_result = Load_Qcode((UTF8_STRING)newLocation);
UpdateData(FALSE);
if (load_result) {
pic_state = 1;
//set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_DECODE, 1, TRUE);
SETBUTTONS(CTXT_Quit, 1, TRUE);
UpdateData(FALSE);
//decode the image - QRCODE and show in CTXT_DECODE Control
decode_result = Decode_Qcode((UTF8_STRING)_T(newLocation));

if (decode_result) {
//set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_QSCAN, 1, TRUE);
m_CTXT_QSCAN =_T("RESULT:");

} else {
//set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_QSCAN, 1, FALSE);
m_CTXT_DECODE = _T("Error Decoding Image - Not Readable");
} //if decode_result
UpdateData(FALSE);
} else {
UpdateData(FALSE);
//set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_QSCAN, 1, FALSE);
SETBUTTONS(CTXT_DECODE, 1, TRUE);
SETBUTTONS(CTXT_Quit, 1, TRUE);
m_CTXT_DECODE = _T("Error loading File - Please try again");
UpdateData(FALSE);

} //if load result

} else {
//set the buttons on the screen to appropriate labels and visibility
SETBUTTONS(CTXT_QSCAN, 1, FALSE);
SETBUTTONS(CTXT_DECODE, 1, TRUE);
SETBUTTONS(CTXT_Quit, 1, TRUE);
m_CTXT_DECODE = _T("Error copying from Device");
UpdateData(FALSE);

} //if copy success

} //if result not empty

} //if CMP_SUCCESS(get pitcure filename)

//remove the picture from the temp location on the profile to keep everythng clean.
DeleteFile((UTF8_STRING)newLocation);
break;

default:
pic_state = 0;

} //switch
} while (pic_state == 0); //while CMPGetPictureState

} // if CMP_SUCCESS cmptakepicture

break; //take picture

} //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 CXAQCodeScanDlg::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);

//decode 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(CTXT_DECODE);
Lbl1->ModifyStyle( 1, SS_NOTIFY ); //turn off notify for static text boxes to be non-clickable
Lbl1->ModifyStyle(SS_CENTER, SS_CENTER);
Lbl1->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
Lbl1->SetWindowPlacement(&wpc);
//Lbl1->Invalidate(); // (if outside OnInitDialog)

//Scan 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_QSCAN);
but2->ModifyStyle( 0, SS_NOTIFY);
but2->GetWindowPlacement (&wpc);
wpc.rcNormalPosition = CRect(x1,y1,x2,y2);
but2->SetWindowPlacement(&wpc);

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

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

// set font size for control box
// Initializes a CFont object with the specified characteristics.
//pFont1 medium font adjusted for y axis
VERIFY(pFont1.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

//pFont2 small font/ small ht and wdth/ non-underlined - adjusted for y axis
VERIFY(pFont2.CreateFont(
((ScreenSizeY / 5) * 0.4), // nHeight
(ScreenSizeX / 25), // nWidth // 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 smaller wdth/ non-underlined - adjusted for y axis
VERIFY(pFont3.CreateFont(
((ScreenSizeY / 5) * 0.4), // nHeight
(ScreenSizeX / 65), // 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

//now set fonts on each control
GetDlgItem(CTXT_QSCAN)->SetFont(&pFont2);
GetDlgItem(CTXT_DECODE)->SetFont(&pFont3);
GetDlgItem(CTXT_Quit)->SetFont(&pFont1);

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

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

void CXAQCodeScanDlg::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

}

/********************************************************************
*
* OnEraseBkgnd
*
* set the background colour of the dialog box to black (0,0,0)
*
* ENTRY:
* dialog control
*
* EXIT:
* BOOL - sucess/failure
*
********************************************************************/

BOOL CXAQCodeScanDlg::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 CXAQCodeScanDlg::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 red 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() == CTXT_Quit) {
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_QSCAN) || (pWnd->GetDlgCtrlID() == CTXT_DECODE)) {
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

}



// CXAQCodeScanDlg message handlers

/********************************************************************
*
* OnInitDialog()
*
* prepare the drawing of the dialog box on initial startup
*
* ENTRY:
* nothing
*
* EXIT:
* BOOL - success/failure
*
********************************************************************/

BOOL CXAQCodeScanDlg::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_CTXT_QSCAN = _T("Scan QRCODE image");
m_CTXT_DECODE =_T("Freeware - Ctrl-Alt-Del IT Consultancy - www.ctrl-alt-del.com.au");
m_CTXT_QUIT =_T("Quit");

//set orientation of device
Mobile_Device_Calls(2);

//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
}

// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.

void CXAQCodeScanDlg::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();
}
}

// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CXAQCodeScanDlg::OnQueryDragIcon()
{
return static_cast(m_hIcon);
}


void CXAQCodeScanDlg::OnStnClickedQuit()
{
// UNLOCK MOBILE DEVICE ORIENTATION BEFORE EXIT
Mobile_Device_Calls(3);
CDialogEx::OnCancel();
}


void CXAQCodeScanDlg::OnStnClickedQscan()
{
//disable the scan button from being pressed during the scan and decode process.
GetDlgItem(CTXT_QSCAN)->EnableWindow(FALSE);

//hide keyboard
Mobile_Device_Calls(7);

//set orientatin to portrait
Mobile_Device_Calls(2);

//take photo from device, store jpg in temp location, load to screen and decode QRCODE
Mobile_Device_Calls(9);

//set orientatin to portrait
Mobile_Device_Calls(2);

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

}

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.