Archivo

Archive for 12 agosto 2011

Aplicación que se ejecuta como consola e interfaz gráfica


Siguiendo una petición en la galería de código de MSDN, he publicado este ejemplo: aplicación que se ejecuta como consola e interfaz gráfica. ¡Échale un ojo, descarga y vota si te gusta! A continuación transcribo el extracto del artículo.

Introducción

En ciertas ocasiones nuestra aplicación Windows necesita ejecutarse en modo de consola, o al menos, proveer esta interfaz bajo ciertas condiciones. Es decir, una aplicación que se comporte como aplicación Windows y aplicación consola.

Construyendo el ejemplo

El código asociado es una solución de Visual Studio 2010, y se requiere tener Visual C++ instalado (aunque no debe ser difícil portar hacia versiones anteriores). El proyecto de Visual C++ es  una aplicación MFC basada en ventana de diálogos. Para compilar sólo se necesita MFC y, dado que se hacen llamadas al API de Windows, algún SDK reciente.

Descripción

Al iniciar su ejecución, la aplicación determina si ha recibido un parámetro llamado /use-console. De ser así, crea una consola y la adjunta al proceso actual, para después crear un búfer y asociarlo a la consola. Posteriormente se escribe y lee información de la misma. En caso de no recibir dicho parámetro, la aplicación simplemente muestra una ventana de diálogo modal, con algunos controles en ella.

El bloque de código interesante lo presento a continuación:

if (objCmdLine.UseConsole()) 
{ 
    ::AllocConsole(); 
    HANDLE hBuffer = ::CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, 
        NULL, 
        CONSOLE_TEXTMODE_BUFFER, 
        NULL); 
    ::SetConsoleActiveScreenBuffer(hBuffer); 
         
    HANDLE hInputHandle = GetStdHandle(STD_INPUT_HANDLE); 
 
    CString strMsg;         
    strMsg.LoadString(IDS_INFO); 
    ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); 
    strMsg.LoadString(IDS_GREETINGS); 
    ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); 
         
    CONSOLE_READCONSOLE_CONTROL objCtrl; 
    ZeroMemory(&objCtrl, sizeof(CONSOLE_READCONSOLE_CONTROL)); 
    objCtrl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);         
 
    const int nInputSize = 256; 
    TCHAR szInput[nInputSize]; 
    ZeroMemory(szInput, sizeof(TCHAR) * nInputSize); 
    DWORD dwCharsRead; 
    ::ReadConsole(hInputHandle, szInput, nInputSize, &dwCharsRead, &objCtrl); 
 
    CString strInput(szInput); 
    strInput = strInput.Mid(0, dwCharsRead - 2); 
     
    strMsg.Format(IDS_HELLOFELLOW, strInput); 
    ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); 
    strMsg.LoadString(IDS_PRESSKEYTOEXIT); 
    ::WriteConsole(hBuffer, strMsg, strMsg.GetLength(), NULL, NULL); 
         
    ::ReadConsole(hInputHandle, szInput, 1, NULL, NULL); 
 
    ::FreeConsole(); 
} 
else  
{ 
    CSampleDlg dlg; 
    m_pMainWnd = &dlg; 
    dlg.DoModal(); 
}

Del bloque anterior, hay que notar las siguientes llamadas a funciones del API.

  • AllocConsole se encarga de crear una consola y asociarla al proceso actual.
  • CreateConsoleScreenBuffer y SetConsoleActiveScreenBuffer crea un búfer y lo asocia a la consola previamente creada.
  • GetStdHandle nos proporciona un manejador hacia la salida de la consola (i.e. para poder leer y escribir desde y hacia ella).
  • ReadConsole y WriteConsole se encargan de leer datos desde y escribirlos hacia la consola, respectivamente.
  • Por último, FreeConsole desasocia la consola creada.

Código fuente

  • AboutDlg (h y cpp) – el cuadro de diálogo de "acerca de".
  • ConsoleCommandLineInfo (h y cpp) – implementa la clase que analiza los parámetros de la aplicación.
  • Resource.h – el encabezado donde se guardan los valores de recursos.
  • SampleApp (h y cpp) – la aplicación; en particular, contiene el método InitInstance donde se decide si mostrar la aplicación Windows o la aplicación por consola.
  • SampleDlg (h y cpp) – la ventana de diálogo principal de la aplicación.

Más información

Para mayor información, revisa esta sección: Sobre Consolas en MSDN.

Anuncios
Categorías:C++, Código y ejemplos, WIN-32 Etiquetas: , ,

Consulta, descarga y exportación de perfiles de usuarios en SharePoint


Puedes consultar mi último código en la galería de código de MSDN: consulta, descarga y exportación de perfiles de usuarios en SharePoint. Algunos extractos a continuación.

Introducción

El presente ejemplo muestra cómo utilizar los servicios web para construir una aplicación que permita consultar, descargar y exportar los perfiles de usuario de SharePoint Server 2010, así como información suplemental.

Construyendo el ejemplo

El código de ejemplo es una solución hecha con Visual Studio 2010 para .NET Framework 3.5. No tiene alguna otra dependencia. Hace referencia a dos servicios web, cuyas clases proxy se generaron a partir de los servicios _vti_bin/UserGroup.asmx y _vti_bin/UserProfileService.asmx. El primer servicio está disponible en SharePoint Foundation 2010 y el segundo en SharePoint Server 2010. Nota: este programa funciona también con versiones anteriores de SharePoint, aunque es posible que algunas características no estén disponibles.

Descripción

El programa cuenta con un componente llamado ProfileSource. Éste actúa como fuente de datos para el programa, y contiene los métodos que obtienen la información de los perfiles mediante consultas hechas a los servicios web. Este componente implementa gran parte de la lógica de la aplicación. En particular el método OnDoWork es quien se encarga de descargar la información de los perfiles. A continuación se muestran extractos del mismo.

_worker.ReportProgress(0, Resources.ProfileSource_StartingServices); 
userGroup = new UserGroup(); 
PrepareService(userGroup, _connectionProperties.UserGroupWebService); 
userProfile = new UserProfileService(); 
PrepareService(userProfile, _connectionProperties.UserProfileWebService); 
 
_worker.ReportProgress(0, Resources.ProfileSource_DownloadSchematics);  
Table = CreateTable(userProfile);

El método PrepareService establece algunas propiedades básicas del servicio web, como la URL de donde se encuetra y las credenciales. Nota que éstas últimas varían dependiendo de si el usuario ha seleccionado usar las credenciales por defecto o no. Por otra parte, el método CreateTable utiliza el servicio UserProfileService y su método GetUserProfileSchema para descargar todos los campos disponibles desde el servidor, y con base en ellos generar el esquema de un objeto DataTable.

XmlNode mainNode = null; 
if (ConnectionProperties.AllSiteUsers) 
{ 
    _worker.ReportProgress(0, Resources.ProfileSource_DownloadAllUserInfo); 
    mainNode = userGroup.GetAllUserCollectionFromWeb(); 
} 
else 
{ 
    _worker.ReportProgress(0, Resources.ProfileSource_DownloadUserInfo); 
    mainNode = userGroup.GetUserCollectionFromWeb(); 
} 
_worker.ReportProgress(0, Resources.ProfileSource_UserInfoDownloaded);

El extracto anterior obtiene los usuarios disponibles del sitio (todos los registrados, o bien cualquiera que haya ingresado), mediante la llamada a los métodos GetAllUserCollectionFromWeb o GetUserCollectionFromWeb del servicio web UserGroup, según sea el caso.

XmlDocument doc = new XmlDocument(); 
doc.LoadXml(mainNode.OuterXml); 
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); 
nsmgr.AddNamespace("defns", "http://schemas.microsoft.com/sharepoint/soap/directory/"); 
 
string xpath; 
if (ConnectionProperties.AllSiteUsers) 
    xpath = "defns:GetAllUserCollectionFromWeb/defns:Users/descendant::defns:User"; 
else 
    xpath = "defns:GetUserCollectionFromWeb/defns:Users/descendant::defns:User"; 
XmlNodeList userNodes = doc.SelectNodes(xpath, nsmgr); 

Una vez descargados los usuarios, necesitamos analizar el documento XML regresado. Para ello hacemos uso de XPath. El extracto anterior muestra el código que genera la consutla XPath, y que nos devuelve una lista con todos los nodos que contienen la información de los usuarios.

Posteriormente, recorremos cada elemento de la lista y llamamos al método GetUserProfile, el cual usará al método GetUserProfileByName del servicio web UserProfileService. Los resultados obtenidos los insertamos como una fila en nuestra tabla, como se muestra en el siguiente extracto.

foreach (XmlNode userNode in userNodes) 
{ 
    double stepDouble = ((double)++count / (double)maxCount) * 100.0; 
    int stepInt32 = Math.Min((int)Math.Round(stepDouble, 0), 100); 
    string loginName = userNode.Attributes["LoginName"].Value; 
    _worker.ReportProgress(stepInt32, string.Format(Resources.ProfileSource_DownloadingProfile, loginName, stepInt32)); 
    DataRow row = GetUserProfile(userProfile, loginName); 
    Table.Rows.Add(row); 
 
    if (count >= maxCount) 
        break; 
}

Esta porción de código muestra cómo el método GetUserProfile obtiene el perfil de un usuario.

PropertyData[] allData = service.GetUserProfileByName(loginName); 
foreach (PropertyData data in allData) 
{ 
    if (row.Table.Columns.Contains(data.Name)) 
    { 
        if (data.Values.Length > 0 && data.Values[0].Value != null) 
            row[data.Name] = data.Values[0].Value.ToString(); 
        else 
            row[data.Name] = "[Empty]"; 
    } 
}

Adicional a los métodos mencionados, el componente cuenta con los siguientes miembros.

  • Table. Un objeto DataTable que contiene la información descargada de los perfiles.
  • ConnectionProperties. Un componente ConnectionProperties, el cual contiene la información necesaria para establecer la conexión y descarga de elementos (i.e. usuario, contraseña, URLs, etc.).
  • GetColleagues. Consulta al servicio web UserProfileService por los colegas de un usuario determinado.
  • GetInCommon. Consulta al servicio web UserProfileService por información que el usuario actual tenga en común con otro usuario determinado.
  • GetLinks. Consulta al servicio web UserProfileService por los enlaces de un usuario determinado.
  • GetMemberships. Consulta al servicio web UserProfileService por las membrecías de un usuario determinado.
  • GetOrganizations. Consulta al servicio web UserProfileService por las organizaciones a las que pertenece un usuario determinado.
  • GetPinnedLinks. Consulta al servicio web UserProfileService por los enlaces marcados de un usuario determinado.
  • SaveAs. Exporta los perfiles de usuario hacia un archivo de valores separados por coma (CSV) o a un XML.
  • Loaded. Evento que se dispara cuando el componente ha terminado de descargar la información de perfiles.
  • LoadProgress. Evento que se dispara cuando hay un avance significativo durante la descarga de información de perfiles.
  • ProfileFailure. Evento que se dispara cuando se intenta obtener el perfil de un usuario y éste falla (i.e. por cuestiones de permisos, porque no existe, etc.), en cuyo caso se puede continuar con el siguiente perfil (comportamiento por defecto) o bien terminar con el proceso.
  • Failure. Evento que se dispara cuando la carga de información de usuarios falla y lanza una excepción.

Como última precisión, comentar que el programa está localizado y puede visualizarse en inglés y en español.