Módulo de control de presencia

septiembre 2, 2010 en Código, Delphi, Entrada Diario, Taller práctico

Vamos a ponernos un poco las pilas ahora que ya han terminado las vacaciones. ¡Volvemos a lo nuestro!, a este breve tiempo que compartimos y que nos mantiene en contacto y vivos. Y como hacía ya bastante tiempo que no subía unas lineas de codigo, hoy casualmente, rebuscando en las carpetas perdidas, he encontrado un pequeño ejemplo que tenía preparado para el blog y que accidentalmente perdí.

Lo dicho. Estas lineas domían el sueño de los justos. :-) Son ni mas ni menos que del 2007, por lo que tienen la friolera de unos dos años.  Creo recordar que se prepararon en el transcurso de ese año, mientras colaboraba con Jose Luis Freire en el Boletín de Delphi, y casí con seguridad hubieran formado parte de los siguientes boletines, ya que finalmente se suspedió su publicación al no existir suficientes medios humanos para mantenerlo. Bueno. No nos engañemos. :-( Cualquier iniciativa gratuita corre ese peligro. Todos vamos sobrecargados de trabajo y al no ser una actividad que tenga una recompensa económica pues es dificil encontrar gente que colabore de forma regular. Pero bueno… se pudieron publicar bastantes numeros gracias a la constancia y a la entrega de Jose Luis, de ir detrás de unos y de otros, recordándonos que faltaban pocos días para la publicación.  :-)

¡Qué tiempos aquellos!  -parezco un viejete contando batallitas-

jejejeje

Sigamos…

En este caso vamos a hacer algo distinto. Normalmente preparo unos artículos introductorios para explicar el código y al final se acompaña, en el último de los artículos que forman parte de la serie. Como no tengo preparado nada, os voy a incluir el fichero con el codigo fuente del ejemplo y prepararé las entradas si es que me enviais alguna pregunta. :-D

Es un código muy sencillito. Leed el archivo leeme contenido en el zip que os cuenta un poco la película.

¿vale?

Imagen de la aplicación

Codigo

Nada mas por hoy. Espero que tengais un buen día y que seais, como se suele decir, muy felices.

Simplifica tu código Delphi…

marzo 30, 2010 en Código, Consejo, Delphi, Enlace interesante, Entrada Diario

Simplifica tu código, piensa en clases, abstrae y racionaliza, usa el sentido común, etc… son algunos de los lemas que hemos podido compartir durante muchos de los artículos anteriores, casi desde siempre, con mas o menos acierto. Valga la redundancia, casi diría que en realidad, es una preocupación cuasi universal que nos corroe, a medida que avanzamos y aprendemos y nos formamos. También de alguna forma, exteriorizamos esos pensamientos en muchos de los post que acabamos publicando.

Nuestro punto de parada hoy, es nuevamente el blog de Stefaan Lessage, y la parada es para compartir cuatro artículos que ha escrito durante el mes de marzo y que pienso que forman parte de esa idea general que siempre hemos intentado plasmar: pensar en clases y abstraer. Pienso que la lectura de las cuatro entradas de Stefaan es muy aconsejable  y si bien, puede resultarnos mas o menos incomodo que esté escrita en otro idioma (ese punto ya depende de cada uno), existe el suficiente código para que pueda entenderse el trasfondo y la enseñanza que aporta. Sobretodo, os la aconsejo si os estáis iniciando en el entorno y buscais patrones de razonamiento que os sirvan de referencia en vuestros desarrollos.

La serie se titula, como no, “Simplify your Delphi Code using some basic rules, OO techniques and some refactoring“. Mas o menos, “Simplifica tu codigo Delphi usando algunas reglas básicas, tecnicas orientadas a objetos y refactorización”.

Parte 1

En la primera parte de la serie, Stefaan Lessage presenta el problema o la cuestión. Es un capítulo introductorio y breve, en el que inicia su reflexión imaginando una necesidad que suele darse con frecuencia y que consiste en retomar código escrito años atrás para su revisión. Puede que nos toque enfrentarnos a código difícil de leer o entender y que requiera un nuevo enfoque para facilitar en un futuro su mantenimiento.

Al hilo de mostrarnos las ventajas, Stefaan se centra con un ejemplo que puede ser representativo de esa ganancia y que va a servir de eje sobre el que van a girar el resto de los artículos de la serie. Personalmente, creo que ha elegido un ejemplo que recoge una necesidad básica en cualquier aplicación: guardar y cargar datos de personalización. Y como imagináis, casi siempre van a estar implicados o bien archivos ini o el registro, ficheros xml, etc.

Parte 2

La segunda parte, una vez planteada esa introducción, se nos muestra algo de código. Código que podría representar a esas lineas que deberíamos revisar, bajo un nuevo enfoque al hilo de la programación orientada a objetos. Siguiendo con su ejemplo, muestra varios procedimientos que permitirían desde los eventos de creación y destrucción del formulario, cargar y guardar los valores de personalización. Lógicamente, junto con estas lineas de código se nos presentan las desventajas de estas decisiones y los problemas que pueden suponer en distintos escenarios.

Parte 3

Aquí ya entra Estefaan Lessage en el primer acercamiento a resolver el problema desde la perspectiva OOP. Para ello, crea una clase base que permita tratar distintos tipos de datos de forma unificada. Así pues, la clase TdvSetting realmente no hace nada mas que preparar el camino. De hecho, los metodos Get/Set se limitan unicamente a generar una excepción, puesto que ella no sabe realmente como debe responder a dichos métodos. Será sus descendientes los que sobrescriban los métodos adecuados al contexto de la aplicación, creando código especifico.

Parte 4

Y ya la parte cuarta, finaliza la serie. En este artículo se define la clase TdvStringSetting a modo de ejemplo, descendiente de TdvSetting, y se nos proponen nuevas clases descendientes (TdvInteggerSetting, TdvBooleanSetting, etc.) y una clase adicional TdvSettings, descendiente de TObjectList, al modo de contenedor. Fijaros en un detalle importante, esta lista de objetos, recibe como parámetros en su funciones y procedimientos la clase base, jugando de esa forma Steffaan Lessage con la herencia y el polimorfismo (si no recuerdo mal a este tipo de polimorfismo se denomina como Polimorfismo de subtipado o de inclusión).

Creo que vale la pena que perdáis unos minutos y reviséis el código ya que este tipo de razonamientos pueden aportar un valor añadido al código que escribimos. No es tan importante la cantidad de lineas que uno puede abarcar en una jornada frente a que éstas garanticen que pueda ser revisadas meses después al aire de un nuevo contexto o en respuesta de nuevos requerimientos. Calidad o cantidad… Siempre debemos “aspirar” a la calidad.

Comparto plenamente lo escrito por Stefaan en la introducción

Think before you start writing your first line of code…

Una verdad como un templo.

El mundo en un conjunto…

junio 17, 2009 en Artículos, ¿Sabías que...?, ¿Sabías que...? [Delphi], Delphi, Entrada Diario, Sintaxis

Los conjuntos son de esos tipos que muchas veces nos pasan desapercibidos. :-)  Se podría incluso decir que pasan sin pena ni gloria. No tienen la importancia de una clase o de un registro. Y se situan dentro del ranking de popularidad entre los Enumerados y los Subrango,  que es casi lo mas bajo que uno puede caer :-)  Al menos siempre tuve esa impresión. :-D

Hablando en serio, ya que hemos acabado las cinco entradas donde todo giraba en torno a pensar en clases, parece que se hacía apropiado comentar algo de los tipos personalizados. Y digo lo de apropiado, porque los tipos personalizados no son imprescindibles puesto que al final los identificadores son representaciones de un valor ordinal. Pero pensar en términos de clases incluye un premisa que hasta ahora habia quedado un poco en el aire, y es simplemente que el código debe ser también lo mas claro posible. Y en eso, sin duda, puede ayudarnos la existencia de los tipos personalizados, tanto enumeraciones, subrangos, conjuntos o registros, (éstos últimos, con un peso mucho mayor lógicamente).

Realmente, ese pensamiento, que mi código sea claro, sencillo, limpio, nos debería acompañar siempre. Nuestra “supervivencia” depende un poco de eso. Nuestros razonamientos deben seguir caminos similares. No supone un rasgo de inteligencia enrevesar nuestro código adrede y hacerlo ilegible incluso para nosotros mismos, (a poco que pasen algunos meses desde que lo escribimos,  puede convertirse involuntariamente en una realidad, y en un momento posterior en una pequeña pesadilla). Y eso, a menudo, pasa mas de lo que quisiéramos. :-)

Y entre algunos de esos “problemas”, de refilón puede haberse colado no haber hecho uso de los tipos personalizados: ¿Métodos que recibían parámetros de tipo Integer y que escondían un tipo personalizado?. Puede ser uno de los ejemplos que me vienen a la mente:

-¡Ahí la cagaste, amigo!-, pensaba para mí…- ¡podías haber utilizado un tipo enumerado!-.

Y así podríamos enumerar muchos mas.  Es cierto que es muy subjetivo pero creo que todos estaremos de acuerdo en que resulta mas legible algo como

function ElUsuarioEligio: TDia;

siendo TDia =(dLunes, dMartes, dMiercoles, dJueves, dViernes))

que

function ElUsuarioEligio: Integer;

Pero empecemos por el principio, y es entender que es una Enumeración: Una enumeración es un tipo ordinal, definido como un conjunto ordenado de valores en el que cada valor tiene un unico antecesor y un unico sucesor, a excepción del primer valor o del último, dado que el primer valor no tiene antecesor y el ultimo carece de sucesor, por definición.

¿No es demasiado intuitiva, no?

Pero es eso: Un conjunto de valores ordenado. No hay mucho mas.

Si tuviéramos que identificar los dias de las semana para poder referirnos a ellos en nuestro código, siempre podríamos apoyarnos en las constantes. Tal que así:

   const
     klunes = 1;
     kmartes = 2;
     kmiercoles = 3;
     kjueves = 4;
     kviernes = 5;
     ksabado=6;
     kdomingo=7

Lo cual puede ser claro, pero no nos seria demasiado útil en algunos casos, ya que no tengo un indice ínterno que me permita recorrer todos los valores y hacer algo para cada día, salvo que me pueda crear también “algo” que haga la misma función, mediante una variable entera y un bucle que recorra los valores desde 1 hasta 7.

Por lo que, resulta natural que el lenguaje me ofrezca la forma de agrupar esos valores en un tipo (enumerado) de forma que dicho tipo pueda representar a cualquiera de ellos. El tipo TDiaSemana se representaría:

TDiaSemana = (seLunes, seMartes, seMiercoles, seJueves, seViernes, seSabado, seDomingo)

Desde ese momento, contando con los tipos enumerados, ya resulta mas fácil en un procedimiento cualquiera hacer uso de un parámetro de tipo TDiaSemana en lugar del mismo de tipo Integer. Ofrece mas claridad al revisar el código y menor posibilidad de errar al invocarlo, evaluando un valor que no se establece en la precondición.

Por defecto, la enumeración se iniciaría en el valor 0 (cero) pero eso tampoco es obligatorio, como podemos observar en el ejemplo que he añadido. Podríamos iniciar la semana en el valor 1, por eso de que es el primer día… :-)

TDiaSemana = (seLunes = 1, seMartes = 2, seMiercoles = 3, seJueves =4 , seViernes = 5, seSabado = 6, seDomingo = 7)

Y ya casi para acabar con los tipos enumerados, al igual que  sucede con los tipos ordinales, disponen de un índice que permite ser recorrido, y existen a nuestra disposición algunas funciones predefinidas que podemos usar para manipularlos dentro de nuestro código:

  • Ord (Valor de una expresión ordinal).
  • Pred (Valor antecesor del citado por la expresión)
  • Succ (Valor sucesor del citado por la expresión)
  • High (Valor máximo que puede tomar el tipo)
  • Low ( Valor mínimo que puede tomar el tipo)

Eso con respecto a los tipos enumerados.

Otro tipo personalizado, es el Subrango, que representa un subconjunto de un tipo ordinal (de cualquiera de ellos). Y se construye mediante un valor minimo y un mayor maximo separados por dos puntos, siendo ese mínimo y ese máximo expresiones de un tipo de valor ordinal. Admite un máximo de 256 valores

TMisFavoritos = seViernes...seDomingo;

Pero bueno… también podemos hacer:

TMiRangoPorcentajes = 0..199;
TAlfabeto = 'a'..'z';

Lo mas habitual es que definamos variables de tipo y, como comentábamos lineas mas arriba, hagamos uso de las funciones predefinidas para movernos por el rango de valores que especifica el tipo.

Y finalmente, llegamos a los conjuntos, que era lo que me movía a iniciar la entrada. Puede ser que fuera porque, pienso yo, al igual que muchos compañeros, si bien usamos habitualmente los tipos enumerados y los subrangos, no lo hacemos con las misma frecuencia que los conjuntos y siempre quedan como mas relegados. Y eso a pesar que los vemos presentes en propiedades de clases tan habituales como la clase TFont, en lla propiedad Style[] . Por citar una de las que me parecen mas intuitivas para citar.

¿Que es un conjunto?

Pues un conjunto es una colección de valores ordinales, con un máximo de 256 elementos (1 byte). Se utliza la sintaxis:

Type
TMiConjunto = Set of TMiTipoOrdinal;

Olvidaba comentar que al admitir el valor nulo como posible valor de dominio, es decir, el conjunto vacío, no tenemos disponibles 256 valores sino 255 distintos de este valor nulo.

En el caso que comentabamos, el de los días de la semana, podríamos establecer un conjunto que representara los días laborables de semana y utilizarlo para indicar que días había trabajado de la misma.

type
   TDiasTrabajo = seLunes..seViernes;
   TMiJornada = Set of TDiasTrabajo;
var
   MiSemana: TMiJornada;
begin
//la variable no contiene ningun elemento
   MiSemana:= [];

Es decir. Esa semana no hemos dado palo al algua… :-)

Estas son las funciones que tenemos disponibles y que podeis ampliar información consultando en la ayuda del entorno: (las pongo en función del ejemplo para que se van mas claras)

Si incluimos un elemento, podemos valernos del operador (+) o de la función Include

MiSalida:= MiSemana + [seLunes];  o bien
Include(MiSemana, seLunes)

Y si deseo excluir un elemento del conjunto, podemos hacer uso del operador (-) o de la función Exclude

MiSalida:= MiSemana - [seLunes];  o bien
Excude(MiSemana, seLunes)

En el caso de los conjuntos, tenemos formas de saber si un elemento pertenece a un conjunto. ¿como?

Muy fácil, haciendo uso del operador (in), que nos permite evaluar en una expresion booleana la pertenencia del elemento.

if (seLunes in MiSemana) then

También, desde Delphi 2005 es posible recorrer los valores que pertenecen a un conjunto haciendo uso de for (ValorOrdinal) in (Conjunto). Lineas mas abajo lo podréis ver.

Bueno… Lo mejor, es que lo veamos en un ejemplo muy sencillo. Vamos a usar una ventana modal para preguntar al usuario que meses va a elegir, suponiendo que este proceso sea parte de otro mas general del que valernos para obtener una consulta (ej: estadísticas de ventas en los meses deseados, etc.). Al cerrar la ventana de dialogo, nos copiará la selección deseada en el listbox, permitiendo que al volver a ser llamada se restablezca la selección anterior.

He intentado que el ejemplo contenga las expresiones mas generales. Esta es la imagen de la ventana modal, que va a ser llamada para que el usuario pueda elegir que meses desea incluirl modulo principal:

Y este es el código que se ejecutará:

unit UMeses;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons;

type
  TMeses = (meEnero = 1,
            meFebrero = 2,
            meMarzo = 3,
            meAbril = 4,
            meMayo = 5,
            meJunio = 6,
            meJulio = 7,
            meAgosto = 8,
            meSeptiembre = 9,
            meOctubre = 10,
            meNoviembre = 11,
            meDiciembre = 12);

  TAnual = Set of TMeses;

  TDialogoMeses = class(TForm)
    grbMeses: TGroupBox;
    chbEnero: TCheckBox;
    chbFebrero: TCheckBox;
    chbMarzo: TCheckBox;
    chbJulio: TCheckBox;
    chbMayo: TCheckBox;
    chbJunio: TCheckBox;
    chbAgosto: TCheckBox;
    chbSeptiembre: TCheckBox;
    chbOctubre: TCheckBox;
    chbAbril: TCheckBox;
    chbNoviembre: TCheckBox;
    chbDiciembre: TCheckBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FSeleccion: TAnual;
    procedure SetSeleccion(const Value: TAnual);
    { Private declarations }
  public
    { Public declarations }
    property Seleccion: TAnual read FSeleccion write SetSeleccion;
  end;

var
  DialogoMeses: TDialogoMeses;

implementation

{$R *.dfm}

procedure TDialogoMeses.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: Integer;
begin
  for i := 0 to grbMeses.ControlCount - 1 do
     if TCheckBox(grbMeses.Controls[i]).Checked then
       Include(FSeleccion, TMeses(grbMeses.Controls[i].Tag));
end;

procedure TDialogoMeses.FormCreate(Sender: TObject);
begin
  Seleccion:= [];
end;

procedure TDialogoMeses.SetSeleccion(const Value: TAnual);
var
  i: Integer;
begin
  FSeleccion := Value;
  for i := 0 to grbMeses.ControlCount - 1 do
     if TMeses(grbMeses.Controls[i].Tag) in Seleccion then
                 TCheckBox(grbMeses.Controls[i]).Checked:= True;
end;

end.

Como podéis ver es muy sencillo y claro. Creamos el conjunto con un valor vacio y tan solo si es asignado a través de la propiedad Selección, reponemos la elección previa que hizo el usuario.

Y esta es la imagen del formulario principal, donde llamaremos a la ventana modal y recogeremos los valores elegidos:

unit UMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TMain = class(TForm)
    btnsiguiente: TButton;
    lbxSeleccion: TListBox;
    labPaso1: TLabel;
    Label1: TLabel;
    btnCerrar: TButton;
    procedure btnsiguienteClick(Sender: TObject);
    procedure btnCerrarClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Main: TMain;

implementation

uses UMeses;

{$R *.dfm}

procedure TMain.btnCerrarClick(Sender: TObject);
begin
  Close;
end;

procedure TMain.btnsiguienteClick(Sender: TObject);
const
  MESES: array [TMeses] of String = ('Enero',
                                     'Febrero',
                                     'Marzo',
                                     'Abril',
                                     'Mayo',
                                     'Junio',
                                     'Julio',
                                     'Agosto',
                                     'Septiembre',
                                     'Octubre',
                                     'Noviembre',
                                     'Diciembre');
var
  i: Integer;
  fMes: TMeses;
begin
  DialogoMeses:= TDialogoMeses.Create(nil);
  try
    //vamos a restaurar la seleccion previa en la ventana modal
    //y solo lo hacemos si existen items de una seleccion anterior
    if lbxSeleccion.Items.Count > 0 then
      for i := 0 to lbxSeleccion.Items.Count - 1 do
         DialogoMeses.Seleccion:= DialogoMeses.Seleccion +
                               [TMeses(Integer(lbxSeleccion.Items.Objects[i]))];
    //lanzamos la ventana modal
    if DialogoMeses.ShowModal = mrOk then begin
       lbxSeleccion.Clear;
       for fMes in DialogoMeses.Seleccion do begin
            lbxSeleccion.AddItem(MESES[fMes], TObject(fMes));
       end;
    end;
  finally
    FreeAndNil(DialogoMeses);
  end;
end;

end.

Descargar

Tambien hubiera podido ser correcto, en el interior de un bucle mas general que recorriera los valores que puede tomar la variable fMes:

If (fMes in DialogoMeses.Seleccion) then begin
            lbxSeleccion.AddItem(MESES[fMes], TObject(fMes));

Hay un detalle que os puede ser de interes y es el uso de la constante MESES, que permite recuperar el valor literal. Es muy habitual hacer uso de un array de dimensión el Tipo ordinal (en este caso TMeses).

 const
   MESES: array [TMeses] of String

Esta es una imagen del ejemplo en tiempo de ejecución.

Nada mas por hoy. La idea era compartir con vosotros la necesidad de que el código sea lo mas intuitivo posible. Y los tipos personalizados, tanto las enumeraciones, los subrangos o los conjuntos, enriquecen nuestro código aportando información adicional, semántica, que permite lo que hemos escrito sea mas fácil de comprender, tanto para nosotros como para otras personas que lo compartan con nosotros. Ese es, un poco el resumen.

Un enfoque modular para nuestra aplicación. (FUENTES)

octubre 19, 2007 en Delphi, Entrada Diario, framework, Taller práctico

Durante estos últimos días, he recibido algunos correos de compañeros solicitandome el codigo fuente del articulo escrito en el Boletin de Delphi, y que llevaba como título “UN ENFOQUE MODULAR PARA NUESTRA APLICACION”. En principio la intención era adjuntarlo en la ultima descarga del boletín, pero al final mi amigo Jose Luis Freire olvidó incluirlo.

Así que lo mas fácil, hasta que contacte con él, es añadir a esta entrada un link para su descarga.

El artículo se reparte en los siguientes boletínes:

Desgargar fuentes artículo

Espero que os guste y pueda ayudar a alguien.

Seleccionar una carpeta

agosto 28, 2007 en Artículos, Entrada Diario, Velneo

En una de las entradas anteriores, justamente en los comentarios de “Primeros pasos tras el curso…”, AROJAS comentaba:

Buenos Días, dime tendrias alguna DLL hecha que sirva para llamar el cuadro de diálogo Buscar y Seleccionar Carpeta de Windows, como para que funcione en VELNEO, desde ya gracias

Veamos a ver que podemos hacer… ummmm :-)

En este caso concreto, no tiene mucho sentido desde la perspectiva de Delphi tener esta función dentro de una librería. Sin embargo, desde la perspectiva de Velneo la cosa cambia y posiblemente sí que nos interese incluirla, ya que al necesitar inicializar estructuras para invocar la función, puesto que recibe como parámetro un puntero a la estructura TBrowseInfo, no lo vamos a poder hacer desde Velneo. Solución: Abrimos nuestro editor de Delphi (otros compañeros sustituiran este entorno por otro cualquiera que nos permita generar la libreria) y no queda más que escribir unas lineas de código.

Vamos a necesitar la función del API de Windows, SHBrowseForFolder que se encuentra ya declarada en el modulo ShlObj.pas de Delphi, lo que nos ahorra tiempo y problemas. De no ser así, deberiamos declararla y traducir los tipos originales de C que necesita la función a los de nuestro entorno.

Básicamente, lo que hacemos es inicializar la estructura TBrowseInfo con los valores adecuados y una vez hecho esto, entregamos un puntero al registro como parámetro de entrada de la función ShBrowseForFolder. La respuesta de la función es similar a otras funciones de la Shell del mismo ámbito. Nos devuelve un puntero a una estructura (PItemIdList). Finalmente, liberamos mediante la interfaz IMalloc la asignación de memoria que hizo el sistema en su respuesta, según reza la documentación técnica.

Descargar el codigo fuente y el mapa para testear la función

Lo que ya dejo un poco a gusto de vosotros es modificar los parámetros de entrada de la función MostrarCuadroCarpetas para que muestre determinadas opciones, o por el contrario no las muestre. Podriamos querer que se visualizase un botón para permitirnos crear una nueva carpeta, mostrar solo los directorios del sistema, mostrar solo los equipos o solo las impresoras. Existen bastantes posibilidades y parece razonable que no nos sean necesarias siempre por lo que he preferido no complicar innecesariamente este pequeño ejemplo. En todo caso, una gran parte de ellas van a depender del valor que tome el campo ulFlags y convendría conocer los distintos valores de las constantes que pueden ser usadas.


BIF_RETURNONLYFSDIRS = $0001;
BIF_DONTGOBELOWDOMAIN = $0002;
BIF_STATUSTEXT = $0004;
BIF_RETURNFSANCESTORS = $0008;
BIF_EDITBOX = $0010;
BIF_VALIDATE = $0020;
BIF_NEWDIALOGSTYLE = $0040;
BIF_USENEWUI = BIF_NEWDIALOGSTYLE or BIF_EDITBOX;
BIF_BROWSEINCLUDEURLS = $0080;
BIF_UAHINT = $100;
BIF_NONEWFOLDERBUTTON = $200;
BIF_NOTRANSLATETARGETS = $400;
BIF_BROWSEFORCOMPUTER = $1000;
BIF_BROWSEFORPRINTER = $2000;
BIF_BROWSEINCLUDEFILES = $4000;
BIF_SHAREABLE = $8000;

Si quisieramos por ejemplo que además de las carpetas nos mostrara los archivos para poder seleccionar cualquiera de ellos, bastaría hacer la asignación:
ulFlags:= BIF_STATUSTEXT or BIF_BROWSEINCLUDEFILES;
Así de fácil. ;-)

function MostrarCuadroCarpetas(ACaption: PChar; ACallBack: Boolean): PChar; stdcall;
var
    fres: Array[0..MAX_PATH] of Char;
    fName: Array[0..MAX_PATH] of Char;
    IDList: PItemIDList;
    BrowseInfo: TBrowseInfo;
    Malloc: IMalloc;
begin
    //rellenamos la estructura con los datos deseados
    with BrowseInfo do begin
        hwndOwner:= 0; //en nuestro caso no existe handle y
         //se mostrara de forma no modal
         pidlRoot:= nil; //no existe origen predefinido
         pszDisplayName:= fName; //Nombre de la carpeta
         lpszTitle:= ACaption; //Titulo de la ventana
         //se podria parametrizar desde Velneo para mostrar opciones distintas
         //en este caso solo hemos seleccionado la posibilidad de mostrar el
         //texto a traves del mensaje en la función callback
         {
         Ver la unidad ShlObj para obtener una lista de todas las constantes
             disponibles.
         }

        ulFlags:= BIF_STATUSTEXT;
         //si no queremos ejecutar el callback para modificar la ventana de exploracion
         if ACallBack then
             lpfn:= @BrowseCallBackProc
         else lpfn:= nil;
         lParam:= 0;
    end;
     //Invocamos la funcion del API SHBrowseForFolder pasando como parámetro la
     //estructura que hemos inicializado
     IDList:= SHBrowseForFolder(BrowseInfo);
     //Obtenemos la ruta
     ShGetPathFromIDList(IDList, @fres);
     //segun la documentación de windows sería necesario liberar la memoria
     //que windows reserva para la estructura IDList. Nosotros seguimos la
     //mecanica aconsejada y hacemos uso de la interfaz IMalloc para ello.
     //El metodo Free libera la memoria asignada
     //Previamente comprobamos que podemos obtener un handle a la interfaz
     //para hacer uso de las funciones.

     if Succeeded(SHGetMalloc(Malloc)) then begin
         Malloc.Free(IDList);
     end;
     Result:= fres;
end;

{$R *.res}

exports
     BrowseCallBackProc,
     MostrarCuadroCarpetas;

begin
end
.