• View Communities
    • Citrix Developer Network
      The place for unfiltered straight talk on Citrix products. Blogs, code downloads, best practices, APIs, and more can all be found here.
    • Citrix Ready Community Verified
      Does it work with Citrix? Application compatibility questions are a thing of the past with the new Citrix Community Verified site.
    • Blogs
      Learn the latest from the Citrix employees who are building application delivery infrastructure technologies.
    • Blogosphere
      The Citrix Blogosphere is a window into the thousands of conversations taking place about Citrix and Application Delivery.
  •  Sign In
XenApp Developer Network

How to get a user's client IP address and other information using Citrix MFCOM - Visual C++

Description

There is a lack of examples of using MFCOM in Visual C+. Additonally there are a lot of bugs that arise when you try and build your code based on the examples in the MFCOM SDK. This example show a programmer how to display the information about a client's session to the command line. It displays items such as Client IP, Session Name and other relevant information. The comments within the code highlight issues that may arise when making your own MFCOM example and how to solve them. One example of an apparent bug is the use of the Global variable MetaFrameCurrentSessionID. In all examples I tested, this variable failed to provide the correct information to the procedure call. The use of the WFAPI was the only way around this. I hope this assists the C+ coders out there.

Code Snippet

Beginners: Start a new Win32 Console Application in Visual C++, call it GetCTXNfo, and prepare a 'Hello World' project. Copy and code below and replace the pre-written code in GetCTXNfo.cpp with this code. Read the comments in the beginning of the code to learn how to set up the project environment for building. 

// GETCTXNFO.cpp :
//
// A utility to return the Client IP Address and other inofmration of
// the Citrix Client to the console. Requires Presenation Server 4.0/
// Xenapp 4.5 or above.
// written by Warren Simondson of Ctrl-Alt-Del IT Consultancy, Australia.
// www.ctrl-alt-del.com.au
// Citrix Server SDK WFAPI and MFCOM COMPILED PROGRAM
//
// NOTE: Passing MetaFrameCurrentSessionID to pFarm->GetSession(...
// does not work. MetaFrameCurrentSessionID which parses a value of -1
// is okay as a parameter itself but parsed to GetSession(... it returns
// session details of the console only, which means you don't get the
// current clients data, thus you get NULL in items such as
// pSession->get_ClientName(, pSession->get_UserName and so on.
//
// As a work around, we must use the WFAPI and query the Session ID of
// the current user, and return the real session id of that user. this
// can then be used is the pFarm->GetSession(... call to return the
// correct values of the current client information.
// Please include mfcom.lib and wfapi.lib in your library modules decalration
// also include UNICODE,_UNICODE,_WIN32_DCOM,WIN32,_CONSOLE,_MBCS,X86 in your
// Preprocessor declarations of the Visual C++ project settings
// Also note that MFCOM Visual C++ examples should be built in Release mode and
// not in Debug mode. Release mode will make the build easier as many switches
// need to be set in Debug mode.
// This application will only return client information if the user is a member
// of the Xenapp administrators, however they do not need full rights. This is
// a requirement of MFCOM. The best practice to run this application as a domain
// user is to open the AMC or CMC, go to the ADMINISTRATOR branch and add new
// administrator. Add Domain users to the list and set their privileges to VIEW
// ONLY.
// After using MFCOM for the first time and realising what complete rubbish it is,
// I think I'll stick the ever faithful and reliable WFAPI. After all XENDESKTOP
// was primarily written using the WFAPI. 
#include "stdio.h"
#include "stdlib.h"
#include "stdafx.h"
#include "mfcom.h"
#include "wfapi.h"
#ifdef UNICODE
#define STRNCMP wcsncmp
#else
#define STRNCMP strncmp
#endif
#define MAX_IDS_LEN                 256
//
// Resource string IDs
//
#define IDS_ERROR_COM_FAILED        100
#define IDS_ERROR_CREATE_FARM       101
#define IDS_ERROR_INIT_FARM         102
#define IDS_ERROR_GET_SESSION       103
#define IDS_ERROR_NEEDS_SESSION     104
#define IDS_ERROR_CREATE_CHANNEL    105
#define IDS_ERROR_NO_MEMORY         106
#define IDS_ERROR_CLIENT_INFO       107
#define IDS_ERROR_SEND_FAILED       108
#define IDS_ERROR_CTXPING_FAIL      109
#define IDS_ERROR_WRITE_FAILED      110
#define IDS_ERROR_READ_FAILED       111
#define IDS_ERROR_WRITE_SHORT       112
#define IDS_ERROR_READ_SHORT        113
#define IDS_ERROR_PACKET_INVALID    114
#define IDS_ERROR_USAGE_INFO        115
#define IDS_ERROR_QI_WINFARM        116
#define IDS_ERROR_NOT_ADMIN         117
#define IDS_QUERY_RESULT            200
#define IDS_QUERY_C2H1              201
#define IDS_QUERY_C2H2              202
#define IDS_QUERY_C2H3              203
#define IDS_QUERY_C2H4              204
#define IDS_QUERY_C2H5              205
#define IDS_QUERY_C2H6              206
#define IDS_QUERY_C2H7              207
#define IDS_QUERY_C2H8              208
#define IDS_QUERY_C2H9              209
#define IDS_QUERY_C2H10             210
#define IDS_TOTAL_PACKET            211
#define IDS_ROUND_TRIP              212
#define IDS_SEND_BEGIN_PACKET       213
#define IDS_SEND_END_PACKET         214
#define IDS_BEGIN_PACKET_TIME       215
#define IDS_END_PACKET_TIME         216
#define IDS_SENDPING_SUCCESS        217
#define IDS_SENDPING_FAILED         218
#define IDS_INFO_SENDING            219
#define IDS_INFO_RECEIVED           220
#define IDS_DATA_SENT               221
#define IDS_DATA_RECEIVED           222
/********************************************************************
 *
 *  HelpCRT
 *
 *    Print help to screen
 ********************************************************************/
void
HelpCRT( int helpPerc)
{
 if (helpPerc ==1) {
  printf("GETCTXNFO is a command-line tool to retrieve and display, via the\n");
  printf("command line, inofrmation regarding a Citrix client session.\n\n");
 } 
  printf("\nThis utility is FREEWARE and was written by Warren Simondson of\n");
  printf("Ctrl-Alt-Del IT Consultancy, Australia. www.ctrl-alt-del.com.au\n\n");
  printf("Thankyou to the Citrix MFCOM SDK available from http://www.citrix.com/cdn\n\n");
 
  printf("Usage:\n");
  printf("GETCTXNFO [/?]\n\n");
  printf("     /?                  Displays this help information.\n\n");
  printf("IN NO EVENT WILL CTRL-ALT-DEL IT CONSULTANCY BE LIABLE TO YOU FOR ANY GENERAL,\n");
  printf("SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR OTHER DAMAGES ARISING OUT OF \n");
  printf("THIS PRODUCT.\n");
}
/********************************************************************
 * PrintMessage
 *
 * Display a message to stdout with variable arguments.  Message
 * format string comes from the application resources.
 *
 * ENTRY:
 *     nResourceID (input)  Resource ID of the format string to use in
 *                          the message.
 *     ...         (input)  Optional additional arguments to be used
 *                          with format string.
 *
 * EXIT:
 *     none
 *
 *******************************************************************/
VOID PrintMessage( int  nResID, ...  )
{
    WCHAR               str1[MAX_IDS_LEN + 1];
    WCHAR               str2[2 * MAX_IDS_LEN + 1];
    va_list             args;
    va_start(args, nResID);
    if (LoadStringW(NULL, nResID, str1, sizeof(str1))) {
        vswprintf(str2, str1, args);
        wprintf(str2);
        wprintf(L"\n");
    } else {
        fwprintf(stderr,
            L"PrintMessage(): LoadString %d failed, Error %ld)\n",
            nResID, GetLastError());
    }
    va_end(args);
} // PrintMessage
/********************************************************************
 *
 *  QuerySession
 *
 *    Query and return session ID
 *
 * ENTRY:
 *    hServer (input - handle to server)
 *    SessionID (input - session ID)
 * EXIT:
 *    Current Session ID - as seen by system
 *
 *******************************************************************/
DWORD
QuerySession( HANDLE hServer, DWORD SessionId)
{
    //WF_INFO_CLASS InfoClass;
 LPTSTR pSessionInfo;
    DWORD ByteCount;
    DWORD * pDword;
 
    if ( !WFQuerySessionInformation( hServer,
                                     SessionId,
                                     WFSessionId,
                                     &pSessionInfo,
                                     &ByteCount ) ) {
        wprintf( L"*** WF Query Session ID failed. ***");
        return NULL;
    }
 
 //get session id value
    pDword = (DWORD *) pSessionInfo;
 //return the value
  return *pDword;
   
 WFFreeMemory( pSessionInfo );
} // QuerySession()
/********************************************************************
 *
 *  GETClientNFO
 *
 *    Query and display current Session information
 *
 * ENTRY:
 *    none
 * EXIT:
 *    none
 *
 *******************************************************************/
void GETClientNFO ()
{
 HANDLE hServer;
 HRESULT             hr;
    MULTI_QI            IPArray[1];
    IUnknown*           pUnk;
    IMetaFrameFarm*    pFarm;
   
 WCHAR               ServerName[MAX_COMPUTERNAME_LENGTH + 1];
 DWORD               tmpLong;
    IMetaFrameSession* pSession;   //for general Client information
    BSTR                pBuf;
    LONG                ByteCount;
    USHORT              usPingCount;
    LONG                psessionID;
    IMetaFrameWinFarm2* pWinFarm;
    BOOL                bWhoAmI;
    hr = NOERROR;
    pUnk = NULL;
    pFarm = NULL;
    pSession = NULL;
    pBuf = NULL;
    ByteCount = 0;
    usPingCount = 0;
    pWinFarm = NULL;
 LONG CurrentSessionID;
  //
    // Initialize the COM library.
    //
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        PrintMessage(IDS_ERROR_COM_FAILED);
        goto ExitProcedure;
    }
    //
    // Initialize the IPArray.  We want the interface pointer for
    // IMetaFrameFarm interface.
    //
    ZeroMemory((char*)&IPArray[0], sizeof(MULTI_QI) * 2);
    IPArray[0].pIID = &IID_IUnknown;
    IPArray[1].pIID = &IID_IMetaFrameFarm;
    //
    // Create a farm object instance.
    //
    hr = CoCreateInstanceEx(CLSID_MetaFrameFarm, NULL, CLSCTX_SERVER,
        NULL, 2, &IPArray[0]);
    if (FAILED(hr)) {
        PrintMessage(IDS_ERROR_CREATE_FARM);
        goto ExitProcedure;
    }
    //
    // Get the interface pointers.
    //
    pUnk = (IMetaFrameFarm*)IPArray[0].pItf;
    pFarm = (IMetaFrameFarm*)IPArray[1].pItf;
    pUnk->Release();
    pUnk = NULL;
    //
    // Initialize the farm object.
    //
    hr = pFarm->Initialize(MetaFrameWinFarmObject);
    if (FAILED(hr)) {
        PrintMessage(IDS_ERROR_INIT_FARM);
        goto ExitProcedure;
    }
    //
    // Make sure the current user is a Citrix administrator.
    //
    hr = pFarm->QueryInterface(IID_IMetaFrameWinFarm2, (VOID**)&pWinFarm);
    if (FAILED(hr)) {
        PrintMessage(IDS_ERROR_QI_WINFARM);
        goto ExitProcedure;
    }
    pWinFarm->get_IsCitrixAdministrator(&bWhoAmI);
    if (bWhoAmI == FALSE) {
        PrintMessage(IDS_ERROR_NOT_ADMIN);
        goto ExitProcedure;
    }
    //
    // Get the current server name.
    //
    tmpLong = MAX_COMPUTERNAME_LENGTH + 1;
    GetComputerNameW(ServerName, &tmpLong);
    //
    // Use pBuf as a temporary BSTR.
    //
    pBuf = SysAllocString(ServerName);
    if (pBuf == NULL) {
        PrintMessage(IDS_ERROR_NO_MEMORY);
        hr = E_OUTOFMEMORY;
        goto ExitProcedure;
    }
    //
    // Get the session object for the current session.
    //
 // NOTE: Passing MetaFrameCurrentSessionID to pFarm->GetSession(...
 // does not work. MetaFrameCurrentSessionID which parses a value of -1
 // is okay as a parameter itself but parsed to GetSession(... it returns
 // session details of the console only, which means you don't get the
 // current clients data, thus you get NULL in items such as
 // pSession->get_ClientName(, pSession->get_UserName and so on.
 //
 // As a work around, we must use the WFAPI and query the Session ID of
 // the current user, and return the real session id of that user. this
 // can then be used is the pFarm->GetSession(... call to return the
 // correct values of the current client information.
 //
 // use wfapi to get current session id
 //
 hServer = WFOpenServer( pBuf );
 if ( hServer ) {
  CurrentSessionID = QuerySession(hServer, WF_CURRENT_SESSION);
  WFCloseServer( hServer );
 } else {
  wprintf( L"*** ERROR: cannot open server %ls. ***\n", pBuf );
 } //if hserver
 //
 // Now we have the real session id of the current client we can proceed.
 //
 hr = pFarm->GetSession(MetaFrameWinSrvObject, pBuf,
   CurrentSessionID, &pSession);
 if (FAILED(hr)) {
  
  PrintMessage(IDS_ERROR_GET_SESSION);
      
  goto ExitProcedure;
 }
 //
 // send the client information to the screen
 //
 wprintf( L"Client Session Info:\n\n" );
 pSession->get_UserName(&pBuf);
 printf("Username: %ls\n", pBuf );
 pSession->get_ServerName(&pBuf);
 printf("Server:%ls\n", pBuf );
 pSession->get_ClientName(&pBuf);
 printf("Client Name: %ls\n", pBuf );
 pSession->get_SessionName(&pBuf);
 printf("Session Name: %ls\n", pBuf );
 pSession->get_SessionID(&psessionID);
 printf("Session ID: %ld\n", psessionID );
 pSession->get_ClientAddress(&pBuf);
 printf("Client IP Address: %ls\n", pBuf );
 //pSession->get_VIPAddress(&pBuf);
 //printf("Client VIP: %ld\n", pBuf );
 SysFreeString(pBuf);
 pBuf = NULL;
 
ExitProcedure:
    if (pUnk != NULL) {
        pUnk->Release();
    }
    if (pFarm != NULL) {
        pFarm->Release();
    }
    if (pSession != NULL) {
        pSession->Release();
    }
    if (pBuf != NULL) {
        SysFreeString(pBuf);
    }
    if (pWinFarm != NULL) {
        pWinFarm->Release();
    }
    //
    // Unitialize COM library.
    //
    CoUninitialize();
    if (hr != NOERROR) {
        PrintMessage(IDS_ERROR_CTXPING_FAIL, hr);
    }
 return;
} //GETVIPnfo
/********************************************************************
 *
 *  main
 *
 *******************************************************************/
__cdecl wmain(int argc, WCHAR* argv[])
{
  
 //
    //check for number args
 //
 if (argc > 1)
 {
  HelpCRT(1);
  return 0;
 }
 //
    // Start process
    //
 GETClientNFO();
   
    return 1;
}
 

Disclaimer

These software applications are provided to you as is with no representations, warranties or conditions of any kind. You may use and distribute it at your own risk. CITRIX DISCLAIMS ALL WARRANTIES WHATSOEVER, EXPRESS, IMPLIED, WRITTEN, ORAL OR STATUTORY, INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. Without limiting the generality of the foregoing, you acknowledge and agree that (a) the software application may exhibit errors, design flaws or other problems, possibly resulting in loss of data or damage to property; (b) it may not be possible to make the software application fully functional; and (c) Citrix may, without notice or liability to you, cease to make available the current version and/or any future versions of the software application. In no event should the code be used to support of ultra-hazardous activities, including but not limited to life support or blasting activities. NEITHER CITRIX NOR ITS AFFILIATES OR AGENTS WILL BE LIABLE, UNDER BREACH OF CONTRACT OR ANY OTHER THEORY OF LIABILITY, FOR ANY DAMAGES WHATSOEVER ARISING FROM USE OF THE SOFTWARE APPLICATION, INCLUDING WITHOUT LIMITATION DIRECT, SPECIAL, INCIDENTAL, PUNITIVE, CONSEQUENTIAL OR OTHER DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You agree to indemnify and defend Citrix against any and all claims arising from your use, modification or distribution of the code.

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.

Tags

xenapp codeshare xenapp codeshare Delete
xenapp tools xenapp tools Delete
xenapp scripts xenapp scripts Delete
Enter tags to add to this page:
Please wait 
Looking for a tag? Just start typing.
  1. Feb 17, 2009

    Vishal Ganeriwala says:

    Thanks Warren for sharing this code with the community. Please feel free to cont...

    Thanks Warren for sharing this code with the community. Please feel free to contribute more samples and ask questions.

Add Comment

Related Links