jueves, 20 de septiembre de 2012

Error al publicar una aplicación mediante ClickOnce con Visual Studio 2010

Una rápida, que me acaba de pasar y que si no lo apunto, la siguiente vez que me pase lo mismo estoy todo el día mirando por qué.

El problema

Abrimos una solución con Visual Studio 2010, vamos a las propiedades del proyecto que queremos publicar mediante ClickOnce, y le damos al botón “Publish” (o al “Publish Wizard”, realmente sucede lo mismo); resultado de la publicación:

image

¿Cómo?

A ver. Compilamos la solución y compila bien, volvemos a compilar todos los componentes y compilan bien, intentamos hacer la compilación y… el mismo error.

La solución

Voy a ir directamente a la solución, ya que no tengo explicación para este comportamiento:

1.- En el Solution Explorer, localizamos el proyecto.

2.- Sobre el proyecto, clic con el botón derecho del ratón, “Publish”

image

¿El resultado?

image

Que alguien me lo explique, por favor.

miércoles, 18 de julio de 2012

Cómo cambiar el icono de las bibliotecas de Windows 7

Un pequeño truquito para las vacaciones.

Objetivo

Lo que queremos es personalizar cómo se ven las bibliotecas en Windows 7:

image

Al turrón

Desafortunadamente, Windows no trae ningún interfaz gráfico para personalizar la visualización de las Bibliotecas (hasta donde yo recuerdo, claro). Afortunadamente, no es especialmente complicado.

  1. Abrimos un explorador de Windows y en la barra de direcciones ponemos la ruta “%AppData%\Microsoft\Windows\Libraries”
  2. Vemos que accedemos a la carpeta de las bibliotecas, pero con un formato un poco diferente:
    image
  3. Abrimos un Bloc de Notas y arrastramos (sí sí, arrastramos) el icono correspondiente a la carpeta a la que queremos modificar el icono. Como veremos se abre un archivo XML de configuración:
    image
  4. Para especificar que queremos que la biblioteca se muestre con un icono personalizado, lo único que haremos será copiar el icono deseado a esta carpeta (realmente es suficiente con que siempre esté en la misma ruta) y en el archivo que hemos abierto añadimos la siguiente entrada, justo encima de “<templateInfo>:
    <iconReference>C:\Users\XXX\AppData\Roaming\Microsoft\Windows\Libraries\NombreArchivoIcono.ico</iconReference>
  5. Cerramos el archivo guardando y… Listo!

Por supuesto, antes de hacer ningún cambio, guardad el contenido original por si la liamos…

martes, 3 de julio de 2012

Ensamblados comerciales y builds de TFS

O cómo modificar las builds para TFS 2010.

Cuando estamos desarrollando una aplicación, es frecuente que necesitemos o decidamos utilizar algún componente comercial para simplificar el propio desarrollo o para mejorar la experiencia del usuario.

En este artículo vamos a revisar y solucionar un problema muy común que aparece cuando queremos realizar compilaciones automáticas de estos proyectos mediante Team Foundation Server 2010.

El problema

Pongamos que en mi aplicación estoy utilizando un control de menú muy ‘mono’, que me pinta las barras de herramientas como si de la Ribbon de Office se tratara.

Pongamos también que ese control de menú es comprado y licenciado, en la clásica licencia de componentes para .NET que requiere una licencia para cada desarrollador pero no requiere licencias de ejecución (ojo que este punto es importante, este artículo sólo aplica en estos casos)

Termino mi jornada y hago un check-in del formulario que desencadena una compilación en el servidor de Builds.

¡Sorpresa!

image

La causa

Si observamos la captura anterior, podemos ver que el error al compilar se está generando con el archivo “licenses.licx”

¿Pero qué es este archivo?

En las condiciones que hemos especificado en el punto anterior, en este archivo aparecen los componentes de terceros que estemos utilizando en nuestro proyecto y que además requieran de un control de licenciamiento en la máquina de desarrollo. El contenido concreto, que no voy a reproducir aquí, es una serie de líneas de texto ‘plano’ que indican la ‘firma’ de los componentes que requieren licenciamiento.

El problema que está encontrando el servidor de Builds es que al lanzar la compilación está realizando las operaciones necesarias para comprobar la licencia de todos los controles que aparezcan en este archivo.

Una posible solución

Revisando este problema por Internet, una de las propuestas que he encontrado es quitar ese archivo del control de versiones, con lo que al lanzar la compilación el archivo no existirá y en consecuencia el servidor no intentará validar las licencias. Reconozco que esta opción no la he probado, porque excluir un archivo del proyecto del control de versiones me da la sensación de que me puede acabar generando problemas a posteriori.

La solución

Como la primera alternativa no me convence, es hora de probar otra cosa. He leído por ahí que si el archivo está vacío, hará el mismo efecto que si está fuera del control de fuentes. Esto por si sólo tampoco es que sea una mejora de lo anterior, ya que tendríamos que vaciar el archivo ‘a mano’ antes de cada Build (inaceptable)

Lo que vamos a hacer a cambio, es generar una tarea personalizada para el flujo de compilación que cuando se ejecute limpiará el contenido del archivo automáticamente. De esta forma, las copias de trabajo y el control de fuente no corren riesgo de quedar en un estado inestable, y a su vez el proceso de compilación funcionará correctamente.

Al turrón

Vamos a trabajar con Visual Studio 2010.

Creación del proyecto

  1. Creamos una solución con dos proyectos de “Class Library”. Uno de los proyectos contendrá las tareas personalizadas (CustomBuildActivities) utilizando .Net 4 y el otro contendrá la plantilla de proceso que vamos a modificar para incluir nuestra tarea (CustomBuildProcess).
  2. Localizamos la plantilla de proceso por defecto “DefaultTemplate.xaml” y cambiándole el nombre la incluimos en el proyecto CustomBuildProcess. Una vez incluida, tenemos que modificar su propiedad “Build Action” a “XamlAppDef”.

    image

Creando una actividad personalizada

Para que este sistema funcione, necesitaremos una actividad que nos permita manipular el atributo de sólo lectura de nuestro archivo, ya que si está marcado como de sólo lectura, difícilmente podremos manipular su contenido.

En nuestro proyecto CustomBuildActivities añadimos un nuevo elemento de tipo “Workflow”, “Code Activity”, en este caso voy a utilizar el nombre “SetReadOnlyFlag”. El código de esta clase está disponible en el archivo adjunto y sus características más relevantes son las propiedades que definimos para controlar el Workspace donde el servidor de compilación almacena los archivos y los archivos a manipular.

Necesitamos también una actividad que nos permita modificar el contenido del archivo. Para esta actividad voy a utilizar el nombre “UpdateLicensesContent”. El código de esta clase está disponible también en el archivo adjunto y sus características más relevantes son las propiedades de definimos para localizar el archivo a manipular. Básicamente lo que hace esta actividad es localizar el archivo y, si no está vacío, vaciarlo.

Actualizando el flujo de trabajo de compilación

Hasta ahora hemos preparado unas actividades que nos permitirán manipular el archivo de licencias, pero ahora llega el momento de utilizarlas.

Para ello abrimos el proceso de Build en el proyecto “CustomBuildProcess”. En la caja de herramientas veremos las actividades que hemos programado anteriormente:

image

Lo que vamos a hacer va a ser ubicar nuestras tareas en el punto adecuado del flujo, para ello en el flujo localizamos la siguiente sección:

image

Justo después de “Get Workspace” añadimos una actividad de tipo “Sequence”, le cambiamos el nombre de manera adecuada y añadimos dentro la actividad personalizada que hemos creado “SetReadOnlyFlag”.

image

Los indicadores de error significan que nos falta informar las propiedades de la actividad, para ello vamos al panel de propiedades:

image

Lo que vamos a hacer es permitir a los usuarios especificar los valores de la propiedad “FileMask” en la ventana de definición de la Build, para darle más flexibilidad al sistema y además los valores de la propiedad “Workspace” estarán basados en la propia Build actual.

Para permitir a los usuarios interactuar con la propiedad “FileMask”, añadimos un nuevo “Argument” al Flujo que estamos personalizando, utilizando para ello el botón “Arguments” que aparece en la parte inferior del diseñador del Flujo. Una vez que se abre el panel de argumentos, vamos al final del mismo y hacemos clic en “Create Argument”, creando nuestro argumento (en este caso “LicensesMask”) e indicando el valor deseado:

image

Antes de utilizar este argumento en nuestra actividad, tenemos que habilitar su ‘presencia’ en la ventana de definición de la Build. Para esto en el panel de argumentos localizamos “Metadata” y añadimos un parámetro a la colección:

image

Una vez añadido el parámetro, actualizamos la propiedad “FileMask” en la actividad “SetReadOnlyFlag” para utilizar el argumento que acabamos de crear, así como el resto de propiedades:

image

Una vez configurada la actividad, añadimos a continuación la segunda actividad personalizada “UpdateLicensesContent” y otra instancia de la actividad “SetReadOnlyFlag” y establecemos sus correspondientes propiedades:

Actividad “UpdateLicensesContent”

image

Segunda actividad “SetReadOnlyFlag”

image

y ya está todo preparado para utilizar el nuevo proceso de Build.

Utilizar el nuevo proceso de Build

Para poder utilizar el proceso de Build, tenemos que asegurarnos de que tanto el propio proceso como las actividades personalizadas están disponibles para el sistema. Para ello lo más común es añadir la plantilla de proceso a la carpeta “BuildProcessTemplates” y las actividades en una subcarpeta, por ejemplo “CustomActivities”. Esta carpeta está accesible desde el Team Explorer:

image

Lo siguiente que tenemos que hacer es indicar al controlador de compilaciones dónde tiene que ir a buscar las actividades personalizadas, para ello en el elemento “Builds”, hacemos clic con el botón derecho del ratón y elegimos “Manage Build Controllers”

image

En la ventana que se abre especificamos la ruta a las actividades personalizadas:

image

Ahora ya podemos generar una nueva compilación que utilice nuestra plantilla de proceso.

Lo único que tenemos que tener en cuenta al generar la nueva definición de build es que en la pestaña “Process” tendremos que elegir nuestra plantilla personalizada en lugar de la que propone Visual Studio por defecto:

image

El resultado

Una configurado el nuevo proceso de compilación, el resultado del mismo ya, por fin, es el esperado:

image

El código asociado a este artículo estará disponible próximamente como adjunto y en Codeplex, en cuanto lo tenga publicado actualizaré esta entrada. Si alguien quiere más información no tiene más que dejar un comentario.

viernes, 30 de marzo de 2012

Outlook y el misterio de la carpeta perdida

Hoy toca un off-topic, ya que esta semana he tenido un problema con el correo electrónico, que al ser tan, por denominarlo de alguna manera, ‘curioso’ no he podido evitar dejarlo reflejado por si me vuelve a pasar.

Situación inicial

Normalmente tengo siempre el cliente de correo (en mi caso Outlook 2010) abierto, y siempre en la Bandeja de Entrada.
En un momento dado, al volver del calendario a los correos, no pude hacerlo con los accesos directos de la parte inferior (operación que realizo habitualmente) en lugar de abrir la Bandeja de Entrada, me aparecía debajo del calendario las carpetas del buzón:
image
Primer pensamiento: “He hecho algo raro” así que le doy al botón de “volver atrás” del ratón, y vuelvo a la Bandeja de Entrada.
En otro momento dado, voy a una de las carpetas de RSS que tengo configuradas, y al ir a volver a la Bandeja de Entrada, de repente no está (se puede ver en la captura anterior, donde ya tampoco no aparecía, aunque en ese primer momento no me fijé)
Como podéis imaginar, tras unos ‘pocos’ juramentos y expresiones ofensivas, me tranquilicé un poco al ver que los correos seguían entrando y que lo ‘único’ que había pasado (que no es poco) es que no tenía un enlace a la propia carpeta de la Bandeja de Entrada.

En busca de la solución

A la hora de buscar una solución el tema era complicado, ya que ‘conscientemente’ no había realizado ninguna operación con Outlook distinta de las que puedo realizar cualquier día, e incluso no estaba seguro de cuándo había desaparecido la dichosa carpetita.
Lo primero, copia de seguridad de todo el buzón, y, sobre todo de los contactos y el calendario.
Lo segundo, San Google.
Gracias a este santo, llego a una descarga en Codeplex que permite acceder a las ‘tripas’ de almacenes MAPI, así que a descargar y a ver si internamente está todo en orden.
La herramienta es “MFCMAPI” y este es el acceso.
Ojo porque la versión a ejecutar (32 o 64 bits) depende de la versión de Outlook (bueno, o del sistema, no estoy seguro, porque yo tengo ambos en 64 bits)

Al turrón

Ejecutamos la aplicación (no voy a hacer comentarios acerca de la apariencia…)
image
0.- Cerramos el Outlook.
1.- Hacemos clic en el menú “Session” y a continuación en “Logon…”
2.- Elegimos el perfil de correo que queremos revisar.
image
En la ventana de la aplicación se cargarán todos los almacenes que tengamos configurados.
3.- Elegimos el correspondiente al buzón que está dando problemas (ante la duda, posiblemente sea el que está marcado como almacén por defecto)
image
4.- Con el botón derecho del ratón, hacemos clic sobre el y elegimos “Open Store”. Se abrirá una nueva ventana con un estructura de árbol que contiene todo nuestro buzón:
image
5.- La carpeta que nos interesa el la “IPM_SUBTREE” así que la desplegamos. Esta carpeta muestra todo el contenido de nuestra estructura en el Outlook:
image
En mi caso al menos, suspiro de alivio: La carpeta “Bandeja de Entrada” aparece ahí, la primera, no ha huido de mí.
6.- Seleccionamos la carpeta Y revisamos las propiedades (y sus valores) que se cargan en la parte derecha de la ventana. A pesar de que la mayoría de elementos no me suenan de nada, hay uno que llama la atención, tanto por el nombre en sí como por el valor que tiene asignado:
image
Entiendo que no hace falta, pero por si acaso, la propiedad que me llama la atención es la que aparece resaltada “PR_ATTR_HIDDEN” con un valor de “True”.
¿Será casualidad que una propiedad de mi carpeta desaparecida que se llama “HIDDEN” esté a True? Vamos a probar (recuerdo que tengo por si acaso una copia de seguridad de todo… o de casi todo)
7.- Hacemos doble clic en la propiedad, y en la ventana que aparece, establecemos el valor de la misma a “False”:
image
8.- Confirmamos con “OK” y al volver a la pantalla anterior, vemos que el valor ha cambiado (en este punto, sólo por asegurarme, reinicié el programa y volví a navegar hasta esa pantalla sólo para ver si el valor se había reseteado, pero seguía correcto, a False)
9.- Cerramos la aplicación, cruzamos los dedos y lanzamos el Outlook y…
¡¡SOLUCIONADO!! Ya tengo de nuevo la carpeta de la Bandeja de Entrada y todo vuelve a la normalidad.

¿Alguna explicación?

Yo desde luego no la tengo, pero por lo menos he conseguido ‘apañarlo’ sin perder nada.

viernes, 23 de marzo de 2012

TFS Builds, error con “Tracker.exe”

Una rápida para terminar la semana…

Situación inicial

Tras instalar un nuevo servidor de compilación, la primera Build configurada para un proyecto de tipo Windows Forms, falla con el siguiente mensaje:

image

 

Posibles soluciones

Como indica el propio mensaje, hay varias soluciones posibles:

  1. Instalar el Windows SDK v7.0A o superior.
  2. Instalar Visual Studio 2010.
  3. Establecer ‘a mano’ una propiedad en el registro de Windows.
  4. Desactivar la operación que desencadena el error en la compilación.

La primera opción entiendo que es la mejor y la que aplicaré en el servidor de compilación, pero no quería tener que hacerlo hoy.

Las segunda y tercera opciones, pues cómo decirlo, no me convencen. No quiero tener que instalar Visual Studio 2010 en un servidor de CI, y por supuesto que NO quiero tampoco andar jugueteando con el registro (si no es estrictamente necesario)

La cuarta opción es la que he aplicado de manera temporal y la verdad es que funciona perfectamente.

Desactivar la generación de recursos incremental

En el mensaje lo pone muy fácil:

You can turn off incremental resource generation by setting the "TrackFileAccess" property to "false"

Investigando por Internet, he visto que es un parámetro que se le puede indicar al “MSBUILD”, e incluso que se puede añadir en la definición XML de la compilación.

Lo que no tenía tan claro era cómo aplicar ese parámetro a la definición de la compilación desde el propio Visual Studio, y al final ha sido sencillo, basta con introducir el parámetro en la definición de la compilación, como aparece en la siguiente captura:

image

Ojo porque hay que añadir la marca de parámetro, quedando así:

/p:TrackFileAccess=false

Una nota curiosa, otras compilaciones configuradas en el servidor funcionaban correctamente, pero ninguna utilizaba archivos de recursos…

Y ya está solucionado, ahora que no se me olvide instalar el dichoso SDK.

martes, 20 de marzo de 2012

Impedir Cortar / Pegar en un ComboBox

Los controles ComboBox no disponen de una propiedad ReadOnly, únicamente podemos ‘jugar’ con la propiedad Enabled.

Sin embargo, en ocasiones no es suficiente con la propiedad Enabled, sino que necesitamos, por ejemplo, que se pueda seleccionar el texto del control. Para conseguir esto es relativamente sencillo generar una propiedad ReadOnly que lo que haga sea modificar el estilo del ComboBox a “ComboBoxStyle.Simple”. Peeero, no es esa parte la que nos interesa, sino que lo que vamos a explicar es cómo impedir que se pueda utilizar el ratón para ‘Cortar’ y/o ‘Pegar’ el contenido del control.

Al turrón

Lo primero que hacemos es generar una clase que herede de “NativeWindow”, en la que sobrescribiremos el método “WndProc” que se invoca cada vez que se lanza un mensaje al handle de la ventana.

El código de la clase aparece a continuación.

 

    /// <summary>
    /// Esta clase se utiliza para evitar las operaciones de Cortar / Pegar en los combos deshabilitados.
    /// </summary>
    public class DisallowCutPaste : NativeWindow
    {
        #region
Constantes
        private const int WM_CUT = 0x0300;
       
private const int WM_PASTE
= 0x0302;

        #endregion
Constantes

        #region
Overrides
        
/// <summary>
        /// Sobrecarga del método <see cref="WndProc"/> para evitar operaciones de cortar / pegar.
        /// </summary>
        /// <param name="m">
        /// El <see cref="Message"/> recibido por el objeto.
        /// </param>
        protected override void WndProc(ref Message m
)
        {
           
if (m.Msg == WM_CUT || m.Msg == WM_PASTE
)
            {
               
return
;
            }

           
base.WndProc(ref m
);
        }

        #endregion

    }

Una vez generada la clase, la aplicamos al control que queremos manipular (en este caso un ComboBox personalizado)


Para esto, en el código del control referenciamos la siguiente función del API:


 

        [DllImport("user32.dll", SetLastError = true)]
       
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter
,
           
string lpszClass, string lpszWindow
);

Ya sólo queda aplicar todos los elementos para que todo funcione, como podemos ver en este fragmento de código:


 

    IntPtr handle = FindWindowEx(this.Handle, IntPtr.Zero, "EDIT", null);
   
DisallowCutPaste p = new DisallowCutPaste
();
   
p.AssignHandle(handle
);

Una vez hecho esto, ya podremos comprobar que aunque en el combo tengamos disponible las opciones de ‘Cortar’ y ‘Pegar’ con el botón derecho del ratón, cuando el código anterior se ejecuta, al utilizar esas opciones, no sucede… NADA.


Nota


Hay que decir que estas instrucciones es recomendable ejecutarlas solamente cuándo sea necesario, por ejemplo en el “Set” de una propiedad, de forma que para desactivarlo sólo hace falta no ejecutar el código en el mismo “Set” de la propiedad (por ejemplo en un bloque ‘If’) el siguiente fragmento de código explica un poco mejor esta observación:

        public bool ReadOnly
        {
           
get
            {
               
return this._readOnly
;
            }
           
set
            {
               
if (value
)
                {
                   
IntPtr handle = FindWindowEx(this.Handle, IntPtr.Zero, "EDIT", null
);
                   
DisallowCutPaste p = new DisallowCutPaste
();
                   
p.AssignHandle(handle
);
                }
               
this._readOnly = value
;
            }
        }

Y esto es todo.

jueves, 23 de febrero de 2012

Ajustar el tamaño de un IFrame a su contenido

O cómo evitar que aparezcan scrolls repetidos en una aplicación web.
En relación con la entrada “Cómo acceder a una aplicación ASP.NET externa desde Sharepoint” apareció un problema con el visor de páginas (más bien con el IFrame utilizado para exponer la aplicación en Sharepoint) relacionado con las dimensiones del mismo.
En concreto, lo que sucede es que si la página que “asomamos” por el IFrame es más grande que la altura que le hemos asignado al IFrame, aparece una barra de scroll dentro del IFrame. Esto ya de por sí queda un pelín ‘raro’, pero si lo combinamos con que en la página ‘contenedora’ también aparece el scroll, entonces ya queda fatal de la muerte, con dos scrolls “independientes”
NOTA: Lo lamento enormemente, pero en esta ocasión no dispongo de ninguna de captura de pantalla, así que toca todo texto ‘plano’

Solución 1

La más rápida y sencilla, poner al IFrame una altura ‘suficiente’ para que no aparezca el scroll interior. ¿Hay alguien a quien se le ocurra el problema con esta solución?
¿no?
Venga, que es muy fácil: El problema es que si ponemos la altura muy grande (porque sabemos o intuimos que el contenido del IFrame es también grande, lo que conseguimos es que en la página ‘contenedora’ aparezca el scroll vertical… Pero siempre! Imaginad que una de las páginas que asomamos por el IFrame es más pequeña, entonces queda un scroll en la pantalla, pero todo vacío hacia abajo, de verdad no queda muy bien.

Solución 2

[CORRECCIÓN]] Ojo porque el código original publicado puede llevar a un problema de uso de CPU en el equipo cliente. Las correcciones necesarias aparecen en rojo en el código.

¿No estaría bien que el IFrame que utilizamos se ajustase a su contenido? Como podéis imaginar por el título del artículo, esto es precisamente lo que se va a explicar a continuación, y además es bastante sencillo.
NOTA: Una cosa a tener en cuenta. Esta solución se implementa con JavaScript y accede al contenido del IFrame, tenéis que tener en cuenta que si el contenido está en un dominio diferente al de la página contenedora, encontraréis problemas de “Acceso Denegado” salvo que implementéis las instrucciones del artículo mencionado al principio.

Al turrón (para la Solución 2)

En la página donde vamos a poner el IFrame (o mejor aún en un archivo .js) ponemos el código de JavaScript que tenemos a continuación. El archivo .js está disponible para descarga desde aquí.

<script type="text/javascript" language = "javascript" >
    function doIframe(){
        o = document.getElementsByTagName("iframe");
        for(i=0;i<o.length;i++){
            if (/\bautoHeight\b/.test(o[i].className)){
                removeEvent(o[i],'load', doIframe);
                setHeight(o[i]);
                addEvent(o[i],"load", doIframe);
            }
        }
    }
    function setHeight(e){
        if(e.contentDocument){
            e.height = e.contentDocument.body.offsetHeight + 35;
        } else {
            e.height = e.contentWindow.document.body.scrollHeight;
        }
    }
    function addEvent(obj, evType, fn){           
        if(obj.addEventListener)
        {
            obj.addEventListener(evType, fn,false);
            return true;
        } else if (obj.attachEvent){
            var r = obj.attachEvent("on"+evType, fn);
            return r;
        } else {
            return false;
        }
    }
    function removeEvent(obj, evType, fn){
        if(obj.removeEventListener)
        {
            obj.removeEventListener(evType, fn,false);
            return true;
         } else if (obj.detachEvent){
             var r = obj.detachEvent(""on""+evType, fn);
             return r;
         } else {
             return false;
         }
     }
</script>

No voy a entrar en qué hacen las funciones setHeight (creo que es obvio) y addEvent (para ésta, está Google) pero sí que me voy a detener un poco en la función doIframe, ya que es fundamental para el funcionamiento del script. Como podemos ver, esta función se recorre todos los IFrames que existan en la página, y les adjunta el evento “load”, pero solamente a aquellos IFrames que tengan especificada una estilo CSS “autoHeight”. ¿Por qué? Si no ponemos esa condición o similar, el script se ejecutará para todos los IFrames que tenemos en la página, y eso puede producir una sensación de descontrol, es preferible elegir cuáles de los IFrames queremos que se ajusten y cuáles no es necesario.
Con esto no hemos terminado, necesitamos el código de JavaScript que desencadena todo el proceso, se puede incluir en la página o en el mismo archivo .js:

<script type ="text/javascript" language ="javascript" >
    if (document.getElementById && document.createTextNode){
        addEvent(window,'load', doIframe);
    }
</script>

Estas instrucciones son las que consiguen que el proceso se lance al cargar la página en el navegador.
Y ya para terminar un detalle del IFrame, para recordar la condición del estilo css:

<iframe id = "xxx" frameborder = "0" scrolling = "no" runat ="server" class = "IframeCarga autoHeight" visible = "false" src = "/dirección" ></iframe>
Y esto es todo!!!!