Todo lo que quisiste saber sobre las DLL y no te atreviste a preguntar (II)

Como podéis ver no se me ha olvidado el tema, simplemente es que he estado demasiado gandulete como para escribir sobre esto, pero una pregunta en el foro web de C++ me ha hecho que me ponga a ello. En teoría esta debería ser la tercera parte en lugar de la segunda, aunque la verdad no es que realmente tenga mucha importancia. Por lo tanto, esta entrada que estás leyendo sigue a esta otra.

Aquí voy a contar los tres modos que hay de usar una DLL desde C y C++, aunque solo voy a explicar dos de ellos porque el tercero digamos que viene de las primeras versiones de Windows y ni siquiera sé si los compiladores modernos la soportan.

Pero antes, una introducción.

¿Qué necesito para usar una DLL de terceros?

Cuando vamos a usar una DLL, ya sea nuestra o de terceros, se nos deben suministrar al menos tres archivos. El primero, que no nos haría falta para el segundo método, es un fichero cabecera conteniendo los prototipos de las exportaciones de la DLL, es decir, las firmas de las funciones y la declaración de los tipos contenidos.

En general, el formato de cada prototipo debe seguir de cerca el siguiente formato:

__declspec(dllexport) __stdcall int HazAlgo(int algo);

O

extern “C” __declspec(dllexport) int HazAlgo(int algo);

La primera firma declara una función con formato de llamada estándar (el __stdcall) que pertenece a una DLL y que resulta exportado, es decir, que podemos usar. La segunda declara lo mismo pero con el protocolo de llamada de C, que también podríamos haber puesto como __cdecl. Del protocolo de llamada ya hablé en la entrada anterior, y en general todos los compiladores de C y C++ deberían entender ambas declaraciones, y si no lo hacen seguro que tienen expresiones similares.

El segundo fichero es lo que se conoce como biblioteca de importación, que es un archivo con la extensión .lib, con la diferencia de que, en este caso, en lugar de contener las propias funciones como en una biblioteca normal, lo que contiene son las firmas de las funciones exportadas y cómo están relacionadas con la propia DLL.

En el mundo Windows existen dos formatos de ficheros .lib: el COFF, que es el que usa Microsoft y compiladores compatibles y el OMF, que es el que usan otros compiladores como el C++Builder. Generalmente todos los compiladores suelen venir con una herramienta de conversión de formato.

Y finalmente necesitamos la DLL y todas sus dependencias si las hubiera.

En general muchas veces no es necesario recompilar el código de nuestra aplicación cada vez que se modifique la DLL si no hemos cambiado nada de la parte pública. Creo que incluso si se añaden funciones a la DLL tampoco es necesario recompilar siempre y cuando no se cambie ninguna declaración de las existentes y enlazadas.

Primer método: el fácil

El método más sencillo para usar una DLL en nuestro código en C o C++ es añadir la biblioteca de importación (el fichero .lib) a las demás bibliotecas en nuestro proyecto, lo que se suele hacer en las opciones del enlazador.

Luego incluimos el fichero cabecera en donde nos haga falta y ya está todo hecho: tan sólo debemos llamar a las funciones de la DLL como si fueran funciones normales y corrientes.

Debemos tener en cuenta que si la DLL contiene clases o tipos no estándar, quizás el enlazador sea incapaz de encontrar los nombres porque cada compilador de C++ ofusca el nombre de las funciones de una manera diferente, y en ese caso sólo nos queda la opción de o bien pedir una DLL compatible con nuestro compilador o bien hacer un strip del fichero LIB, ver cómo se llaman en realidad las funciones, y hacernos nosotros mismos el fichero cabecera. No os lo recomiendo.

El último paso es dejar la DLL en una ruta que nuestro programa sea capaz de encontrar, como el directorio donde se está ejecutando o en algún camino disponible en el PATH.

Segundo método: carga dinámica

No lo he dicho antes, pero cuando se usa el primer método, las DLL se cargan de forma automática a la vez que nuestra aplicación, y siguen en memoria hasta que salimos.

Existe una forma de carga manual un poco más laboriosa, pero que nos permite cosas como la carga dinámica de temas, de extensiones, de plugins o de diferentes versiones de una misma DLL.

Supongamos que tenemos una DLL con las cadenas para cada idioma. Cuando arranquemos nuestra aplicación miramos qué idioma está en la configuración y cargamos manualmente la DLL adecuada. Es lo que hace automáticamente el sistema MUI de Windows y la internacionalización en .NET y otros lenguajes. Pues de igual forma que se hace automáticamente, se puede hacer manualmente.

Para este método sólo necesitamos la DLL en cuestión y la lista de prototipos de funciones. Lo primero es hacernos un puntero a función de cada una de las funciones de la DLL:

int (__stdcall *pHazAlgo)(int);

Luego llamamos a la función de Win32 LoadLibrary() pasándole la cadena en donde está la DLL que queramos cargar. La función nos devolverá un HINSTANCE, que si todo ha ido bien será diferente de NULL. Entonces, para cada una de las funciones de la DLL, la cargamos sobre nuestro puntero mediante GetProcAddress() pasándole el HINSTANCE y la cadena con el nombre de la función en la DLL:

pHazAlgo=GetProcAddress(hLib,”HazAlgo”);

Ahora tendremos un puntero a función (que realmente es un doble puntero, porque ese puntero apunta a una tabla dentro de la DLL que a su vez apunta a la función real). Con ese puntero podemos llamar a la función sin problemas:

int respuesta=pHazAlgo(3);

Quizás tengas que hacer algún tipo de moldeo en la llamada a GetProcAddress().

Cuando queramos descargar de memoria esta DLL, llamamos a FreeLibrary() y será descargada. Nuestros punteros seguirán asignados, pero apuntarán a ningún sitio, por lo que si volvemos a necesitar la DLL (u otra versión de la misma) deberemos repetir el proceso.

Tercer método: ordinales y ficheros DEF

Este es el que no voy a explicar porque es muy antiguo y ni siquiera sé si sigue siendo válido. Antiguamente las funciones dentro de las DLL se guardaban numeradas. Es decir, en lugar de guardar una cadena con el nombre y un puntero a la relación relativa dentro del fichero en los metadatos de la DLL, se guardaba un array de punteros que apuntaba a cada una de las funciones.

Luego se tenía un fichero DEF que ponía el nombre de la función y el ordinal que tenía dentro de la DLL (la posición dentro de ese array), de forma que cuando se compilaba un programa para una DLL de este tipo (usando el fichero DEF en lugar del LIB), se dejaba un array de punteros a funciones que el cargador pareaba con el de la DLL…

Ya os podéis imaginar la que se podía armar si por cualquier motivo ese orden era cambiado por el compilador. El que faltara un ordinal simplemente lanzaba un error, pero el que se cambiara uno por otro podía terminar tumbando no solo nuestra aplicación, sino Windows al completo, ya que en aquella época las DLL sólo se cargaban una vez y eran usadas y compartidas por todas las aplicaciones que la requerían, sistema operativo incluido.

Esto se hacía así porque con la velocidad de los procesadores de aquellos años, el tiempo para buscar la cadena con la función en los metadatos y parearla con el puntero correspondiente consumía una gran cantidad de tiempo, sobre todo si había muchas funciones dentro de una DLL, y en la forma de ordinales tan sólo había que copiar punteros en bucle.

Obtener el propio número de versión desde MFC y Win32 (y II)

Hace poco expliqué cómo obtener el número de versión del propio ejecutable desde C++ Builder utilizando la VCL (su biblioteca de clases) y dejé como pendiente el obtenerla desde Win32 o MFC. Como lo he necesitado, he modificado el código y lo presento aquí:

TCHAR appName[MAX_PATH];
GetModuleFileName(AfxGetApp()->m_hInstance,appName,MAX_PATH-1);
DWORD handle;
DWORD size = GetFileVersionInfoSize(appName, &handle);
if(size!=0)
{
    TCHAR *buffer = new TCHAR[size + 1];
    GetFileVersionInfo(appName, 0, size, buffer);
    VS_FIXEDFILEINFO *pFixedInfo;
    UINT uVersionLen;
    if (VerQueryValue(buffer, _T("\"), (void**) & pFixedInfo, (UINT*) & uVersionLen) != 0)
    {
        CString version;
        version.Format(_T("%d.%d.%d.%d"),HIWORD(pFixedInfo->dwFileVersionMS),LOWORD(pFixedInfo->dwFileVersionMS),HIWORD(pFixedInfo->dwFileVersionLS),LOWORD(pFixedInfo->dwFileVersionLS));
        CString title;
        GetWindowText(title);
        CString newTitle;
        newTitle.Format(_T("%s - V%s"),title,version);
        SetWindowText(newTitle);
    }
    delete[] buffer;
}

Si os fijáis, el código es muy similar al del otro post, tan sólo cambia la forma de usar las cadenas, en una mezcla de MFC y Win32.

Para obtener la ruta del propio ejecutable, tenemos que hacer una llamada a la función de Win32 GetModuleName() que, a través del HINSTANCE de la aplicación, rellenará una cadena con la ruta, devolviendo el tamaño ocupado. No deben preocuparnos los desbordamientos de buffer si pasamos el tamaño correcto de la cadena: la función truncará hasta el máximo.

Los pasos siguientes son iguales, y lo que cambia, de nuevo, es el manejo de las cadenas y cómo las añadimos al título de la ventana.

Para que este código funcione en Win32 puro, tan sólo hay que usar las cadenas de C (y sprintf() y accesorias) o bien usar las cadenas de la biblioteca de C++, que es lo que recomendaría.

Instalar el SDK del iRex DR1000S en Linux (Ubuntu 9.10 Karmic Koala)

Ya sabéis qué poco me gusta Linux, pero a veces tiene su interés, o más bien me da por tomármelo sin más. Añadamos que Ubuntu no es que sea de muy de mi agrado y tendremos la perrería más absoluta en investigar sobre todo eso.

Pero resulta que sí tengo otra pasión, y es la de los aparatos equipados con pantallas de tinta-e, y la mayoría de ellos –por no decir todos- llevan en sus tripas Linux, y si algún fabricante saca algún SDK este seguro que va a ejecutarse bajo dicho sistema operativo.

Los productos de la empresa iRex tendrán muchas pegas y deficiencias en cuanto al software, pero lo que sí es cierto es que se toman muy en serio el Open Source. Cuando abandonaron el desarrollo del iLiad, publicaron todo el código fuente completo, y el DR1000S tiene su SDK e incluso su emulador. Esperemos que el recién salido DR800S también cuente con él.

Bueno, pues el otro día me dio por intentar, por enésima vez, instalarme el citado SDK dentro de una Ubuntu. En su momento, con la versión adecuada y la documentación oficial, no conseguí hacerlo funcionar, así que menos con una versión más moderna. Pero me picó el gusano y me puse a ello. Y lo conseguí tras muchos sudores y esfuerzos.

Pero primero lo que ya existe. La descarga oficial del código fuente y de los SDK está aquí: http://developer.irexnet.com/pub/iOn/. Tan sólo tienes que buscar la versión adecuada. En el momento en el que escribo esto, es la versión 1.7 para el SDK y 1.7.1 para el código fuente. Las instrucciones para instalarlo están aquí.

En Mobileread hay una imagen con Ubuntu lista para utilizar, en este hilo. No sé qué versión de SDK traerá, seguro que una antigua, pero simplemente hay que quitar el viejo y poner el nuevo. Cosas que no me molan de esa imagen es que la versión de Anjuta es muy vieja, así como el propio Ubuntu, pero para quien sólo quiera echarle un vistazo rápido le será suficiente.

Si quieres seguir esta entrada con detalle, ahora es el momento de echarle un vistazo a los primeros puntos del documento de la instalación. Yo cogí mi vmWare e instalé la última versión de Ubuntu, y aunque el manual diga que el SDK no funciona dentro de un sistema operativo virtualizado, no es así, no al menos en mi Windows 7 x64 con 8 GB de RAM. Y para que la VM no ande escasa, le he asignado 2GB de RAM.

Una vez instalada la distro, no añadas el repositorio de la sección 2.2, en lugar de ello haremos algunas cosas a mano. Si intentas seguir el documento te vas a encontrar con un Anjuta y un Poki que no van a funcionar.

Instalemos Anjuta y Anjuta-dev desde los repositorios normales. Una vez hecho eso instalaremos, a mano, los plugins de Anjuta Extras, que tendrás que bajar en código fuente de aquí: http://projects.gnome.org/anjuta/downloads.shtml.

Yo me he creado una carpeta dependiente de mi home con el nombre de “dr1000s”, y ahí dentro he puesto otra nueva llamada “dependencies”, que es donde voy a colocar el código fuente dependiente para que me funcione todo bien.

Descomprime e instala los Extras (ya sabes: ./configure, make, sudo make install), y si no sabes de qué estoy hablando mejor te coges la imagen ya preparada.

***

Ahora viene el tinglado gordo. En la página de arriba hay una serie de plugins, tenemos que instalar, a mano, el Poky SDK Plugin, que va por la versión 0.5 y no es compatible con las versiones modernas de Anjuta, que es el problema que tendríamos si siguiéramos las instrucciones de la documentación original.

Tenemos que bajarnos el código, descomprimirlo e intentar compilarlo. Igual que a mi, os va a dar docenas de errores, que hay que ir corrigiendo a mano, ya que el API de desarrollo de Anjuta ha variado y hay funciones que cambian de parámetros y otras que no existen, cosa que mola mucho y parece que nadie aprende a evitar. Si tienes un API y necesitas crear otro nuevo, no llames a las funciones iguales, o bien mantienes el viejo y creas nuevas funciones o bien creas las nuevas y abandonas las viejas.

También hay otro problema con el paquete gnome-vfs, que parece que ha cambiado de lugar o que el configurador del Poky SDK es incapaz de encontrar, el tema está en que hay que modificar una buena cantidad de código. Trasteé tanto que al final ya no recuerdo qué cambié.

Una cosa que tienes que hacer es copiar el contenido de /usr/include/gnome-vfs-2.0/gnomevfs en /usr/include/gnomevfs en tu distro. Ya sé que es una chapuza, que lo que hay que hacer es añadir las rutas en la configuración del proyecto, pero yo al menos no sé dónde se hace eso.

Bueno, en este hilo de Mobileread tienes el código fuente que modifiqué, listo para un sudo make install… Y lo pongo así porque hay que modificar un makefile generado por el configure, pero como de nuevo no sé dónde hay que añadir la línea, lo hice en el makefile final.

El tema está jodido, porque aparte de tener que añadir inclusiones nuevas, he tenido que poner una buena espuerta de NULL en las funciones que reciben nuevos parámetros, y comentar otras llamadas a funciones que ya no existen en Anjuta, de modo que lo mismo he hecho una bomba de relojería… Si quieres ver qué he cambiado, puedes hacer una comparación de los ficheros originales del Poky SDK con los que yo he modificado.

En fin, el tema está en que siguiendo estas instrucciones y las del propio documento, tendrás instalado tu SDK y tu emulador para desarrollar código para el DR1000 desde el IDE de Anjuta, con integración de todo y en un Ubuntu bastante más moderno y funcional que la imagen original.

Lo que no he conseguido ha sido lanzar el depurador desde el IDE, cosa que queda pendiente para otro momento. Quizás cuando lo consiga publique la imagen en algún lado.

¿Se fragmenta el registro de Windows?

La respuesta corta es que sí. La larga es que no mucho. Veamos.

Acabo de terminar de leer la parte del libro de Windows Internals que se refiere al registro de Windows, y básicamente me ha quedado un mal sabor de boca, cosa que a veces suele pasar con este tipo de libros políticamente aprobados, y es que no deja nada claro si el registro se fragmenta o no.

En disco, el registro no es más que una serie de ficheros repartidos a lo largo de varios lugares, ficheros que se corresponden con las diferentes ramas del mismo. La mayor ventaja de Vista y siguientes frente a XP es que ahora existe un juego de llamadas a función que realizarán operaciones transaccionales sobre el mismo. Es decir, igual que en las bases de datos –y el registro no deja de ser una-, iniciamos una transacción, realizamos una serie de acciones y si las completamos bien, cerramos dicha transacción, y si no, la abortamos. Para los programas instaladores esta característica es cojonuda, y a veces también para los programas de usuario. Con sólo imaginarnos dejar el registro a medio actualizar a causa de un corte de corriente…

Lo que sí queda claro es que en memoria la fragmentación es mínima. Es decir, casi da igual el grado de fragmentación que haya en los archivos (los hive, como los llaman), cuando estos son cargados a memoria mediante virtualización de la misma, la posible fragmentación queda reducida al mínimo. Además, los valores más comúnmente accedidos quedan cacheados. También se mantienen listas y cachés de escritura, para minimizar la sobrecarga en el sistema, lo que a veces, ante un corte imprevisto de corriente, hace que no se graben dichas modificaciones, aunque Windows garantiza la no corrupción de los hives mediante un sistema de mapeados y de escrituras en ficheros dobles y triples (más o menos lo mismo que hago yo con los firmwares cuando los datos no deben corromperse ante un corte de luz –en mi caso se trata de memorias flash y eeproms, pero la situación es la misma). La descarga a disco se hace cada cinco segundos, aunque podemos forzar nosotros una mediante la llamada a la función RegFlushKey() que, pese a lo que diga la documentación, hace un volcado de todo lo pendiente, no solo de la Key abierta.

Pero en disco la cosa no está tan clara, y los autores tampoco lo dicen expresamente, pero parece ser que sí, que el registro se puede fragmentar.

El registro se guarda en disco en bloques de 4KB. Es decir, aunque necesitemos 1 byte, el hive crecerá en 4KB. Eso no quiere decir que hayamos desperdiciado el resto, que no va a ocurrir, ya que luego dicho bloque se irá rellenando conforme se vayan añadiendo claves.

Ese es el primer nivel de indirección. Encima existe un nuevo nivel lógico, en el que el registro se distribuye en forma de listas sobre dichos bloques, y que realmente conforman la estructura que nosotros vemos. Digamos que este nivel consiste en una especie de mezcla de árbol y lista enlazada que se va distribuyendo a lo largo de los bloques en disco.

La combinación de ambas estructuras permite la fragmentación de forma muy fácil: tan solo hay que ir creando árboles y luego eliminando nodos y árboles anteriores. De hecho, en poco tiempo podríamos tener un buen nivel de fragmentación, sobre todo cuando hemos instalado y desinstalado un montón de programas.

No obstante, Windows lucha contra esta fragmentación mediante la compactación de los espacios libres. Es decir, cuando en un bloque físico hay huecos, estos serán compactados para dejar el mayor espacio libre contiguo, para –creo entender- volver a ser reutilizado.

No obstante, el bloque no será liberado de disco hasta que no esté completamente vacío, y sólo cuando esté al final del fichero. Es aquí donde puede venir la fragmentación, ya que podríamos tener muchos bloques vacíos por en medio y una jodida clave al final que nos impidiera la limpieza de un buen espacio en disco, ya que el algoritmo de desfragmentación no coloca los huecos al final, sino que simplemente los maximiza.

Y aquí es donde entra la estadística. Estadísticamente hablando, siempre habrá algo de fragmentación porque habrá huecos intermedios, que serán rellenados conforme se vayan necesitando, y en el peor de los casos el registro no decrecerá en tamaño, pero tampoco crecerá hasta que todos los huecos estén rellenos.

Lo que más me jode de todo esto es que en el libro no está muy claramente contado, sino que el tema de la fragmentación se recorre de puntillas, como si fuera un pecado. Tan sólo se comentan, de pasada, dos funciones que podrían permitirnos compactarlo y quizás decrecerlo: RegSaveKey() y RegReplaceKey(), que dice son usadas por Windows Backup.

Podríamos intentar jugar con dichas funciones para ver si conseguimos compactar nuestro registro pero, siéndoos sincero, no tengo ganas de trastear con algo que podría dejar mi ordenador completamente inservible sólo por ganar unos cuantos megas de disco…

Acronis ya no es lo que era

Vamos a empezar el año dando caña de la buena. Acronis siempre ha sido un buen referente en cuanto a copias de seguridad. Recomendado por muchos expertos, yo, sin serlo, nunca había experimentado problema alguno con el programa. Ni cargaba el sistema, ni producía efectos laterales indeseados y ni siquiera se notaba cuando se estaba realizando la copia de seguridad.

Pero la versión 2010 es otra cosa. Aunque tiene cosas buenas, como que ahora no tarda un cuarto de hora a arrancar analizando las particiones y los discos (y tengo seis). Otra ventaja es que ahora las cosas están mucho más fáciles de encontrar. Se nota que han reorganizado la ergonomía del programa pensando en el usuario novel y con prisas. Antes encontrar las tareas programadas podía ser una odisea de movimientos por el programa.

No obstante esas mejoras, hay otros aspectos del programa que ciertamente lo hacen, a mi modo de ver, completamente inusable. Y el problema no está en los fallos, que los tiene, el problema está en la confianza que te inspira el programa. Hablamos de copias de seguridad, de lo que quizás salve a tu empresa del desastre total. Y si un programa de este tipo no te inspira confianza, lo mejor es que lo quites pese a haberlo pagado a tocateja. Podemos aplicar lo mismo al caso del usuario no profesional. Imagina que pierdes todas las fotos y vídeos hechos a tu hijo…

El programa usa el 100% de la CPU cuando está lanzado. En un Dual Core será el 50%, y el 25% en un Quad. Aunque no esté haciendo nada. Acronis dispone de un servicio que es el que realmente hace el trabajo duro. El programa que lanzamos no es más que un interfaz hacia dicho servicio, y es ese programa el que consume toda la CPU.

Sigue en memoria una vez cerrado y ya no podemos volverlo a lanzar. Es decir, cuando lo cierras, deja de usar el 100% de la CPU pero se queda en memoria, menos mal que sin usar tiempo de proceso. Pero a partir de ese momento ya no podremos volver a lanzarlo. Irán creándose procesos con cada lanzamiento pero no aparecerá nada en pantalla hasta que no los hayamos matado todos (o reiniciado).

Las copias shadow interfieren con la shell. Acronis 2010 tiene un sistema de copias shadow sobre otra unidad, lo que significa que realiza la misma funcionalidad que el “restaurar versiones previas” del propio Windows, pero la copia está en otra unidad de disco, y así matamos dos pájaros de un tiro: copia de seguridad y versionado de archivos. El problema está en que si tenemos eso activado a veces resulta por completo imposible mover o borrar archivos de la unidad protegida. El explorador se queda indefinidamente esperando algo.

Las copias shadow se desactivan solas. Sí, cuando menos te lo esperas, miras el icono de la barra de tareas y resulta que el servicio se ha detenido, sin aviso alguno, y si intentas iniciarlo suele negarse a hacerlo si no abres el Acronis completo. Y entonces a veces empieza desde cero, borrando lo que ya tenía y rehaciendo la imagen. Para confiar en él.

Problemas varios con el secure zone. Cuando activas esta característica (que es una partición especial no visible por el sistema en donde se guardarán y gestionarán de forma automática las copias de seguridad), aparece una nueva unidad en el Explorador. Si intentamos abrirla nos salta el UAC, y una vez aceptado, podemos ver todas las copias de seguridad que tengamos ahí, acceder a ellas y recuperar ficheros. Lo que parece sencillo no lo es. En primer lugar es lento de cojones. En segundo no te muestra todas las copias (en concreto las incrementales). Y en tercero muchas veces aparecen como vacías.

Montar unidades. Acronis permite montar ficheros de copia de seguridad como unidades de disco. Cuando funciona, claro, porque la mayoría de veces te dice que no encuentra una unidad de disco libre (yo tengo 19), y cuando lo monta tarda como cuarto de hora mientras el ordenador se queda como autista, y a veces tampoco lo consigue.

Ralentiza la carga de Windows entre 20 y 60 segundos según cómo lo pilles. Mi Windows 7 tarda sobre 40 segundos a arrancar desde que se quita la pantalla del POST de la BIOS hasta que veo el escritorio. Teniendo el Acronis ese tiempo pasa de 40 a 60 u 80, y a veces más.

Si os dais cuenta, prácticamente todas las características del programa tienen alguna pega, incluso las que funcionaban bien en versiones anteriores. Entiendo que son cosas muy complicadas, sobre todo las novedades, pero es que llevan como diez revisiones de la versión 2010 y no dan pie con bola. Se escudan en los cambios que Microsoft ha hecho al núcleo y a los servicios de Windows, pero a mi modo de ver eso no es más que una excusa para no solucionar los problemas, ya sea porque no quieran o porque no puedan.

Son todas las cosas que me están ocurriendo a mi en mi Windows 7 x64 con 8GB de RAM en un Q4 con 6 discos duros. Si os vais a los foros de Acronis en la web del fabricante veréis cómo son miles las personas que están teniendo problemas similares.

Esto me lleva a pensar en una idea aprovechando las características de Windows Vista y 7: utilizar las copias shadow para hacer una copia. El algoritmo sería el siguiente:

  • Crear una copia shadow.
  • Montarla en una carpeta o unidad.
  • Copiar dicha unidad en otro disco, quizás zipeándola o de cualquier otra forma.
  • Desmontar la copia.
  • Opcionalmente borrarla.

Tan solo tendríamos que programar una tarea con un script de comandos con dichas instrucciones y listo, se podría copiar periódicamente. Incluso podríamos tener una especie de shadow en destino, haciendo una copia en una carpeta diferente cada día de la semana.

Yo tengo algo parecido ahora mismo, pero simplemente se trata de un xcopy sobre las carpetas normales, con lo que los ficheros abiertos no se copian. Y son los abiertos los que interesa copiar, ya que son los que han variado.

Siendo un poco cucos y usando el atributo de archivo (que se pone cada vez que un fichero ha sido modificado) podríamos tener un histórico semanal o quincenal o mensual.

Imaginemos que tenemos la unidad Z:, que es donde están nuestros datos. Hacemos una copia shadow y la montamos en, por ejemplo Z:Shadow. Hacemos la copia de dicha carpeta sobre nuestra unidad de copia de seguridad, digamos M:Backups<dia_de_la_semana>. Y copiamos sólo los archivos con el atributo de archivo activado, con lo que en M:Backups<dia_de_la_semana> tendremos los cambios de ese día. Si generamos un registro de los ficheros copiados, y queremos recuperar la última versión de uno de ellos, abrimos dicho log y buscamos la última ocurrencia del mismo. Si hemos decidido tener un histórico semanal, cada domingo hacemos una copia completa. O incluso podemos tener una cola de copias de seguridad. Si estamos a jueves, el miércoles será la copia anterior, y así hacia atrás hasta el viernes. Evidentemente podemos elegir la periodicidad que queramos siempre que tengamos sitio en la unidad.

El problema es que no sé cómo hacerlo. He estado mirando varios comandos pero no he conseguido sacarle punta, y si Windows lo hace automáticamente, también podrá hacerse a mano. Es cuestión de investigar más a fondo, y yo no tengo tiempo. Lo dejo abierto para quien quiera hacerlo, y si me lo envía se lo publicaré aquí.