1. Este sitio usa cookies. Para continuar usando este sitio, se debe aceptar nuestro uso de cookies. Más información.

[Aporte] Memoria en Visual Basic 6 (Avanzado)

rubio93 23 Jun 2008

Estado del Tema:
Cerrado para nuevas respuestas
  1. rubio93

    rubio93
    It's Time For Plan B Ex-Staff

    Índice

    1. Nociones digitales
    2. Representación de datos binarios
    3. Arquitectura de memoria
    4. Argumentos como referencia o como valor
    5. Comportamiento de Strings en memoria
    6. Funciones de manejo de memoria
    6.1 CopyMemory
    6.2 CopyMemory y Strings
    6.3 Asignar y liberar memoria
    6.3.1 Distribución de la memoria virtual
    6.3.2 Espacio de Direcciones Virtuales y Almacenamiento Físico
    6.3.3 Estado de las páginas
    6.3.4 Alcance de la memoria asignada
    6.3.5 Funciones VirtualAlloc y VirtualFree
    6.4 Protección de memoria
    6.4.1 Modos de protección: VirtualProtect
    6.4.2 VirtualLock, VirtualUnlock
    6.5 Leer y escribir memoria de distintos procesos
    6.6 Memoria compartida
    6.6.1 Concepto. Importancia de la memoria compartida. Ejemplos.
    6.6.2 Asignación de memoria compartida
    6.6.3 Escribir, leer en memoria compartida
    6.7 Validar datos y accesos en memoria
    6.8 Consultar estado y datos de regiones de memoria: VirtualQueryEx
    6.9 Otras funciones de memoria: ZeroMemory, FillMemory, GlobalMemoryStatus
    7. Usar la memoria en otros procesos: consideraciones y ejemplos.
    8. Creando programas para todas las plataformas de Windows.


    Algunas veces se lo expliqué a algunos pero mejor lo dejo sentado en un documento así no tengo que volver a repetirlo xD.

    Antes de explicar el funcionamiento de las funciones de memoria que nos proporciona el SO, hay que tener presentes algunos conceptos básicos de técnicas digitales y cómo se posicionan los datos en memoria, así que voy a empezar por ahí.


    1. Nociones digitales

    Primero, los datos en la memoria física, o sea en la plaqueta que tenemos dentro del gabinete, se guarda en forma binaria, o sea como 1's y 0's, y esto es debido a una razón muy lógica y es que en la electrónica hay dos opciones, hay tensión (un 1) o no (un 0).

    Ahora, para los ojos humanos ver millones de 0s y 1s no nos diría nada y poder mostrar eso de alguna manera mediante un programa, requeriría de muchos recursos. Lo que nosotros vemos en los editores de memoria en realidad son bytes, no bits (un 0 o un 1 es un bit).

    Un byte es un conjunto de 8 bits, que puede adoptar un valor entre 0 y 255 en el caso de que sea sin signo, o de -128 a 127 para el caso que sea con signo. Muchos se preguntarán el por qué estos valores, y para entender algo es mejor llegar hasta el fondo, así que voy a dar el ejemplo con un nº binario.

    Un número de 8 bits visto en binario sería como el siguiente:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Es claro, 8 bits alineados. Entonces, como en el sistema binario sólo se puede trabajar con 0s y 1s, el valor máximo que puede adoptar un número de 8 bits se alcanzará cuando todos los bits estén en 1:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Lo único que nos queda ahora es transformar ese nº binario en decimal, pero primero hay que tener en cuenta unos conceptos:

    1. Cada bit tiene un índice de base 0 que lo identifica, siempre contando de izquierda a derecha:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Dependiendo de la posición del bit, este valdrá diferente, se dice que cada bit tiene "peso", y el más pesado es el primero de la derecha, esto se debe a que cada posición adopta el valor de 2 (dos) elevado al índice, entonces el valor de cada bit sería:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Esto significa que para el índice 0, el peso del bit será 2^0, para el índice 1, será 2^1, para el 2, 2^2, etc.

    Ahora, para terminar la conversión, multiplicamos el valor del bit por el peso que tenga, y eso nos dará el número en decimal:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Y voilà, ahí tenemos el 255 que es lo máximo que podemos representar con un nº de 8 bits.


    2. Representación de datos binarios

    Una vez que lo anterior está claro, voy a explicar cómo representan los editores hexadecimales y demás visores de memoria, y el mismo sistema a los datos.

    Como dije antes, no hay forma de leer un 1 o un 0 de la memoria si no es aplicando álgebra de boole (AND, OR, etc). El valor mínimo que se puede leer de la memoria es 1 byte, por esta razón la representación que hacen es mostrar los datos con caracteres ascii, ya que estos ocupan 1 byte (valores de 0 a 255). Pero que quede bien claro, TODO LO QUE SEA TEXTO ES PARA MOSTRAR AL USUARIO, incluso esto que estoy escribiendo se guarda en forma binaria, pero se muestra en forma gráfica de manera que sea legible para el ojo humano.

    Entonces todos esos "caracteres raros" que vemos al abrir un archivo o al examinar la memoria, no es otra cosa que un conjunto de valores de 1 byte, que por comodidad se muestran gráficamente como un caracter.


    3. Arquitectura de memoria

    Acá viene la parte más importante que es cómo están dispuestos los datos en la memoria, y el funcionamiento general de el SO en la interacción con esta. Si no leyeron lo anterior les recomiendo que lo lean, si no lo entendieron es mejor que pregunten porque sin tener los conceptos anteriores claros va a ser difícil comprender esta parte.

    Otra aclaración, desde esta parte voy a dejar de hablar de bits, y me voy a referir a los bytes ya que así es como trabaja el sistema en general, además se harían muy extensos los ejemplos.

    Voy a empezar explicando dos conceptos que adoptan los microprocesadores: Big Endian y Little Endian

    Como todos sabemos, el micro interactúa directamente con la memoria leyendo y escribiendo datos, pero dependiendo de la arquitectura del micro los va a escribir de diferente manera.

    Little Endian
    En este caso, el microprocesador escribe el byte más bajo de un número en la dirección más baja de memoria. Voy a dar un ejemplo con un número de tipo Long (4 bytes). Para que quede claro mostraré primero cómo estarían repartidos los bits

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Una arquitectura Little Endian dispondría un número de estas caractéristicas de la siguiente manera en memoria:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Donde Dirección es una dirección cualquiera de memoria. Lo importante a destacar es que el primer byte del número será el primero en memoria. Esta norma la adoptan los microprocesadores de Intel y compatibles (los que la mayoría usamos)


    Big Endian
    Al contrario del caso anterior, esta arquitectura dispone el byte más alto en la dirección de memoria más baja:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!

    ¿A qué viene todo esto?, muy sencillo, que para saber cómo leer desde la memoria debemos conocer primero cómo guarda los datos, es una cuestión de lógica.

    Lo último que queda por decir de este tema es que existen otras unidades de medida además del byte, y que son los Words y los DWords o DoubleWords.

    Un Word equivale a 2 bytes, y un DWord equivale a 2 Words. ¿Por qué no digo que un DWord son 4 bytes?, porque así van a comprender mejor lo que sigue :p.

    Y ya está llegando la parte interesante que nos va a introducir adentro de la programación. Desde acá en adelante voy a explicar todo lo que resta del funcionamiento de memoria pero orientado a Visual Basic, que es lo que nos interesa.

    Antes de seguir, voy a poner el código de 6 funciones que nos van a servir para experimentar y ver que lo que estoy explicando en esta guía es verdad.

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Cada una de las funciones está comentada, lo único que voy a destacar es que un Word (2 bytes) equivale a un Integer que ocupa lo mismo, y un DWord (4 bytes) equivale a un Long. Más adelante vamos a usar estas funciones.

    Lo que viene ahora no es exclusivo de VB sino de programación en general, pero será explicado con las matrices de Byte de VB.

    Una matiz de Byte no es más que una serie de bytes consecutivos en memoria. Vamos a suponer que tenemos una matriz de 6 bytes, y le asignamos los siguientes valores:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ahora vamos a suponer que el primero elemento de la matriz está en la dirección de memoria 1000 decimal, entonces en un mapa de memoria estos datos quedarían así:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    El esquema en columna es sólo para demostrar, que cada byte le sucede al anterior pero esto está alineado en realidad:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Como dije en una parte de este documento, los editores Hexadecimales (no sé por qué le pusieron ese nombre xD) y visores de memoria varios, siempre muestran en una parte el valor numérico y en otra parte el caracter gráfico que le corresponde a ese valor. Entonces si pasamos esta matriz a la forma "visible para el ojo humano", quedaría así:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Entonces queda claro que una cadena de caracteres no es más que la forma que tiene el sistema operativo de mostrarle gráficamente al usuario una serie de números de 1 byte.

    Lo único que falta es entender cómo guarda los DWords y los Words, y cómo se generan. Acá es donde nos van a servir las funciones anteriores.

    Tomaremos como ejemplo los primeros 4 elementos de la matriz anterior, 65-66-67-68, para ejemplificar la representación de números de 2 y 4 bytes. Como dije antes, un DWord son 2 Words, y un Word son 2 bytes, por lo tanto un DWord son 2 pares de bytes. Consideremos el esquema siguiente para comprender cómo se forman los números en memoria:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Donde BL es el 'Byte bajo' del correspondiente Word, BH es el 'Byte alto', WordL es el 'Word bajo' del DWord y WordH es el 'Word alto' del DWord.

    Entonces, vamos por partes como dijo jack el destripador xD, el siguiente código primero genera ambos Words y luego el DWord:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ahora vamos a necesitar un editor de memoria, el mejor editor hexa que conozco es el WinHex, pero pueden usar el que más les guste. Este procedimiento crea el DWord por cada una de sus partes, e imprime en la ventana Inmediato la dirección de la variable lDWord en hexadecimal (sólo por conveniencia porque las direcciones de memoria son números muy grandes).

    La función no-documentada de VB VarPtr() nos devuelve el puntero a cualquier variable. El puntero a una variable es la dirección en memoria del primer byte de los datos, se llama puntero por la razón obvia, que apunta al primer byte.

    Si con el editor hexadecimal nos dirijimos a la dirección que se imprime, y vemos la parte de los valores hexadecimales, veremos 4 bytes seguidos, 65-66-67-68.

    Para el que no quiera usar un editor hexadecimal o no lo tenga, escribí otras 2 funciones que convierten de Texto a DWord y de DWord a Texto, tal y como funciona en memoria:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ahora cambiemos la linea que imprime la dirección de la variable lDWord por la llamada a DWordToString(), para que vean lo que pasa :).

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Y magia, ahora sale en la ventana inmediato ABCD :), ¿qué es ABCD?, nada menos que los valores 65-66-67-68 en sus respectivos caracteres ASCII.

    Creo que quedó claro el funcionamiento de la memoria, ahora ya viene la parte de las funciones que nos proporciona el SO para manejarla, pero antes, la gran diferencia entre ByVal y ByRef


    4. Argumentos como referencia o como valor

    Hay dos maneras de pasarle argumentos a las funciones, como valor o como referencia. En Visual Basic TODAS las variables se pasan como referencia a menos que se indique lo contrario usando ByVal, la palabra clave utilizada para pasar una variable como valor.

    La diferencia es que al pasar una variable como referencia, lo que está pasando es el puntero a dicha variable. En cambio, pasando la variable como valor lo que se pasa es el valor o 'contenido' de la variable en ese instante.

    Por ejemplo, tengo la variable lData y le asigno un valor cualquiera:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ahora escribimos dos procedimientos, uno que acepte la variable como valor y otro como referencia:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Luego probamos ambas funciones con el siguiente procedimiento:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Y sólo con mirar los outputs se dan cuenta lo que pasa. Cuando paso la variable como valor, lo que sucede es que se copia el contenido de la misma a otra parte de la memoria distinta de la dirección de esta, entonces el procedimiento modificará los datos en la dirección nueva de memoria, y al terminar (End Sub) VB borrará este 'espacio temporal' que usó para copiar el valor.

    Como se puede ver, pasar un argumento como valor lleva un poco más de tiempo ya que tiene que asignar más memoria y copiar los datos, pero es muy necesario cuando se llama a las APIs ya que si pasamos una variable como referencia y a esta función se le ocurre escribir más allá del tamaño de la variable, hará que el programa produzca un error y se cierre porque vaya a saber qué es lo que modificó.

    El caso de los datos String es algo particular, porque no se comportan como las variables numéricas. Las variables String están compuestas de dos partes, la variable en sí y los datos de la variable.

    Como todos sabrán las variables String ocupan 4 bytes, esto se debe a que en realidad es una dirección de memoria donde están los datos de la misma. Por ejemplo, supongamos que declaro la variable sData y le asigno un valor:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    En este ejemplo hipotéticamente creeremos que la variable sData se encuentra en la dirección 8000, y los datos en 9000. Esto significa que en la dirección de memoria 8000 (el puntero a la variable sData), encontraremos el valor 9000 que indica la dirección de los datos en Unicode, ya que las cadenas de VB son todas Unicode (2 bytes por caracter)


    5. Comportamiento de Strings en memoria

    Creo que el tema de los Strings en VB merece un apartado especial, ya que es algo muy importante y es algo difícil de ver y entender.

    Primero hay que destacar que existen dos tipos de String que usan las APIs y Visual Basic, LPSTR y BSTR.

    Las cadenas LPSTR (String Pointer) son muy simples, se trata de una serie de caracteres terminados en un caracter nulo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Se denomina puntero a la dirección de memoria del primer byte de un dato determinado. En el ejemplo anterior el puntero a esa cadena sería la dirección de 'H', o más bien del valor 72 (código ascii de 'H')

    Esto en Visual Basic es equivalente a una matriz de byte, ya que como dije antes no es más que una serie de valores de tipo Byte consecutivos en memoria.

    En cambio Visual Basic utiliza las cadenas BSTR, que son un tipo de dato de automatización, adoptado de OLE Automation (Automatización OLE). Este tipo de String está diseñado para evitar problemas comunes como lo son los BOFs (Buffer Overflow), ya que debido a su estructura es imposible que se de esta situación en la asignación.

    A diferencia de las LPSTR, las cadenas BSTR son SIEMPRE Unicode, esto significa que cada caracter ocupará 2 bytes (1 WORD). También hay que destacar que no terminan en un caracter nulo sino en dos caracteres nulos, como dije antes cada caracter es un WORD.

    Otra característica especial es que los 4 bytes anteriores al inicio de la cadena especifican el tamaño de la misma, de esta manera el sistema no deberá escanear byte por byte buscando el final del String y el proceso se hará mucho más eficaz y rápido.

    El siguiente esquema ilustra una cadena BSTR:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    En el alfabeto latino y derivados sólo se usa el primer byte del WORD para almacenar el código de caracter, ya que los valores oscilan entre 0-255. En alfabetos orientales y demás tienen códigos de caracter muy superiores a 255 y por esta razón la necesidad de usar 2 bytes para guardar este valor.

    Por un lado ya sabemos cómo se guardan las cadenas en memoria, ahora paso a explicar el funcionamiento de las variables String.

    Primero hay que saber que existen 2 funciones no-documentadas de VB: VarPtr() y StrPtr()

    VarPtr() devuelve el puntero a una variable, cualquiera sea el tipo de variable.
    StrPtr() devuelve el puntero a la cadena de una variable String.

    Ahora, como ya comenté antes las variables String tienen dos partes, la variable en sí y los datos de la variable, que NO se encuentran en la misma dirección. Esto es mucho más sencillo explicarlo con un ejemplo. Vamos a utilizar el siguiente código de ejemplo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ejecuten el procedimiento StrTest() y cuando la ejecución se detenga es donde nosotros comenzamos a experimentar con la memoria. Ahora sí, es determinante para la comprensión de este tema usar un visor de memoria/editor hexadecimal. Yo recomiendo el WinHex porque es el mejor desde mi punto de vista.

    Ahora en la ventana Inmediato aparecieron dos direcciones, la primera es la dirección de la variable y la de abajo la dirección de los datos. Para comprobar que esto es cierto abrimos el WinHex, abrimos la memoria completa del proceso de VB (VB6.EXE) (Alt+F9 para ver la lista de procesos) y por último apretamos Alt+G para que aparezca el cuadro de "Ir a". Ahí vamos a colocar la primera dirección, en mi caso es 7FF540. El programa se posicionará en esa dirección de memoria y veremos algo como lo que muestra la siguiente imagen:

    [​IMG]

    Si se fijan bien en esta dirección no hay ninguna cadena, pero el valor de 32 bits que está almacenado en la posición 7FF540 (VarPtr(sData)) es 64B718, o sea StrPtr(sData) que es la dirección de la cadena.

    Ahora copiamos esta dirección que vemos y hacemos los mismo, Alt+G y vamos a la nueva dirección que nos manda la variable, o sea 64B718. En este momento una imagen vale más que mil palabras :)

    [​IMG]

    En 64B718 es donde realmente están los datos de la variable sData.

    Bueno, entonces resumiendo un poco (ya sé que repetí lo mismo muchas veces, pero es para que se entienda xD), si pasamos una variable String como ByRef estamos pasando la dirección de la variable, en el caso anterior sería 7FF540. En cambio al pasarla como ByVal estamos pasando la dirección de los datos, 64B718 en este ejemplo.


    6. Funciones de manejo de memoria

    No voy a dar muchas vueltas, directamente voy a ir a lo que hace cada función y cómo se comporta en memoria.

    6.1 CopyMemory
    Esta es la función más antigua que implementa el sistema operativo pero sigue siendo muy funcional y vigente. El prototipo de esta función es el siguiente:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    El uso de esta función es muy simple siempre y cuando se esté bien conciente de qué se está copiando y a dónde. Los parámetros que hay que pasarle son los siguientes:

    Destination: Dirección de memoria a la cual se copiarán los datos.
    Source: Dirección de memoria desde donde se copiarán los datos.
    Length: Cantidad de bytes que la función leerá desde Source y copiará a Destination.

    El gran problema a la hora de usar esta función es que hay que saber cuándo debemos pasar un parámetro como valor o como referencia. Si no leyeron todo el documento les recomiendo que lean 'Argumentos como referencia o como valor' primero.

    Consideremos el siguiente ejemplo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Para analizar esto hay que tener en cuenta lo siguiente:

    La palabra clave Any se utiliza para pasar el puntero (vease definición de puntero en la sección anterior) a CUALQUIER TIPO de variable. Pasarle una variable a una función con argumentos declarados como Any, es análogo a usar ByVal VarPtr(Variable). Por ejemplo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    ¿Por qué?. En mi caso particular la dirección de lSourceData es 7FF540, y de lDestData es 7FF53C.

    Cuando llamamos a VarPtr(), como ya notaron anteriormente, nos devuelve 7FF540 para lSourceData y 7FF53C para lDestData. Mirando el primer ejemplo, cuando se pasan las variables como Any y por referencia , lo que Visual Basic le pasa a la función CopyMemory son esos valores que devuelve VarPtr().

    Si pasamos la variable como ByVal, lo que hace VB es pasarle a la función CopyMemory los datos de la misma. Por ejemplo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Lo que le estoy diciendo ahí es que copie a 7FF53C lo que hay en la dirección 123456, ya que en este caso se le pasa el valor de lSourceData y CopyMemory SIEMPRE toma los argumentos como direcciones de memoria.

    Volviendo al ejemplo de ByVal VarPtr(Variable), lo que le estoy indicando a VB es que le pase el valor que devuelva VarPtr, o sea 7FF540 entonces CopyMemory leerá en el lugar correcto. Eso sería equivalente a lo siguiente:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Ya que ahora, cada variable va a contener la dirección de sí misma. Esto es cuestión de probar y ver lo que pasa, por más que yo se los cuente si no lo ven será difícil de asimilar.

    En el caso de una tipo definido por el usuario (TDU), es exactamente lo mismo. Lo que hay que saber, es que cada registro de los TDU se encuentran consecutivos en memoria. Veamos el siguiente ejemplo:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    En memoria estaría dispuesto de la siguiente manera:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Esta estructura ocuparía 22 bytes de memoria, ya que son 2 Long (8 bytes) más un Currency (8 bytes) más un Integer (2 bytes) más un String (4 bytes).

    Vamos a creer que el puntero a la estructura está en 1000 (el puntero es el primer byte del primer registro, o sea el primer byte del campo Size).

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Con esto creo que está concluída la explicación del uso de CopyMemory() con variables no-String.


    6.2 CopyMemory y Strings

    Para este tema prefiero escribir un apartado porque si bien es similar, es mucho más importante porque puede llegar a provocar un error general del programa y que se cierre.

    Antes de seguir recomiendo que lean 5. Comportamiento de Strings en memoria, porque no pienso volver a repetir todo, sólo un repaso muy por arriba.

    Bueno ahora sí empiezo. Para copiar los datos de una variable String hay que tener en cuenta dos puntos claves:

    1. SIEMPRE y sin excepciones hay que usar ByVal.
    2. Antes de llamar a CopyMemory la variable de destino tiene que ser inicializada como mínimo con la misma cantidad de datos que la variable desde donde se copia (Source).

    Estos dos items los voy a explicar con un ejemplo CORRECTO, luego voy a mostrar la forma errónea.

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Este código funcionará correctamente ya que lo que se está haciendo es copiar el contenido de una variable a la otra. A continuación muestro los datos que hay en memoria para la variable sSrcData luego de establecer el ejecutarse la linea sSrcData = "Hola mundo":

    ACLARACIÓN: Para ver bien los datos del dumpeo de memoria copienlos al bloc de notas y deshabiliten el ajuste de linea.

    Código Fuente (Text):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    El puntero a la cadena es 62D924, o sea la dirección de 'H', y la cadena ocupa en memoria 20 bytes, o sea LenB(sSrcData), ya que como pueden observar se encuentra en Unicode.

    Lo segundo que se debe hacer es reservar la MISMA cantidad de espacio en sDestData para poder copiar los datos de sSrcData, eso es lo que hacemos con la función String():

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Por empezar, llenamos la cadena sDestData con caracteres nulos pero sólo es por una cuestión de comodidad, pero puede ser cualquier caracter que queramos. Una vez que asignamos espacio los datos de sDestData se verían así en memoria:

    Código Fuente (Text):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Desde la posición 66E060 (StrPtr(sDesdData)) vemos que ahora la cadena sDestData está inicializada, entonces ya estamos preparados para llamar a CopyMemory:

    Código Fuente (Visual Basic):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Lo que le estamos diciendo es que copie 20 bytes desde 62D924 (el puntero a los datos de sSrcData) a 66E060 (el puntero a los datos de sDestData), por lo tanto quedarían exactamente igual:

    Código Fuente (Text):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Código Fuente (Text):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Como vemos los datos son exactamente iguales. Acá lo importante es inicializar la cadena de destino, porque sino estaríamos sobreescribiendo los datos que siguen y vaya a saber qué modificamos, por eso produce un error de página no válida y se cierra el programa.

    Si no pasamos los datos como ByVal lo que pasaría es que modificaría la variable, no los datos, y sobreescribiría datos que no queremos por lo que se cerrará el programa.

    Código Fuente (Text):
    Sólo usuarios registrados pueden ver esto. Click aquí para registrarte!
    Los datos de la variable sDestData son '60 E0 66 00' y los de sSrcData son '24 D9 62 00', que si usamos las funciones MakeWord y MakeDWord como lo expliqué antes veremos que obtendremos las direcciones de los datos. La cuestión es que si no usamos ByVal le estamos diciendo que copie 20 bytes desde 007FF540 (sSrcData) a 7FF53C (sDestData), o sea que reemplazaría '60 E0 66 00' por '24 D9 62 00 D4 F5 7F 00 81 1B 67 00 04 00 00 00 00 00 00 00 02' y sobreescribiría en memoria a sSrcData que está justo después de sDestData, entonces ya no se puede predecir lo que pasará porque no sabemos qué datos arruinamos, y en general se jode el programa :p.

    No sé si podría explicarlo más claro, cualquier consulta en este mismo post, para comprender esto hay que practicar y ver lo que pasa.


    6.3 Asignar y liberar memoria

    El uso de estas funciones es muy sencillo, pero antes de hablar de ellas voy a describir un poco como se conforma la memoria.

    6.3.1 Distribución de la memoria virtual

    El espacio de memoria virtual está dividido en particiones:[table][tr][td]Windows NT Services, Edición Empresarial[/td]
    [td]La partición de 3 GB en la memoria baja (0x00000000 a 0xBFFFFFFF [3221225471]) está disponible para los procesos, y una partición de 1 GB en la memoria alta (0xC0000000 [3221225472] a 0xFFFFFFFF [4294967295]) está reservada para el sistema[/td][/tr]
    [tr][td]Window NT[/td]
    [td]La partición de 2 GB en la memoria baja (0x00000000 a 0x7FFFFFFF [2147483648]) está disponible para los procesos y la partición de 2 GB de en la memoria alta (0x80000000 (2147483648) a 0xFFFFFFFF [4294967295]) está reservado para el sistema.[/td][/tr]
    [tr][td]Windows 95 y Windows 98[/td]
    [td]La partición de 4 GB en la memoria baja (0x00000000 a 0x00000FFF [4095]) es usada para compatibilidad con MS-DOS y Windows de 16 bits, la siguiente partición aproximadamente de 2 GB (0x00400000 [4194304] a 0x7FFFFFFF [2147483647] está disponible por los procesos para uso privado, la siguiente partición de 1 GB (0x80000000 [2147483648] a 0xBFFFFFFF [3221225471]) es compartida por todos los procesos de Win32, y la partición de 1 GB en memoria alta (0xC0000000 [3221225472] a 0xFFFFFFFF [4294967295]) es usada por el sistema.[/td][/tr][/table]

    En general los procesos se cargan en la dirección 0x400000 ya que es compatible con todas las versiones de Windows.


    6.3.2 Espacio de Direcciones Virtuales y Almacenamiento Físico

    El espacio de direcciones virtuales es la memoria que hay disponible para cada proceso. Se llama así ya que al usar un archivo en el disco para extender la memoria física (archivo de paginación o swap) el sistema puede crear un "mapa" de memoria idéntico para cada proceso. Esto significa que cada proceso virtualmente tiene acceso a TODA la memoria física e incluso más todavía, y además cada proceso puede escribir en la misma dirección debido a que es virtual, por lo tanto no sobreescribiría lo de otro. En resumen, Windows crea una especie de memoria RAM en el disco rígido para cada proceso.

    El espacio de direcciones virtuales para cada proceso es mucho mayor que el total de memoria física para todos los procesos. Como dije antes, para incrementar el tamaño del almacenamiento físico, el sistema usa el disco para almacenamiento adicional.

    La cantidad total de almacenamiento disponible para todos los procesos que se están ejecutando es la suma de la memoria física y el espacio libre en el disco disponible para el archivo de paginación.

    El almacenamiento físico y el espacio de direcciones virtuales para cada proceso están organizados en páginas, unidades de memoria, cuyo tamaño depende del microprocesador. Por ejemplo, en computadoras x86 el tamaño de página es de 4 KB (4096 bytes).

    Pata maximizar la flexibilidad en la memoria principal, el sistema puede mover páginas de memoria física a y desde el archivo de paginación (swap) en el disco. Cuando la página es movida o modificada en la memoria física, el sistema actualiza el mapa de páginas de los procesos afectados para que los datos de la memoria física y del archivo de paginación sean iguales. Cuando el sistema necesita espacio en la memoria física, mueve las páginas recientemente usadas de memoria física a la swap. La manipulación de la memoria física por el sistema es
    completamente transparente para las aplicaciones, las cuales operan sólo en sus espacios de memoria virtual.


    6.3.3 Estado de las páginas

    Las páginas del espacio de direcciones virtuales de un proceso pueden estar en uno de los siguientes estados:[table][tr][td]Libre[/td]
    [td]Una página libre no está actualmente accesible, pero está disponible para ser encargada o reservada[/td][/tr]
    [tr][td]Reservada[/td]
    [td]Una página reservada es un bloque en el espacio de direcciones virtuales de un proceso que ha sido fijado para usos futuros. El proceso no puede acceder a una página reservada, y no hay almacenamiento físico asociado con esta, o sea que no se aumenta el tamaño de la swap. Una página reservada reserva un rango de direcciones virtuales que no pueden ser usadas subsecuentemente por otras funciones de asignación. Un proceso puede usar la función VirtualAlloc para reservar páginas de su espacio de memoria y luego encargar las páginas reservadas. Luego se puede usar VirtualFree para descargarlas.[/td][/tr]
    [tr][td]Encargada[/td]
    [td]Una página encargada es una para la cual ha sido asignado almacenamiento físico (en la memoria física o en el disco). Puede ser protegida para permitir o no el acceso o el acceso sólo-lectura, o puede tener acceso para lectura y escritura. Un proceso puede usar la función VirtualAlloc para asignar páginas encargadas. Las funciones GlobalAlloc y LocalAlloc asignan páginas encargadas con acceso para lectura-escritura. Una página encargada asignada por VirtualAlloc puede ser "descargada" por la función VirtualFree, con la cual se descargan las páginas almacenadas y cambia el estado de la página a reservada.[/td][/tr][/table]

    6.3.4 Alcance de la memoria asignada
    Toda la memoria que un proceso asigna usando las funciones de asignación de memoria (HeapAlloc, VirtualAlloc, GlobalAlloc, LocalAlloc) son accesibles sólo para dicho proceso. Sin embargo, la memoria asignada por una DLL está asignada en el espacio de direcciones del proceso que ha llamado a la DLL y no está accesible por otros procesos usando la misma DLL. Para crear memoria compartida, hay que usar el mapeado de archivos (objetos file-mapping) que se explican más adelante.

    Esto significa que creando una DLL podemos tener acceso a la memoria cualquier proceso, y esto se usa mucho como método de intección. Por ejemplo, si un proceso carga el archivo Injection.DLL y dentro del código de Injection.DLL se llama a VirtualAlloc, va a asignar memoria en el proceso que cargó esta librería. Otra particularidad es que las funciones GetCurrentProcess(), GetCurrentThread(), GetCurrentProcessId() y GetCurrentThreadId() también devuelven los datos del proceso que cargó a la DLL.

    Fuente
     
  2. ^[GS]^

    ^[GS]^
    GS-Zone Admin Administrador Staff RevoluciónAO

    12.499
    359
    283
    Re: Memoria en Visual Basic 6

    Excelente el aporte Rubio! Muy completo ;)
     
  3. Temisto

    Temisto
    Newbie Lvl 1

    1
    0
    37
    He leido tu publicación y me parece excelente, y eso que aún no la he estudiado a fondo.
    Quisiera saber si alguien dispone de un "mapa" de la memoria de Visual Basic (v.6); me explico.
    En que direcciones se almacenan las variables (y cómo poder acceder a ellas), lo mismo para las funciones y procedimientos, etc.
    Si tuviéseis algún texto (en PDF o similar) en el que se explique la configuración interna del VB6, os lo agradecería.
    Muchas gracias.
     
  4. jouse.-

    jouse.-
    nemesis del mal

    32
    0
    42
    no esta activo pero para ese tipo de cosas tendrias que ver como apunta el sistema operativo la memoria, memoria fisica y memoria virtual, procesos, hilos, etc.
     
  5. 0xDEADBEEF

    0xDEADBEEF
    Vicinity of obscenity Ex-Staff

    7.446
    424
    243
    Eso se puede ver mediante un debugger. Normalmente los IDEs suelen llevarlos ya incorporados, pero en vb6 creo que no hay por defecto. De todos modos, seguro que por internet encontrarás de todo..

    Estaria bueno ver la segunda parte de el tutorial xD, esto en realidad es como aprender ensamblador.
     
  6. Anonymous

    Anonymous
    Invitado

    0
    0
    0
    Excelente, el CopyMemory me cerraba mi aplicación y solo por no poner el byval.
     
  7. ZepTimeL

    ZepTimeL
    Usuario Supremo

    1.144
    3
    93
    Que bestia, se agradece
     
Estado del Tema:
Cerrado para nuevas respuestas

Compartir esta página