El misterioso conv.u

Este será casi con seguridad mi último post del 2006, pero eso no lo puedo asegurar ya que cuando me pica la curiosidad, siento esa irresistible necesidad de saciarla. Y eso es lo que me ha ocurrido esta mañana. Como había comentado antes no había usado punteros en c# y este hecho me ha picado, de modo que he tratado ver como son manejados por el CLR.


Bien, todo era normal hasta que he visto este código IL en donde no entendía el misterioso conv.u,  el código que estaba usando es el siguiente:


  public static unsafe float TestPointer2()
        {
            float f = -128.625f;
            int* i = (int*) &f;
 
            float* fp = (float*)i;
            return *fp;
        }
 

Mi sesión de depuración:

!clrstack -a
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0xb7c (2940)
ESP EIP
0012f3f4 00e600ed TestCastPointer.Test.TestPointer2()
LOCALS:
0x0012f400 = 0x00000000
<CLR reg> = 0x00000000
<CLR reg> = 0x00000000
0x0012f3f4 = 0x00000000

0012f444 00e6009a TestCastPointer.Program.Main(System.String[])
PARAMETERS:
args = 0x01321b5c

0012f69c 79e88f63 [GCFrame: 0012f69c]

!ip2md 00e600ed
MethodDesc: 00973078
Method Name: TestCastPointer.Test.TestPointer2()
Class: 009713d0
MethodTable: 00973090
mdToken: 06000003
Module: 00972c14
IsJitted: yes
m_CodeOrIL: 00e600c0

!dumpil 00973078
ilAddr = 0040208c
IL_0000: nop
IL_0001: ldc.r4 -128.625000 //Carga (push) el valor float (4bytes) en el stack
IL_0006: stloc.0 //Saca del stack y carga en la variable 0
IL_0007: ldloca.s VAR OR ARG 0 //Carga la dirección de la variable local existente
//en la dirección (0) en el stack de evaluación.

IL_0009: conv.u //Aquí este misterioso conv.u que se supone convierte
//el valor del stack en un unsigned int

IL_000a: stloc.1 //Saca el valor (pop) del stack y lo carga en var 1

Estado del stack
0x0012f3f4 = 0xc300a000 (-128,625f)
CLR reg = 0x0012f3f4 (var 1)
IL_000b: ldloc.1                    //Carga la var 1 en el stack de evaluación
IL_000c: stloc.2 //Saca el valor (pop) del stack y lo carga en var 2
IL_000d: ldloc.2 //Carga la var 2 en el stack de evaluación
IL_000e: ldind.r4 //Carga en la pila un float indirectamente
IL_000f: stloc.3
IL_0010: br.s IL_0012
IL_0012: ldloc.3
IL_0013: ret

Obviamente, hay muchas cosas que todavía no entiendo del IL y menos el ensablador que es generado por el JIT, pero de eso hablaré el año que viene. LA cuestión es la siguiente, es ese conv.u. He puesto como se queda el estado de la pila del CLR tras ejecutar el IL_000a. La reflexión que me he hecho es la siguiente; si lo que hemos colocado en el stack es una dirección de memoria, en este caso la de la variable f, para que quiero convertirla a un int, ¿no es la dirección de memoria un int32? 

Tras darle algunas vueltas he visto el código que genera el JIT, ¿donde esta el conv.u?

    25:             float f = -128.625f;
0000002e mov dword ptr [ebp-3Ch],0C300A000h //carga el valor en [ebp-3Ch] (f)
26: int* i = (int*) &f;
00000035 lea eax,[ebp-3Ch] //carga en el acumulador la dirección de f
00000038 mov edi,eax //mueve el acumulador al índice de destino (edi)
27:
28: float* fp = (float*)i;
0000003a mov esi,edi //mueve el índice de destino al índice fuente (esi)
29: return *fp;
0000003c fld dword ptr [esi] //carga el contenido del índice fuente como float
0000003e fstp dword ptr [ebp-48h] //almacena el contenido del puntero base – 48h
00000041 nop
00000042 jmp 00000044
30: }
00000044 fld dword ptr [ebp-48h] //prepara el stack para la devolución cargando el float

Entonces ha sido cuando leyendo el Ecma-335 (1.1.4.2 Managed Pointers) me he encontrado con esto:

Managed pointers that do not point to managed memory can be converted (using conv.u or conv.ovf.u) into unmanaged pointers, but this is not verifiable.

¿tendrá que ver con esto?, yo creo que me quedo aquí.

¿Engaña lo Sexy?

Cuando regresaba a casa en el coche he estado pensando sobre las dos soluciones que he propuesto para el problema que puso Ricardo, la cuestión es ¿engaña lo sexy?.
Me explico, la primera versión que puse de la solución no era sexy, en el despacho, tengo una pizarra de esas velleda y pinte la estructura de un número de coma flotante, durante el día le eché unas miraditas pensando cómo hacerlo… en un momento  del día se me ocurrió la primera solución, la cual tras implementarla no tenía nada de sexy:


public float Solucion1(float f)
{
    Byte[] bits;
 
    bits = BitConverter.GetBytes(f);
    BitArray ba = new BitArray(bits);
    BitArray ba2 = new BitArray(32);
 
    // sign
    ba2[31] = !ba[31];
 
    // exp
    for (int i = 0; i < 8; i++)
    {
        ba2[30 – i] = ba[23 + i];
    }
 
    // mantissa
    for (int i = 0; i < 23; i++)
    {
        ba2[22 – i] = ba[i];
    }
                
    ba2.CopyTo(bits, 0);
 
    float ff = BitConverter.ToSingle(bits, 0);
    
    return ff;
}


No sé, será una apreciación mía, ó una deformación de tantos años pero hay veces que un conjunto de líneas de código me parecen sexys y esta primera solución desde luego que no lo era, de modo que al postearla puse un orgulloso LUEGO LA OPTIMIZO …

Durante la mañana de hoy, he retomado el problema y he dado con otra solución, esta desde luego a mi parecer era mucho, mucho más sexy (mi musa opina lo mismo que yo). Me gustaría conocer vuestras opiniones al respecto. La solución usa punteros, para evitar casts, desmonta un número float  usando operaciones AND, OR y desplazamiento de bits, después los reversa  y por último  monta de nuevo el float al más puro estilo de C.

Bien, de regreso, como he comentado al principio, he pensado (hay que ver lo que es capaz de pensar uno) en eso la belleza y la sensualidad del código (jeje, como suena esto) y venia recreándome en lo bonita que había quedado. En ningún caso he pensado que la primera solución fuera mala, solo que no me parecía sexy, lo cual me ha llevado a pensar en las distintas formas que hay de dar solución a los problemas y demás…  (Esto para otra discusión)

En el preciso momento de aparcar el coche, UNA DUDA me ha asaltado, ¿Y si el código SEXY no es tan eficiente como el otro? ¡¡¡¡HORRRORR!!!!!, no puede ser…

Mierda, he cenado y he tenido que comprobarlo (que le vamos a hacer) y ¿cuál ha sido el resultado?  ¿ENGAÑA LO SEXY?.

He ejecutado los dos fragmentos de código para 10000 números aleatorios y el código sexy es unos 15ms más lento que el otro.

Hummm… pero para 100000 las cosas cambian… el código sexy es unas 5 veces más rápido

Ale el debate está abierto, ya veís para un día que tiene de fiesta uno y lo dedica a pensar en estas cosas …

Programancia101

Seguro que este será uno de los blogs (Programancia101) que más asiduamente leeré, lo primero por que estoy seguro de que va ha ser divertido y además es de un tipo al que considero un crack (Ricardo Varela).. en fin no ha hecho más que empezar… dije que: si tenia tiempo trataría de hacer una versión optimizada, aun así creo que se puede hacer mejor. Entre otras cosas, me a servido para usar los punteros en .Net cosa que no tocaba desde mis tiempos C/C++


Bueno, aprovecho para felicitaros la navidad y la llegada del próximo año.


public unsafe float ReverseFloat(float f)
        {
            int* ip = (int *)&f;
            int ff = 0;
 
            // Descomponemos el float
           
            // signo – bit 31
            int s = ((*ip >> 31) == 0) ? 0 : 1;
            // exp – 
            int e = ((*ip >> 23) & 0xFF);            
            // mantissa
            int m = (*ip & 0x007fffff);
 
            // reversamos 
            //s = ~s;
            e = ReverseBits(e, 8);
            m = ReverseBits(m, 23);
 
            // montamos de nuevo el float
            ff |= s << 31;
            ff |= e << 23; 
            ff |= m;
 
            // evitamos casts
            float* fp = (float*)&ff;
            return *fp;
        }

 public int ReverseBits(int i, int bits)
        {
            int reverse = 0;
            while (bits– > 0)
            {
                reverse <<= 1;
                reverse = reverse + (i & 1);
                i >>= 1;
            }
            return reverse;
        } 

Acciones de flujos de trabajo personalizadas en SharePoint Designer (2)

Para añadir una nueva actividad a nuestro SPD, lo primero que debemos hacer es comenzar un proyecto de Workflow,  en concreto un Workflow Activity Library (Biblioteca de actividades para el flujo de trabajo).



A partir de ese punto crearíamos nuestra actividad, para este ejemplo voy a crear una actividad que consiste en enviar un texto al visor de eventos.


   1:  public partial class LogEventViewer : Activity
   2:  {
   3:     
   4:      public static DependencyProperty TextLogProperty =
   5:          DependencyProperty.Register(«TextLog», typeof(string), typeof(LogEventViewer));
   6:   
   7:      public LogEventViewer()
   8:      {
   9:          InitializeComponent();
  10:      }
  11:   
  12:      /// <summary>
  13:      /// Valor que figurará en el visor de eventos
  14:      /// </summary>
  15:      /// <value>Texto</value>
  16:      [Description(«Texto que saldrá en el visor de eventos»)]
  17:      [Category(«User»)]
  18:      [Browsable(true)]
  19:      [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  20:      public string TextLog
  21:      {
  22:          get { return (string) GetValue(TextLogProperty); }
  23:          set { SetValue(TextLogProperty, value); }
  24:      }
  25:   
  26:      /// <summary>
  27:      /// Ejecución de la actividad
  28:      /// </summary>
  29:      /// <param name=»provider»>Contexto de ejecución de la actividad</param>
  30:      /// <returns></returns>
  31:      protected override ActivityExecutionStatus Execute(ActivityExecutionContext provider)
  32:      {
  33:          EventLog eventLog = new EventLog(«Workflow»);
  34:   
  35:          eventLog.Source = «SharePoint Workflow»;
  36:   
  37:          try
  38:          {
  39:              eventLog.WriteEntry(TextLog, EventLogEntryType.Information);
  40:          }
  41:          finally
  42:          {
  43:              eventLog.Dispose();
  44:          }
  45:   
  46:          return ActivityExecutionStatus.Closed;
  47:      }
  48:  }

 


Lo primero que hacemos es declarar una propiedad dependiente del flujo de trabajo llamada TextLogProperty, a través de la cual pasaremos el texto que deseamos visualizar en el visor de eventos.

La propiedad interna de la actividad será TextLog, esta propiedad interna obtiene y establece el valor desde la propiedad dependiente del flujo de trabajo.

Solo nos queda el método Execute, que será el encargado de realizar el trabajo de visualizar el mensaje en el visor de sucesos.

Una buena práctica es que las actividades incluyan un Validador, que yo he omitido en el ejemplo pero que es altamente recomendado aunque no sea obligatorio, ó si queremos usar la actividad con el diseñador de Visual Studio.
 
Una vez tenemos nuestro ensamblado, podemos probarlo primero (otra buena práctica) creando una aplicación host de workflow.

Por último tras asegurarnos que nuestra actividad funciona correctamente, debemos instalarla en el GAC en nuestro servidor de SharePoint. Y debemos editar el archivo web.config para incluir nuestro ensamblado en el apartado siguiente

 <System.Workflow.ComponentModel.WorkflowCompiler>
<authorizedTypes>
……

<authorizedType Assembly=»IdeSeg.SharePoint.Workflow.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3bba710be857fdc1″
Namespace
=»IdeSeg.SharePoint.Workflow.Activities»
TypeName
=»*»
Authorized
=»True» />
</authorizedTypes>
</System.Workflow.ComponentModel.WorkflowCompiler>

Ahora hemos de modificar el archivo WSS.ACTIONS que vimos anteriormente
 <Action Name=»Log en visor de eventos»
ClassName=»IdeSeg.SharePoint.Workflow.Activities.LogEventViewer»
Assembly=»IdeSeg.SharePoint.Workflow.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3bba710be857fdc1″
AppliesTo=»all»
Category=»Personalizadas»>
<RuleDesigner Sentence=»Logear el evento siguiente %1″>
<FieldBind Field=»TextLog» Text=»este mensaje» Id=»1″ DesignerType=»TextArea»/>
</RuleDesigner>
<Parameters>
<Parameter Name=»TextLog» Type=»System.String, mscorlib» Direction=»In» />
</Parameters>
</Action>

Con esto lo que hemos hecho en primer lugar es autorizar nuestro ensamblado para que forme parte del motor de flujos de trabajo de SharePoint, y en segundo lugar al añadirlo en WSS.ACTIONS le hemos indicado a SharePoint, SPD, que ahora disponemos de una nueva actividad.

Cuando usemos SPD para editar un sitio, en el momento que creamos un nuevo workflow o editamos uno existente, SPD se comunica con SharePoint y recupera el Archivo WSS.ACTIONS para a través de este configurar el asistente de flujos de trabajo. De este modo las nuevas acciones formarán parte del archivo XOML que creará SPD.

Finalmente el resultado dentro de SPD es el siguiente:



 


Acciones de flujos de trabajo personalizadas en SharePoint Designer (1)

Sin duda una de las mejores cosas de MOSS 2007 es el nuevo SPD, y dentro de este, el diseñador de flujos de trabajo, que nos permite de forma ágil y sencilla diseñar nuestros flujos de trabajo.

Aunque como he comentado en otras ocasiones, si deseamos realizar flujos de trabajo más complejos, o máquinas de estados debemos usar Visual Studio.

SPD, solo nos permite realizar workflows secuenciales con las actividades que vienen predeterminadas. Sin embargo existe la posibilidad de ampliar las actividades básicas que nos encontramos dentro de SPD, con lo cual podemos extender la funcionalidad de nuestros workflows sin llegar a realizarlos completamente en Visual Studio.

De manera predeterminada, SharePoint Designer incluye 22 acciones.



Estas acciones se encuentran definidas en el archivo WSS.ACTIONS, que se encuentra en “C:Archivos de programaArchivos comunesMicrosoft Sharedweb server extensions12TEMPLATE3082Workflow”.

En este archivo se encuentran definidas tanto las condiciones como las acciones que figuran dentro de SPD.
<Condition Name=»Creado en un intervalo de fechas determinado»
FunctionName=»CreatedInRange»
ClassName=»Microsoft.SharePoint.WorkflowActions.Helper»
Assembly=»Microsoft.SharePoint.WorkflowActions,
                  Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c»

AppliesTo=»list»
UsesCurrentItem=»true»>
<RuleDesigner Sentence=»creado entre %1 y %2″>
<FieldBind Id=»1″ Field=»_1_» Text=»fecha» DesignerType=»Date»/>
<FieldBind Id=»2″ Field=»_2_» Text=»fecha» DesignerType=»Date»/>
</RuleDesigner>
<Parameters>
<Parameter Name=»_1_» Type=»System.DateTime, mscorlib» Direction=»In»/>
<Parameter Name=»_2_» Type=»System.DateTime, mscorlib» Direction=»In»/>
</Parameters>
</Condition>
En donde podemos ver el ensamblado que se ocupa de manejar la condición, a que elementos se aplica y si es posible usar el ítem actual del workflow.

Dentro de la condición tenemos la regla para el diseñador, con el texto que va a aparecer, cada regla usa una serie de campos los tags FieldBind, que se enlazan con los parámetros necesarios en el ensamblado.

Cada tag FieldBind tiene un identificador ID que se corresponde con la posición del campo dentro de la sentencia ID=1 será %1 en la sentencia.

También podemos ver atributos como Text que será el texto por defecto y el tipo de campo DesignerType.

Después tenemos la sección de Parameters con los distintos parámetros que se pasarán al ensamblado. El atributo Name ha de corresponderse con el atributo Field del tag FieldBind, y tenemos la dirección del parámetro In para entradas y Out para las salidas.


<Action Name=»Establecer estado de aprobación del contenido»
ClassName=»Microsoft.SharePoint.WorkflowActions.SetModerationStatusActivity»
Assembly=»Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,
               Culture=neutral, PublicKeyToken=71e9bce111e9429c»

AppliesTo=»list»
ListModeration=»true»
Category=»Acciones principales»
UsesCurrentItem=»true»>
<RuleDesigner Sentence=»Establecer estado de aprobación del contenido en %1 con %2″>
<FieldBind Field=»ModerationStatus» DesignerType=»Dropdown» Id=»1″ Text=»este estado»>
<Option Name=»Aprobado» Value=»Approved»/>
<Option Name=»Rechazado» Value=»Denied»/>
<Option Name=»Pendiente» Value=»Pending»/>
</FieldBind>
<FieldBind Field=»Comments» Text=»comentarios» Id=»2″ DesignerType=»TextArea» />
</RuleDesigner>
<Parameters>
<Parameter Name=»ModerationStatus» Type=»System.String, mscorlib» Direction=»In» />
<Parameter Name=»Comments» Type=»System.String, mscorlib» Direction=»Optional» />
<Parameter Name=»__Context»
      Type=»Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions» />
<Parameter Name=»__ListId» Type=»System.String, mscorlib» Direction=»In» />
<Parameter Name=»__ListItem» Type=»System.Int32, mscorlib» Direction=»In» />
</Parameters>
</Action>

A continuación vamos a ver como se ven las acciones en este mismo archivo, son muy similares a las condiciones.

La primera parte incluye el ensamblado que se ocupará de manejar  la acción.    

La segunda, las reglas para el diseñador. En este caso los campos que usa son un combobox (DesignerType=»Dropdown») y una línea de texto (DesignerType=»TextArea»)

La tercera parte la de los parámetros además de utilizar los dos primeros campos definidos en la regla del diseñador, podemos ver que usa tres parámetros adicionales que son:

__Context : que es el contexto del flujo de trabajo.
__ListId
: el id de la lista con la que estamos trabajando
__ListItem:  el ítem actual

Con estos tres campos (que están preestablecidos, de modo que podemos usarlos siempre que queramos) tenemos control  tanto sobre la lista como sobre el flujo de trabajo que se está llevando a cabo.


No existe un archivo XSD, para este tipo de archivos, por el momento.

Procedimientos y planes de Instalación

Tuve una sesión la semana pasada, donde hablé de la importancia de separar las cuentas administrativas.

Si bien durante aquella sesión, hablé de la importancia que tiene siempre mantener una separación entre la cuenta del Administrador (muchos, además de usarla para todo la dejan con el mismo nombre) y las cuentas de servicio, propias de los aplicativos, en el caso que nos ocupa, lógicamente, me estoy refiriendo a SharePoint.

Para poder realizar esta separación, hay que conocer bien, todos los entresijos que conlleva cada una de las cuentas que podamos ir necesitando, por eso recomiendo siempre la lectura de los procedimientos de instalación.

Después cada administrador podrá hacer lo que crea más conveniente, pero siempre conociendo el procedimiento.

Esto nos lleva a la resolución del siguiente problema, producido principalmente por (y como es un amigo al que le sucedió el problema, y tengo la confianza suficiente)

1.- No tomarse el tiempo debido para planear la instalación (cd, install, a ver q pasa)
2.- No leerse ni por encima la guía de instalación
3.- Excusarse con que no es un entorno de producción, solo pruebas (luego hablaré de esto)
4.- No tener cerveza fría y sí una nevera nueva

El problema viene cuando la aplicación que está realizando no le carga ciertos ensamblados desde el GAC, tras revisar lo que ocurre veo que el servicio DCOM, no permite la ejecución de IIS WAMREG, hummm, ¿Quien ha iniciado dicha aplicación?, ¿Qué permisos tiene?

Si bien se había creado una cuenta particular para la gestión de este servicio, no se le habían dado a esta todos los sacramentos necesarios para funcionar. Posiblemente y para un entorno de pruebas, con añadir la cuenta al grupo de Administradores hubiera bastado ya que este grupo tiene permisos para iniciar servicios DCOM.

De modo que una vez dimos permisos a la cuenta en los servicios de componentes, todo funciono como la seda.

Bien, por último quería matizar que la instalación de un entorno de pruebas, no es una excusa para instalar mal las cosas, fruto de esto vendrán luego los problemas al paso a producción. Un buen escenario de pruebas, debe ajustarse en la medida de lo posible al escenario final.

Incluso para realizar pruebas y desarrollos, se puede virtualizar un sistema de producción, así evitaremos gran cantidad de problemas.

Planning and architecture for Office SharePoint Server 2007

Deployment for Office SharePoint Server 2007

NavarraDotNet – 13 de diciembre – "Los programadores y la seguridad"

Evento: 13 de diciembre – «Los programadores y la seguridad»







Ya es oficial, el miércoles 13 de diciembre a las 18:30 horas se va a celebrar nuestro primer evento.

Tendrá lugar en el CES y correrá a cargo de Chema Alonso que nos hablará sobre «Los programadores y la seguridad».

Aunque la entrada es libre, es necesario registrarse antes. Para obtener mas información sobre el evento visita ésta página.