Algoritmia - We are geeks, after all ;)
  Crear una cuenta
Inicio Buscar Foros Chat Downloads Login
 [Login]
Foros
Ir a los foros 

Menú Trucos Ax
 Inicio(Noticias) :
 Inicio
 Enviar Noticias
 Archivo de noticias
 Buscar
 Temas

 Miembros :
 Lista de miembros
 Tu Cuenta(Regístrate)

 Comunidad :
 Chat

 Foros

 Links

 Downloads

 Estadísticas :
 Estadísticas
 Top 10
 Encuestas

 Otros Servicios :
 Publicidad


 Miscelanea :
Añadir a favoritos
Contactar


Hemos recibido

impresiones desde Diciembre 2005


 Enlázanos :
Copia el código siguiente en tu web para enlazarnos :


Trucos Ax


Mensajes rápidos

Sólo los usuarios registrados pueden escribir login o crear una cuenta.

Trucos
Ir a trucos programación
Ir a trucos consultoría

AxSearch

AxSearch
Es un motor de búsqueda
específico sobre temas
de Axapta (Dynamics Ax)

Anuncios



Mapa

¿De donde venimos?
Ubicación de visitantes de esta página

Webs Amigas
[axapta-links.com] - The startpage for Axapta
DaxGuy


Fred Shen

Comunidad Ax
Conunidad AX

Anuncios



Trucos Ax: Foros

Trucos Ax :: View topic - Controlando Axapta desde documentos externos
 Forum FAQForum FAQ   SearchSearch   UsergroupsUsergroups   ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Controlando Axapta desde documentos externos

 
Post new topic   Reply to topic    Trucos Ax Forum Index -> Trucos desarrollo
View previous topic :: View next topic  

¿Cómo valoras este truco (1/5)?
5
100%
 100%  [ 4 ]
4
0%
 0%  [ 0 ]
3
0%
 0%  [ 0 ]
2
0%
 0%  [ 0 ]
1
0%
 0%  [ 0 ]
Total Votes : 4

Author Message
Manekaze
Site Admin
Site Admin


Joined: Dec 02, 2005
Posts: 382

PostPosted: Thu Feb 22, 2007 12:33 am    Post subject: Controlando Axapta desde documentos externos Reply with quote

//**********************************************************
// Tipo : Desarrollo
// Titulo : Controlando Axapta desde documentos externos
// Codigo : TD0015
// Keywords : Programacion, Axapta 3.0
// Sub-keywords : SysStartupCmd, axlink, DDE, links, html
//
//**********************************************************

Controlando Axapta desde documentos externos

Introducción

Debe hacer ya unos 7 meses que empecé con este truco y al cabo de una semana lo dejé de lado al topar con un pequeño problema de difícil solución. Pasado todo este tiempo no he podido dedicar ni un minuto de tiempo a continuar este truco, pero ... pensándolo bien ... el “pequeño” problema es realmente muy pequeño, lo suficiente como para obviarlo y sacar a la luz de una vez por todas esta pequeña joya que tenía guardada cual Gollum con su tesoro Wink

Objetivo

Sería genial poder abrir una página web o un informe HTML, PDF o cualquier otro tipo de presentación que permita el uso de hyperlinks y poder establecer vínculos dinámicos entre la información que se está mostrando y la aplicación de Axapta.
Es decir, que pudiéramos estar viendo una estadística de ventas por clientes y al hacer clic sobre el código de cliente en el informe ... Axapta nos abriera el formulario de clientes filtrando para mostrarnos los datos del cliente seleccionado.

Bien, pues ... no es ciencia ficción Razz, es posible y vamos a explicar como.



Primer paso : SysStartupCmd

El primer paso será conseguir asociar un tipo de link para que Windows ejecute nuestra aplicación Axapta pasando unos parámetros donde indicará que instrucciones debe seguir al arrancar.

Para ello vamos a crear nuestro parámetro de arranque de Axapta al que vamos a llamar ‘openlink’. De esta forma sabemos que cada vez que se arranque Axapta con este parámetro significa que debemos actuar Smile

Por tanto, creamos nuestra clase :

x++:

// TrucosAxapta.com
// Clase para implementar el startupcmd=openlink
class SysStartupCmdOpenLink extends SysStartupCmd
{

}


Heredamos de SysStartupCmd, por tanto ... tendremos que tocar el método construct de la clase SysStartupCmd para que cree una instancia de nuestra clase cuando sea necesario :

x++:

static SysStartupCmd construct(str startupCommand)
{
    str     s = strReplace(startupCommand,'_',' ');
    int     p = strScan(s,' ',1,strLen(s));
    str     parm;

    if (p)
    {
        parm    = subStr(s,p,strLen(s));
        parm    = strLRTrim(strRem(parm,'"'));
        s       = strLRTrim(subStr(s,1,p));
    }

    switch (s)
    {
        case 'SetBuildNo':
            return new SysStartupCmdBuildNoSet(s,parm);
        case 'UpdateBuildNo':
            return new SysStartupCmdBuildNoUpdate(s,parm);
        case 'Synchronize':
            return new SysStartupCmdSynchronize(s,parm);
        case 'Batch':
            return new SysStartupCmdBatchRun(s,parm);
        case 'compileAll':
            return new SysStartupCmdCompileAll(s,parm);
        case 'Exit':
            return new SysStartupCmdExit(s,parm);
        case 'AOTImport':
            return new SysStartupCmdAOTImport(s,parm);
        case 'ApplUpgrade':
            return new SysStartupCmdApplUpgrade(s,parm);
        case 'OpenLink': // TrucosAxapta.com
            return new SysStartupCmdOpenLink(startupCommand,parm);// Exclamation
    }

    return new SysStartupCmd(s,parm);
}


Esta bien, ahora tenemos que cuando alguien ejecute Axapta con el parámetro “–startupcmd=openlink” ... el sistema creará nuestra clase y podremos ejecutar lo que deseemos.

Nota : Como veremos más adelante, la forma correcta será :
‘ax32.exe -startupcmd=openlink_axlink:............................’


Bien, pero ... ¿que haremos?


Definiendo un protocolo

Vamos a establecer un protocolo básico mediante el que podremos especificar que acciones debe realizar Axapta cuando pasemos los parámetros “openlink”.

Como es posible que más adelante nos animemos a definir diferentes tipos de links y parámetros varios ... lo mejor será que identifiquemos el tipo de link al comienzo de la ristra de parámetros y luego cada tipo tendrá sus pequeñas variaciones.

La forma básica de los parámetros sería una línea de texto en la que se separarán los parámetros mediante puntos y coma ‘;’. Luego la forma de cada parámetro podría ser .... parámetro = valor, por tanto tenemos algo así como :

TYPE=XXXXX;PARM1=P1;PARM2=P2;.......

En donde XXXXX será el tipo de link que se debe procesar e irá seguido de todos los parámetros necesarios para el correcto funcionamiento de dicho tipo de link.

Por ejemplo, si tenemos un tipo de link que se encarga de abrir formularios... lo lógico será que se le pase un parámetro en el que se indique el nombre del formulario que deseamos abrir :

TYPE=FormRun;FORMRUN=CustTable


Bueno, esta idea ya va teniendo forma, ahora tendremos que hacerla realidad Wink


La clase base : ta_AxLinks

Partiendo de la idea de tener diferentes tipos de links para realizar distintas operaciones, vamos a crear una clase base de la que luego podremos heredar para cada tipo de link e implementando un par de funciones dispondremos de una amplia gama de acciones.

Nuestra classdeclaration será :

x++:

// TrucosAxapta.com
// Clase que interpreta los links pasados a Axapta mediante openlink
class ta_AxLinks
{
 str LinkStr;
 container LinkItems;  // Aqui guardaremos ya separados los diferentes items de la cadena link
}


En la función new recibiremos la cadena de caracteres que almacena los parámetros :
x++:

void new( str _LinkStr )
{
 LinkStr = _LinkStr;

 this.Init();
}

La almacenamos en la variable global LinkStr y llamamos a INIT :
x++:

void Init()
{
 LinkItems = ta_AxLinks::ParseLinkStr( LinkStr );
}


En la función INIT convertimos la cadena de caracteres en un container de forma que cada parámetro será un elemento del container. De eso se ha encargado la función estática ParseLinkStr que viene a continuación :
x++:

// Devuelve un container con los elementos que encuentra en el linkstr diferenciados
// El separador es ;  ejemplo:  Type=FormRun;FormRun=CustTable son 2 elementos.
static container ParseLinkStr( str LinkStr )
{
 return str2con(LinkStr,';');
}


Ahora nuestra clase necesita un método encargado de ejecutar las acciones que ha recibido por parámetro, por tanto, crearemos un método llamado RUN :

x++:

void Run()
{
 if (this.validateLink())
   this.DoLink();
}


Evidentemente no podemos ejecutar nada a la ligera sin realizar antes un par de comprobaciones ... para ello la función validatelink que devolverá true si todo es correcto y de esta forma proseguiremos llamando a la función DoLink que se encargará propiamente de ejecutar nuestro link.
x++:

// Función que validará el texto pasado para comprobar si tenemos los parametros necesarios
boolean validateLink()
{
 return true;
}


x++:

// Función que ejecuta el link
void DoLink()
{

}


De todas formas como hemos dicho que esta solo era la clase base, estas funciones están vacías y son las clases que luego iremos creando y hereden de la clase base las que tendrán que implementar el validatelink y el DoLink.

Nos falta lo más importante ... el método construct que creará una instancia de nuestra clase en función del tipo de link que recibamos como parámetro.

x++:

static ta_AxLinks construct( str LinkStr )
{
 container LinkItems;
 container PrimerItem;
 str Clave,tipo;
 ;

 LinkItems = ta_AxLinks::ParseLinkStr(LinkStr);

 // Por si las flys ...
 if (conlen(LinkItems) == 0)
     throw error('Error al construir el interprete de links, problema en la cadena del link.');


 PrimerItem = ta_AxLinks::ParseLinkStrItem(conpeek(LinkItems,1)); //Exclamation

 if (conlen(PrimerItem) == 0)
     throw error('Problema al averiguar el tipo de link');

 Clave = conpeek(PrimerItem,1);
 Clave = StrlrTrim(Clave); // Mirar la macro ta_StrTrim

 if (Clave != 'TYPE')
     throw error('El primer elemento de la cadena del link debe especificar el tipo !. Ej: TYPE=FORMRUN;.....');


 tipo = conpeek(PrimerItem,2);
 tipo = StrlrTrim(tipo);

 // Ahora a ver que tipo de nodo creamos
 switch(tipo)
 {
  case 'FormRun' : return new ta_AxLinksFormRun(LinkStr); break; //Exclamation

  default :
    throw error(strFmt('Tipo de link %1 desconocido para el sistema',tipo));
 }

}


Si examináis el código comprobaréis dos cosas :

Primera que realizamos una llamada a otra función estática de ta_AxLinks llamada ParseLinkStrItem. Esta se encarga de recibir una cadena de tipo “clave=valor” y devolvernos un container de dos elementos, clave y valor.
x++:

// Devuelve un container con las dos partes separadas del string
// clave = valor
static container ParseLinkStrItem( str LinkStrItem )
{
 return str2con(LinkStrItem,'=');
}


Es decir, que la forma final de la clase base sería como en la imagen :


La segunda cosa es que ya tenemos un tipo de link definido, el tipo ‘FormRun’ y la clase que lo procesa es la ta_AxLinksFormRun. Esto pasa porque lógicamente, antes de explicar todo esto a nadie ... yo ya lo he probado Razz

O sea que vamos directos a implementar el tipo de link ‘FormRun’.


El tipo de link ‘FormRun’ : ta_AxLinksFormRun

Este tipo de link se encargará de abrir un formulario que le indiquen por parámetro. Luego añadiremos soporte para añadir QueryRanges a las tablas del formulario que acabamos de abrir, por supuesto leyendo dichos QueryRanges de los parámetros.
x++:

// TrucosAxapta.com
// Clase que usamos para abrir links de tipo FormRun
class ta_AxLinksFormRun extends ta_AxLinks
{
}


Como podemos apreciar, heredamos de ta_AxLinks.
Ahora debemos implementar aquellos dos métodos de los que hablábamos con anterioridad, validatelink y DoLink.


Empecemos con validatelink :
x++:

boolean validateLink()
{
    boolean ret;
    container FrItem;

    ret = super();

    if (ret)
    {
     // Miramos el segundo elemento de la cadena de links, ya que ...
     // se supone que debe indicarnos el formrun a ejecutar FORMRUN=xxxxx
     FrItem = ta_AxLinks::ParseLinkStrItem(conpeek(LinkItems,2));

     if (StrlrTrim(conpeek(FrItem,1)) != 'FormRun')
       ret = checkfailed('Se esperaba que el segundo elemento de la cadena link indicara el formRun');

    }

    return ret;
}

La forma de cadena que esperamos es :

TYPE=FormRun;FORMRUN=xxxxxxx;........

Por tanto, el primer elemento de la cadena es ‘TYPE=FormRun’ y el segundo elemento de la cadena debe ser ‘FORMRUN=xxxxxxx’. Si no es así ... nos quejamos Smile

Suponiendo que todo sea correcto, tal y como hemos visto cuando analizábamos la clase base, si validatelink devuelve true, es el turno de la función DoLink :
x++:

void DoLink()
{
 formrun formrun;
 args args;
 container iFormRun, iQRange;
 str FormName;
 int i;

 FormDataSource Fds;
 Query Query;
 ;

 // Obtenemos el nombre del formulario, que esta en el segundo item de la cadena links
 // recordamos que es de la forma ... TYPE=FormRun;FormRun=XXXXX;......
 iFormRun = ta_AxLinks::ParseLinkStrItem(conpeek(LinkItems,2));
 FormName = StrlrTrim(conpeek(IFormRun,2));

  // Lo creamos
  args = new args();
  args.name(formName);
  formRun     = classFactory.formRunClass(args);
  if (! formRun)
    throw error(strfmt('Se ha producido un error al intentar crear el form %1',FormName));
  formRun.init();


  Fds = formRun.objectSet();
  Query = Fds.query();

  // Vamos a procesar el resto de parametros a ver si hay algún QueryRange=xxx:xxx:xxx  // Exclamation
  for(i=3; i<=conlen(LinkItems); i++)
  {
   iQRange = ta_AxLinks::ParseLinkStrItem(conpeek(LinkItems,i));
   if (StrlrTrim(conpeek(iQRange,1)) == 'QueryRange')
   {
     this.AddQueryRange(Query,StrlrTrim(conpeek(iQRange,2)));
   }
   // Else ... si no es QueryRange ... es desconocido, o almenos por ahora Smile
   // optamos por no hacer nada.

  }

  formRun.run();
  formRun.wait();

}

Como se puede apreciar, recogemos el parámetro del nombre de formulario y creamos dicho formulario.
Luego viene una parte interesante, es cuando recorremos el resto de parámetros que puedan existir en la cadena de texto.

Tal y como aparece comentado en el código, hemos ampliado el protocolo añadiendo un parámetro ‘QueryRange’ con la forma 'QueryRange=tabla:campo:rango’. Fijaos que en un mismo parámetro aceptamos 3 valores distintos, la tabla a filtrar, el campo y el valor del rango todos ellos separados por dos puntos ‘:’.

Es por eso que realizamos un bucle mientras existan otros parámetros de entrada y si son de tipo ‘QueryRange’ los procesamos en la función AddQueryRange.
x++:

void AddQueryRange( Query Query, str RangeString )
{
 QueryBuildDataSource QBDS;
 QueryBuildRange QBR;
 container Rangeitems;
 TableName TableName;
 FieldName FieldName;
 tableId tableId;
 FieldId FieldId;
 str RangeValue;
 ;

 // Como de costumbre guardamos en un container los diferentes items del string
 // La forma de los rangos sera : QUERYRANGE=Tabla:Campo:ValorRango
 rangeItems = str2con(RangeString,':'); // Exclamation

 TableName = StrlrTrim(conpeek(rangeItems,1));
 TableId = tablename2Id(TableName);
 if (! TableId)
  throw error(strFmt('Imposible crear rango para tabla %1, tabla inexistente',TableName));

 QBDS = Query.dataSourceTable(TableId);
 if (! QBDS)
  throw error(StrFmt('Imposible crear rango para tabla %1, tabla encontrada en el formulario',TableName));

 // Ahora el campo
 FieldName = StrlrTrim(conpeek(rangeItems,2));
 FieldId = fieldname2Id(TableId, FieldName);
 if (! FieldId)
   throw error(StrFmt('Imposible crear rango para tabla %1, campo %2 no existe',TableName, FieldName));


 QBR = QBDS.findRange(FieldId);
 if (! QBR) QBR = QBDS.addRange(FieldId);

 // Como ya nos ha sucedido alguna vez, str2con no se aclara con valores alfanumericos
 // estilo 003 y los trata como numericos ... etc
 // Para evitar problemas el tercer item (el valor del rango) lo cogemos a la brava Smile
 RangeValue = substr(RangeString,
            strscan(RangeString,':',strlen(RangeString),-strLen(RangeString))+1,
            strlen(rangeString)); // Exclamation
 QBR.value(StrlrTrim(RangeValue));
 QBR.status(RangeStatus::Locked);

}


En esta función no hacemos más que separar los 3 valores del parámetro en el container rangeItems (tabla, campo, rango) y luego aplicar dicho rango.
De todas formas, nos encontramos con algo a tener en cuenta. Al recoger el valor del rango, como este puede contener valores de tipo ‘003’ o ‘0200002’, la función str2con no determina con exactitud si se trata de un valor numérico o alfanumérico y al final se decanta por numérico :/
Con lo que no es lo mismo buscar el cliente 001 que el cliente 1. Por eso capturamos el tercer valor del parámetro de una forma más .... rupestre Razz

Perfecto, pero ... ¿todo esto porque lo estábamos haciendo? ¡Ah si! Queríamos arrancar Axapta pasando por parámetro una serie de acciones a realizar y que este las ejecutara.

Pues entonces nos falta una cosa ...

El punto final en la clase SysStartCmdOpenLink

Como íbamos diciendo, solo nos queda crear el método que se ejecutará al arrancar Axapta con el parámetro ‘openlink’. Y eso es el método infoRun de nuestra clase SysStartCmdOpenLink.
x++:

void infoRun()
{
  ta_AxLinks AxLinks;
  str Comando;
  ;

  // Primero quitamos de la cadena link el comienzo que pone el sistema
  Comando = strdel(StartupCmd,1,strlen('openlink_axlink:'));

  // Parseamos la cadena link y la ejecutamos
  AxLinks = ta_AxLinks::construct(Comando);

  AxLinks.Run();
}


Como ya hemos comentado, por convenio el parámetro que disparará nuestra clase realmente será ‘openlink_axlink:’

Bien, pues en esta función únicamente eliminamos la primera parte de la cadena (la que contiene ‘openlink_axlink:’) y ejecutamos el constructor de ta_AxLinks para que nos devuelva una instancia según el tipo de parámetro que esté llegando del sistema, para luego lanzar la ejecución del método Run.


Hasta aquí la primera fase

Pues hasta aquí la primera fase, pero ... y ¿como pruebo yo esto?

La prueba más visceral :

Bien, podemos abrir una instancia de emulador DOS (inicio / ejecutar / “cmd”)

Una vez en la ventana del DOS, entramos un comando como :
Code:
C:\Axapta30\Client\Bin>ax32 -regconfig=xxxxxx
 -startupcmd=openlink_axlink:TYPE=FORMRUN;FORMRUN=CustTable


Evidentemente sin el salto de línea y sustituyendo la ruta correcta al ejecutable ax32.exe y el nombre de la configuración de Axapta que estemos usando:)

La forma más elegante :

Creamos un archivo de texto con la siguiente información :
Code:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\axlink]
@="URL: axlink Protocol"
"URL Protocol"=""
"EditFlags"=dword:00000002
"BrowserFlags"=dword:00000008

[HKEY_CLASSES_ROOT\axlink\DefaultIcon]
@="C:\\Axapta30\\Bin\\Ax32.exe"

[HKEY_CLASSES_ROOT\axlink\shell]
@="openlink"

[HKEY_CLASSES_ROOT\axlink\shell\openlink]

[HKEY_CLASSES_ROOT\axlink\shell\openlink\command]
@="C:\\Axapta30\\Bin\\Ax32.exe  -regconfig=xxxx  -startupcmd=openlink_%1"

Tenéis que sustituir en “hkey_classes_root\axlink\DefaultIcon” y en “hkey_classes_root\axlink\shell\openlink\command“ las rutas correctas a vuestra aplicación Axapta y por el nombre de configuración adecuado. CUIDADO con las dobles barras y sobretodo, si tenéis que introducir una ruta larga que va entre comillas dobles, debéis introducir una barra antes de las comillas.
Por ejemplo :

@=”\”C:\\Esta ruta es muy larga y con espacios\\ax32.exe\” –regconfig=........

Luego renombramos el archivo de texto a una extensión “.reg” y le damos doble clic para que lo incorpore al sistema.

Ahora si en una sesión DOS introducimos :
Code:

Start axlink:TYPE=FORMRUN;FORMRUN=CustTable


Nos arrancará Axapta y abrirá el formulario de clientes.

O mejor aún, podemos crear un archivo HTML (testaxlink.htm) con algo como :
HTML:

<a href=axlink:TYPE=FORMRUN;FORMRUN=CustTable>Abrir Clientes</a>


Si lo abrimos y hacemos clic sobre “Abrir Clientes” …. ¡Tachaaaaan!


Esto me gusta, pero ... yo no quiero que abra una instancia nueva de Axapta para cada link que ejecuto.

Segunda Fase : Servidor DDE

Vamos a evitar que se lance una segunda instancia de Axapta para ejecutar un nuevo link creando un servidor DDE que atienda las peticiones del sistema de archivos de Windows. Dicho así parece algo solo apto para doctorados en física nuclear pero ahora veremos que es muy sencillo Smile

Vamos a crear una clase que heredará de DDETopic :
x++:

// TrucosAxapta.com
// Este sera nuestro servidor DDE para poder abrir links de Axapta
// sin necesidad de arrancar otra instancia de Axapta cada vez
class ta_DDEAxLinks extends DDETopic
{
}

Hasta aquí fácil, ¿no? Razz

Ahora creamos un método llamado execute (bueno, realmente no lo creamos, lo rescribimos ya que es un método de la clase padre)

x++:

public void execute(str _Command)
{
 ta_AxLinks AxLinks;
 str comando;
;

   switch (this.getCommand(_Command,1))
    {
        case 'openlink':
                // Como siempre, quitamos el prefijo del comando
                comando = strdel(_Command,1,strlen('openlink axlink:'));
                // Ahora lanzamos nuestra clase de links
                AxLinks = ta_AxLinks::construct(comando);
                AxLinks.Run();
             break;

    }

    this.status( DDEstatus::OK );
}


Aquí simplemente obtenemos el primer fragmento de texto entre espacios del comando DDE que nos llega y si es ‘openlink’ ¡es el nuestro!
Eliminamos la primera parte (que en el caso DDE será ‘openlink axlink:’) y lanzamos nuestra ya famosa clase Axlinks para que procese el link.

En este fragmento se usa una función para obtener el primer fragmento de texto entre espacios, es esta :
x++:

str getCommand(str line, int idx)
{
    int no = 1;
    int p1 = 1;
    int p2;

    p2 = strFind(line,' ',p1,strLen(line));

    while (no < idx)
    {
        no++;
        p1 = p2+1;
        p2 = strFind(line,' ',p1,strLen(line));

        if (!p2)
            p2 = strLen(line)+1;
    }

    return subStr(line,p1,p2-p1);
}

Lo se, se podía hacer de otra forma, pero ... lo hice hace tiempo y funciona ... y ya se sabe, si funciona .... Smile

La forma final de nuestra clase servidor ta_DDEAxLinks es como en la imagen :


Ahora tenemos que hacer que Axapta arranque nuestro servidor DDE cuando se inicie.
Por tanto ...

Arrancando el servidor DDE

Nos vamos a la clase Info y le añadimos un par de líneas en el classdeclaration :

x++:

// TrucosAxapta.com : AxLinks
    ta_DDEAxLinks _ddeSystem;
    DdeServer  _ddeServer;


Estas líneas podemos añadirlas al final del classdeclaration.

Ahora creamos un nuevo método (seguimos en la clase info) :
x++:

// TrucosAxapta.com
// Arrancamos el servidor DDE para dar servicio a peticiones de links
void ArrancaServidorDDE_axLinks()
{
 ddeClient _ddeClient;
 boolean primerintento = true;

  void IniciaDDE()
  {
   _ddeServer = new DdeServer("AxLinks"); // Exclamation
   _ddeSystem = new ta_DDEAxLinks(_ddeServer,'System');// Exclamation
  }
 ;

  try
    {
     // Hacemos una peticion DDE por si ya está en marcha
      _ddeClient = new DDEClient("AxLinks","System");
    }
    catch(Exception::Internal)
    {
     if (primerIntento)
      {
       // Si llega aquí es que no ha encontrado el servidor DDE, por tanto intentaremos arrancarlo
       primerintento = false;
       IniciaDDE(); // Exclamation
       // A partir de ahora, si vuelve a petar es que algo no ha ido bien
       retry;
      }
    }
   _ddeClient = null;
}

Aquí es importante señalar que lo primero que hacemos es mirar si podemos conectarnos vía DDE con un servidor DDE que sea “AxLinks/System” (o sea, el nuestro) ya que es posible que ya esté en marcha en alguna otra instancia de Axapta y en ese caso no podemos crear otro servidor, daría error.

Es decir, si intentamos conectarnos y falla, tenemos vía libre para crear nuestro servidor DDE Smile

Ahora solo nos queda añadir la llamada a este método cuando inicie Axapta, es decir, vamos a tocar el método startuppost (también de la clase info) :
x++:

/*
No SYS code must exist in this method
*/

void startupPost()
{
 this.ArrancaServidorDDE_axLinks();
}


Bien, ahora la próxima vez que arranquemos Axapta debería arrancar nuestro servidor DDE. Solo nos queda indicarle al sistema de archivos de Windows que nuestro Axapta dispone de interacción DDE.

Volvemos a editar el fichero “.reg” que habíamos creado antes y añadimos estas líneas al final :
Code:

[HKEY_CLASSES_ROOT\axlink\shell\openlink\ddeexec]
@="openlink %1"

[HKEY_CLASSES_ROOT\axlink\shell\openlink\ddeexec\Application]
@="AxLinks"

[HKEY_CLASSES_ROOT\axlink\shell\openlink\ddeexec\Topic]
@="System"


Guardamos el archivo y le damos doble clic de nuevo (contestamos si a la pregunta).

Si ahora reiniciamos la sesión de Axapta y volvemos a abrir el archivo HTML que hemos creado antes, al darle clic sobre “Abrir Clientes” debería hacerlo sin abrir otra instancia de Axapta.

Es más, si está todo correcto, podéis probar un link como ...
HTML:
<a href=axlink:TYPE=FORMRUN;FORMRUN=CustTable;QueryRange=CustTable:Name:M*>Clientes M...</a>


Con el, Axapta os debería mostrar los clientes que empiezan por la letra ‘M’. Wink

Y ¿cual es ese pequeño problema del que hablábamos al principio?

Pues que si, ahora que hemos ajustado el sistema para que funcione a través de DDE, si pulsamos un link cuando Axapta no está en ejecución … Windows ejecuta Axapta como hacíamos al principio y le intenta enviar los comandos DDE. Como nuestro servidor DDE no arranca hasta que hacemos login en Axapta … el sistema da un error como si no pudiera abrir el link, pero una vez aceptamos el mensaje de error y hacemos login … todo funciona ok Razz


Supongo que ya le veis bastantes aplicaciones posibles .... Informes estadísticos interactivos, manuales de circuitos lógicos del programa que te llevan a las pantallas que debes parametrizar ... etc.

Saludos,

Mkz.

NOTA: He añadido el proyecto con las clases de este truco en la zona de downloads. Este es el link directo.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Trucos Ax Forum Index -> Trucos desarrollo All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2001, 2005 phpBB Group
Forums ©

 
Trucos Axapta 
Comunidad aprobada por :
Microsoft corp. MVP LogoVisit community


Axapta y Dynamics Ax son marcas registradas de Microsoft corporation.
Todos los logos y marcas son propiedad de sus respectivos propietarios.
Excepto trucosAx.com que este si que es mio :-). (c) 2005 by Manel Querol (Mkz)
TrucosAx.com no pertenece ni está asociada a Microsoft corporation.
Los fragmentos de código y proyectos importables que aquí se muestren están realizados sobre bancos de pruebas. No nos hacemos responsables de cualquier daño o pérdida de datos que se pudiera originar del hecho de instalar alguno de estos ejemplos en un sistema productivo. Es responsabilidad del usuario ser consciente del impacto que puede ocasionar en sus aplicaciones el uso del código que de aquí extraiga.