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í.
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 ...
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; }
El viernes 15 tuve la oportunidad de asistir a mi segundo Open Day y reencontrarme con toda esa gente tan especial, para los que solo tengo palabras de agradecimiento. También echamos de menos a algunos buenos amigos que no pudieron asistir, y a los que desde aquí les mando un saludo.
Por la tarde, me uní a las sesiones de desarrollo las cuales fueron sensacionales, David Salgado e Ignacio Alonso, nos dieron una charla sobre Depuración en entornos de producción .Net realmente fantástica, (David, a ver si organizamos lo de las camisetas [Be a pointer my friend]), después tuve la oportunidad de ver la sesión sobre Infopath y Forms Server que dio Cesar de la Torre, y por último antes de que nuestros tecnico-less hicieran acto de presencia, Unai Zorrilla (O bruxo mobile) dio una magnifica charla sobre el servicio de persistencia de Windows Workflow Foundation.
(la foto gracias a Luis Franco)

In order to add a new activity to our SharePoint Designer, first we need to begin a WorkFlow project in Visual Studio 2005, concretely, a Workflow Activity Library (Workflow activity library).

From this point we would create our activity; for this example I am going to create an activity that will send a text to the event viewer.
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: }
First, we declare a dependent property of the workflow called TextLogProperty, which we will use to pass the text that we wish to show in the event viewer.
The internal property of the activity will be TextLog; this internal property obtains and establishes the value from the dependent property of the workflow.
Then we define the Execute method, that will be the method in charge to visualize the message in the event viewer.
A good practice is that the activities include a Validador, that I have omitted in the example but that is highly recommended although not obligatory, or if we want to use the activity inside of the visual studio workflow designer.
Once we have made the compilation, we can test it first (another good practice) creating an application to host the workflow and checking the activity.
Finally, after we are sure that our activity works correctly, we must install it in the GAC in our SharePoint server. And we need to modify the Web.config to include our assembly in the following section: <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>
Now we need to modify the file WSS.ACTIONS that we saw in the first article in order to add our new action
<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>
What we have done in the first place is authorize our assembly so that he is now an integral part of the WorkFlow engine of SharePoint, and secondly, adding it to the WSS.ACTIONS file, we have indicated to the SharePoint Designer that we have added a new activity.
When we use SharePoint Designer to publish a site, at the moment that we created a new workflow or we published an existing one, he communicates with SharePoint and recovers the File WSS.ACTIONS to configurate the assistant of the WorkFlow.
In this way the new actions will be part of the file XOML that the SharePoint Designer will create. Finally the result within the SharePoint Designer will be as follow:


Without a doubts one of the best things of MOSS 2007 is the new SharePoint Designer, and within him, the workflow designer, that allows us speedy and simple form to design workflows.
As I have commented in other occasions, if we wish to make more complex workflows, or state machines, we need to use Visual Studio.
SharePoint Designer, allows us to make sequential workflows with predetermined activities. Nevertheless, the possibility to extend the basic activities exists and we can extend the functionality of ours workflows without to write them completely in Visual Studio. Out-the-box, SharePoint Designer includes 22 actions.

Those actions are defined in the file WSS.ACTIONS, that resides in the directory “C:\Program Files\Common files\Microsoft Shared \web server extensions\12\1033\TEMPLAT\Workflow”. In this file, the conditions and the actions that appear within SharePoint Designer are defined.
<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>
Herein, we can see the assembled dll that handles the condition, to which elements he is applied, and if it is possible to use the present item in the workflow.
Within the condition we have the rule for the designer, with the text that is going to appear; each rule uses a series of fields tags FieldBind, that connects with the necessary parameters in the assembled file.
Each tag FieldBind has an identifier that corresponds with the position of the field within sentence ID=1, and that will be %1 in the sentence attribute as well. Also we can see attributes like Text, that will be the text by defect, and the DesignerType, that is the type of field.
After that, we have the section of Parameters with the different parameters that will go to the assembled file. The Name attribute has to correspond with the Field attribute of tag FieldBind, and the direction of the parameter needs to be In for input and Out for output.
<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>
Now, we are going to see how the actions look in the same file; they are very similar to the conditions.
The first part includes the assembled one that will take care to handle the action. The second one, are the rules for the designer. In this case the fields that has been used are a combobox (DesignerType= " Dropdown ") and a line of text (DesignerType= " TextArea ") The third part of the parameters, besides to use both fields defined in the rule of the designer, uses three additional parameters that are:
__Context: that it is the context of the WorkFlow. __ListId: you go of the list with which we are working __ListItem: the present item
With these three fields (that they are pre-established, so that we can use them whenever we want) we have as much control on the list as on the job stream that is being carried out.
At the moment A file XSD for this type of archives does not exist.
Para añadir una nueva actividad a nuestro SharePoint Designer, 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, SharePoint Designer, que ahora disponemos de una nueva actividad.
Cuando usemos SharePoint Designer para editar un sitio, en el momento que creamos un nuevo workflow o editamos uno existente, SharePoint Designer 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á SharePoint Designer.
Finalmente el resultado dentro de SharePoint Designer es el siguiente:


Sin duda una de las mejores cosas de Office Sharepoint Server 2007 es el nuevo SharePoint Designer, 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. SharePoint Designer, 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 SharePoint Designer, 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 programa\Archivos comunes\Microsoft Shared\web server extensions\12\TEMPLATE\3082\Workflow”. En este archivo se encuentran definidas tanto las condiciones como las acciones que figuran dentro de SharePoint Designer. <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.
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
Evento: 13 de diciembre - "Los programadores y la seguridad"
.jpg) |
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.
|
|
Copyright © 2008 Carlos Segura. All rights reserved.
|
|