Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)
Mostrando entradas con la etiqueta Windows Api. Mostrar todas las entradas
Mostrando entradas con la etiqueta Windows Api. Mostrar todas las entradas

miércoles, 16 de junio de 2021

Las Apis Copyfile y Shellexecute, y sus Equivalentes en .Net Con C#

Otra entrada migrada de la vieja web :)

Después de mucho programar Apis en Visual Basic 6, se me hizo difícil dejar esa costumbre al pasar al C Sharp. Con la plataforma .Net,  ya no son necesarias muchas de las llamadas a Apis, pero dado que el Visual Basic 6 está tan bien documentado suele ser más sencillo hallar en internet cómo hacer algo usando una Api que usando .Net. Lo malo: llamar a las apis hace depender nuestras aplicaciones del sistema operativo Windows.  

Con ShellExecute para C Sharp:

En .Net tenemos la clase Process que hace todo lo que hace la api ShellExecute así que no tendríamos porqué necesitarla más. Por ejemplo, para ejecutar un programa, se pone:

Process p = new Process();
p.StartInfo.FileName = "iexplore.exe";
p.StartInfo.Arguments = "http://www.microsoft.com";
p.Start();


o abrir el Notepad:

Process p = new Process();
p.StartInfo.FileName = "notepad.exe";
p.Start();


Si quiero abrir un archivo el código sería:

Process pr = new Process();
try
{
    pr.StartInfo.FileName = @"C:\un_archivo_cualquiera";
    pr.Start();
}
catch (NullReferenceException ex)
{
    MessageBox.Show(ex.Message, "Error");
}


Que reemplaza a este otro código (dentro de una clase):

class executeIt{

/*
Public Declare Function ShellExecute _
Lib "shell32.dll" _
Alias "ShellExecuteA" ( _
ByVal hwnd As Long, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long) _
As Long
*/

    [System.Runtime.InteropServices.DllImport("shell32.dll", EntryPoint = "ShellExecute")]
    public static extern int ShellExecuteA(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory,
    int nShowCmd);
}



Lo que está comentado es la llamada a la api para Visual Basic 6, debajo está la "traducción" para C Sharp. Así se puede ir deduciendo cómo "traducir" el código que llama apis para VB6 a C Sharp.

Para usar la api se hace:

executeIt.ShellExecuteA(0, "OPEN","E:\\mundodisco_1-27.zip","", "", 0);
executeIt.ShellExecuteA(0, "OPEN","F:\\Músi-k","", "", 1);


La primera línea abre mis libros zipeados del Mundodisco de Terry Pratchett. La segunda abre mi carpeta con mis mp3 descargados (y de paso dejo información sobre las particiones de mi disco duro).

Si en Visual Basic 6 las variables se declaraban como Long, en C Sharp su equivalente es Int. Esto es debido a cuántos bytes ocupan estas variables en cada lenguaje. En esta página se puede ver una comparación de los tipos de variables, su tamaño y otras características.

Usando la api CopyFile:

Si se compila el siguiente código:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        /* Public Declare Function CopyFile Lib "kernel32.dll" Alias
        * "CopyFileA" (ByVal lpExistingFileName As String,
        * ByVal lpNewFileName As String, ByVal bFailIfExists As Long) As Long
        */


        [System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint = "CopyFile")]
        public static extern int CopyFileA(string lpExistingFileName, string
        lpNewFileName, int bFailIfExists);

        private void replicar()
        {
            int count, rad;
            string copia = " ";
            string nombre_archivo = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;

            // Fuente: http://blogs.msdn.com/brada/archive/2004/04/14/113620.aspx  por Luc Cluitmans

            count = 0;
            System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(@"C:\");
            foreach (System.IO.FileInfo file in dir.GetFiles("CopyIt*.exe"))
                count++; // si es cero es que no existe el archivo.

            for (int i = count; i <= count * 2+1; i++)
            {
                copia = String.Concat("C:\\", "CopyIt", i.ToString(), ".exe");
                rad = CopyFileA(nombre_archivo, copia, 0);
            }
        }
    
        static void Main(string[] args)
        {
            Program pp = new Program();
            pp.replicar();
        }
    }
}


se tendrá un programa que simplemente busca cuántas copias existen en C:\ (los archivitos "CopyIt*.exe") y luego con un bucle se copia el doble de veces más uno. No interesa cómo se llame el programa original o dónde se encuentre, siempre sus copias terminarán en C:\ y siempre contará cuántas de ellas existen. Así, después de unos pocos clicks, el disco duro (poniendo el ícono apropiado) se verá así:


Y como todos son el mismo programa, se puede ejecutar cualquiera de ellos y harán lo mismo: copiarse y copiarse.

Y ahora, su equivalente para .Net:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        private void replicar()
        {
            int count;
            string copia = " ";
            string nombre_archivo = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
            // Fuente: http://blogs.msdn.com/brada/archive/2004/04/14/113620.aspx  por Luc Cluitmans
            count = 0;

            DirectoryInfo dir = new DirectoryInfo(@"C:\");
           
            foreach (FileInfo file in dir.GetFiles("CopyIt*.exe"))
                count++; // si es cero es que no existe el archivo.

            for (int i = count; i <= count * 2+1; i++)
            {
                copia = String.Concat("C:\\", "CopyIt", i.ToString(), ".exe");
                File.Copy(nombre_archivo, copia);
            }
        }

        static void Main(string[] args)
        {
            Program pp = new Program();
            pp.replicar();
        }
    }
}


Si se usan las rutas al estilo Visual basic en C Sharp se debe poner una arroba antes para indicarle que es una ruta de directorio, pues el formato de las rutas a los directorios es:

Rutas de archivos en Visual Basic: por ejemplo: "c:\mi archivo"
Rutas de archivos en C, C++ y C Sharp: por ejemplo: "c:\\mi archivo"

lunes, 17 de agosto de 2020

Dibujando la espiral de Fermat en Visual C++ 2017

Tomando como base esta aplicación https://programacionamartillazos.blogspot.com/2020/07/ejemplo-del-uso-de-la-api-de-windows.html quise crear la espiral de Fermat en Visual C++.

Primero se debe crear un nuevo proyecto de C++ para Escritorio de Windows:

 

En la función WndProc se deben declarar las siguientes variables:

 

Intenté que la espiral se dibujara al crear la ventana o activarla, pero sólo obtenía una ventana toda negra, éste es un detalle que no he podido resolver, así que decidí dibujar la espiral al hacer clic. Para esto se debe poner el siguente código dentro de la sentencia switch, de modo que la ventana también recibirá los eventos (mensajes) que se disparan al hacer clic con el botón izquierdo del ratón:


Lo que hace el código es obtener las coordenadas (x.y) a partir de la fórmula de la espiral de Fermat y dibujar en ellas un pequeño recuadro. En windows no hay una función para dibujar puntos, pero éstos se pueden emular dibujando rectángulos o círculos muy pequeños. El código después del bucle es para copiar el dibujo a un mapa de bits en memoria. Después de algunas pruebas, hallé un incremento y una cantidad de puntos tales que permiten ver cómo se dibuja la espiral en tiempo real:

El resto del código es para que tremenda espiral no se borre al minimizar y maximizar la ventana, el mapa de bits con la espiral se copia a la ventana en el evento repaint (mensaje WM_PAINT):


Ya con este código, y cambiando las fórmulas, se pueden dibujar otras espirales, como la de Doppler:

 

Y ya poniendo otras funciones (simplemente mezclando senos, cosenos y tangentes), conseguí dibujar cosas más interesantes:


El proyecto completo puede descargarse de aquí. Incluye más dibujos :)

martes, 21 de julio de 2020

Ejemplo del uso de la Api de Windows para dibujar rectángulos: api Rectangle()

El código que voy a mostrar a continuación está basado en este tutorial:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd145184%28v=vs.85%29.aspx
pero hace algo mucho más simple: dibuja cuadraditos en donde se hace click con el cursor:



Como estoy usando la api de Windows, recomiendo leer este otro tutorial para entender el bucle de mensajes y qué significa cada pedazo de código que voy a presentar.

Windows tiene varias funciones para dibujar figuras gemétricas y texto, en este caso voy a usar la api Rectangle(), la cual recibe como parámetros el identificador o "handle" de dónde se va a dibujar y las coordenadas de las cuatro esquinas del rectángulo.
El handle es un número que identifica la "superficie de dibujo", en este caso es nuestra ventana, para obtenerlo tenemos otra api: GetDC().
El evento que nos interesa es cuando se hace clic con el botón izquierdo, en el bucle de mensajes de la ventana es el mensaje WM_LBUTTONDOWN:



La variables necesarias son:


Se declaran estáticas para que sus valores sean persistentes y no sea necesario inicializarlas.

Las coordenadas del cursor se obtienen con la api GetCursorPos() que obtiene las coordenadas con respecto a la esquina superior de la pantalla. Para convertirlas a corrdenadas sobre la ventana se obtiene la ubicación de ésta con si sin la barra de título. para esto se usan las apis GetWindowRect() y GetClientRect(). El objeto HPEN sirve para indicar de qué color y con qué relleno queremos los rectángulos. Éstos se dibujan con la api Rectangle(). la Api ReleaseDC() es para liberar la memoria usada.

La vatiable hscreen es del tipo HDC y se coló en el código pues no se usa. Está obteniendo el handle del escritorio de Windows, el cual Windows devuelve al pasarle cero a la api GetDC().

Lo malo es que al minimizar la ventana, o taparla por otra, y volverla a mostrar, se borra lo que hemos dibujado. Para evitarlo se debe copiar la imagen de la ventana y pegarla en el evento repaint que para la api de Windows es el mensaje WM_PAINT.


Primero declaro otras dos variables:

 
Y en los mensajes WM_LBUTTONDOWN y WM_PAINT añado el siguiente código (en el mensaje WM_LBUTTONDOWN va después de llamar a la api Rectangle()):


Lo que hace el código añadido es, cuando se hace clic, crear un mapa de bits en memoria y copiar en él la imagen que se muestra en la ventana. Al repintarse la ventana se copia el mapa de bits en memoria de vuelta a la ventana, haciendo que parezca que no se ha borrado. Para copiar mapas de bits en ventanas, memoria o viceversa, uso la api BitBlt().


miércoles, 5 de junio de 2019

Tutorial De Win32 Y Ejemplo Del Uso De Las Apis Bitblt, Getdibits Y Setdibits En Visual C++ 2008 Express


Este es otro tutorial viejito migrado desde la web Electrónica a Martillazos :D

En este tutorial la idea es manipular imágenes y mostrarlas en una ventana usando solamente apis de Windows. La manera más fácil es empleando el Visual C++ de Microsoft, el cual ya instala los archivos de encabezado necesarios y genera automáticamente el código para crear una ventana vacía.

Primero abrimos el Visual C++ (yo uso la versión 2008 Express), elegimos Nuevo Proyecto->Win32->Proyecto de Win32. A mi proyecto le he puesto el nombre de "FirstTry".

 

Como tipo de aplicación elegimos "Aplicación para Windows". Por ser la versión Express, no nos permite usar las librerías MFC ó ATL, pero no importa porque no las vamos a necesitar (y yo tampoco sé usarlas :P)




Al presionar el botón de "Finalizar", el Visual C++ nos crea varios archivos, con extensión .cpp y extensión .h. Donde debemos mirar es el código del archivo cpp que tiene el mismo nombre de nuestro proyecto. Para este caso es FirstTry.cpp. Con lo primero que nos encontramos es que el Visual C++ ha generado casi doscientas líneas de código. Todo ese código es necesario para mostrar una sola ventana, simple y vacía. Para entender en detalle qué significa, recomiendo este tutorial: The Forger's Win32 Api Tutorial.


Como resumen, voy a explicar un poco qué hace este código:


Hay cuatro funciones a las qué prestar atención: MyRegisterClass, InitInstance, WndProc y _tWinMain.


MyRegisterClass: En esta función se establecen las características de la ventana: colores, íconos, etc. También se le envía el nombre de la función (WndProc) donde se procesarán los eventos que ocurran en la ventana (al redimensionarla, maximizarla, hacerle click, cerrarla, etc).


InitInstance: Nuestro programa en ejecución (que por ahora sólo muestra una ventana vacía) se le conoce como instancia. Una aplicación puede tener varias instancias: por ejemplo, dos ventanas del Block de Notas abiertas son dos instancias de la aplicación Block de Notas. La función InitInstance inicializa la instancia actual de nuestra aplicación, crea la ventana llamando a la api CreateWindow y retorna un valor Verdadero o Falso dependiendo si la ventana pudo crearse con éxito o no.


WndProc: Aquí se procesa todo lo que ocurre en la ventana. Ese "todo lo que ocurre" se conoce como Eventos, y pueden ser desde entradas del teclado o clicks del ratón, hasta el cierre de la ventana y el final de la instancia de la aplicación.


_tWinMain: Función principal y desde dónde arranca nuestra aplicación. Esta función llama a MyRegisterClass y a InitInstance. Si todo sale bien, entrará al bucle de mensajes. Como en MyRegisterClass ya se asoció a la función WndProc con la ventana actual, no es necesario llamarla desde _tWinMain, de esto se encarga automáticamente el bucle de mensajes. Cuando ocurre un evento, la ejecución del programa "salta" a WndProc junto con un número que identifica al evento ocurrido.





Dentro de WndProc siempre hay un "switch" que evalúa el valor que acompaña a los eventos lanzados, de esta forma se puede identificar al evento y ejecutar el código correspondiente:




¿Complicado? ¡Claro que sí! Comparado con el Form_Load de Visual Basic o C#, esto es una pesadilla. Pero es así como el sistema operativo Windows maneja las ventanas de TODAS las aplicaciones. En Windows, no importa si se programó en .Net, VB6, Java o Borland C++, Delphi... las librerías que usan estos lenguajes no son más que "envolturas" (wrappers) de la api de Windows. En sus profundidades, todas las aplicaciones de Windows que tengan ventanas terminan en un bucle de mensajes.


Nota: Nótese que repito "Windows" muchas muchas veces, pues este tutorial es exclusivamente para este sistema operativo. En Linux las ventanas se crean con librerías como GTK, las cuales también poseen un bucle, pero las funciones y la lógica son totalmente distintas.


Para los ejemplos en este tutorial el evento que nos interesa es WM_PAINT, el cual se lanza cada vez que se maximiza o minimiza la ventana, cuando se la redimensiona, o se la vuelve a mostrar luego de estar tapada por otra ventana, en otras palabras, cada vez que se la "repinta".



Primer Ejemplo: Mostrar en la ventana una imagen en formato BMP.

Para cargar una imagen en el proyecto, debemos ir a donde dice Archivos de Recursos, hacer click derecho->Agregar->Elemento existente. Se abrirá una ventana desde la que elegiremos una imagen en formato BMP. En este caso he elegido un archivo llamado p1.bmp.




Luego le hacemos click derecho al archivo FirstTry.rc->Ver Código y añadimos la siguiente línea donde se declara el nombre con el que identificaremos a la imagen p1.bmp:




Luego vamos a Resources.h y definimos un valor único para el nombre que identifica a la imagen p1.bmp:


Estos pasos se deben repetir para cada imagen que se carga al proyecto. Para este ejemplo he cargado una segunda imagen llamada "IDI_CMC" y cuyo valor en Resources.h es 100.


A continuación vamos a FirstTry.cpp y, en la función WndProc agregamos el siguiente código en el "case" de WM_PAINT:



Este código crea un "handle" a la imagen ya definida en los archivos de recursos. Luego se crea un "Device Context" llamado hdcMem el cual se "enlaza" al handle de la imagen. Los "Device Context" (Dispositivos de Contexto) son áreas de memoria donde podemos dibujar y copiar imágenes. Luego se llama a la api GetObject para obtener las dimensiones de la imagen, éstos valores se guardarán en el objeto BITMAP llamado bm.


La imagen está cargada en algún lugar de la memoria RAM. Para que aparezca en la ventana debemos copiarla. Eso se hace con la api BitBlt cuyos parámetros son: destino (el handle del Device Context de la ventana, y que es el valor devuelto por la api BeginPaint y guardado en la variable hdc), coordenada "x" donde se ubicará la esquina superior izquierda de la imagen copiada, coordenada "y" donde se ubicará la esquina superior izquierda de la imagen copiada, ancho de la imagen, alto de la imagen, handle del Device Conext con la imagen a copiar, coordenada "x" de la esquina superior izquierda de la imagen a copiar, coordenada "y" de la esquina superior izquierda de la imagen a copiar, operación de copiado. Más información de esta api aquí.


Luego de terminar de utilizar los handles, bitmaps y demás objetos, se los debe borrar de la memoria. Al programar directamente con la Api de Windows usamos código no administrado. Es decir: no hay un recolector de basura que libere la memoria. Si una función en código no administrado no libera todos los recursos utilizados tendrá "fugas de memoria" (memory leaks) las cuales se irán acumulando cada vez que se llame a la función. En el peor de los casos terminará agotando la memoria asignada a nuestra aplicación y haciendo que se cuelgue.

Al ejecutar la aplicación se mostrará la imagen en la ventana. Cada vez que ésta se "repinte" ejecutará el código que vuelve a cargar y copiar la imagen. Si no fuera así, la imagen desaparecería si se minimiza y maximiza la ventana, se la redimensiona, etc.

El resultado final es:




Segundo Ejemplo: Copiar una imagen de forma que cubra toda la ventana.

Para tener el código de forma más ordenada, vamos a crear un nuevo archivo cpp y llamar sus funciones desde el "case" WM_PAINT en WndProc. Para ello hacemos click derecho en "Archivos de Código Fuente" y elegimos la opción Agregar->Nuevo Elemento:



En la ventana de "Agregar Nuevo Elemento" elegimos la opción Código->Archivo C++: 




Esto creará un archivo con extensión cpp en blanco. Primero le agregamos las referencias a los archivos de encabezado stafx.h y el que lleva el nombre del proyecto: 



Luego, en el archivo de encabezado con el nombre del proyecto, se añaden las declaraciones de las funciones que irán en el nuevo archivo cpp (para este caso lo he llamado Image.cpp). La función que dibujará y copiará una imagen en toda la ventana se llamará ImagenCopiar. En general, las funciones que dibujan en una ventana necesitan 3 parámetros: La instancia actual de la aplicación (para cargar la imagen dentro de un recurso), el handle de la ventana (hWnd) y el handle del Device Context (hdc) de la ventana (donde copiaremos la imagen).



A continuación muestro el código de ImagenCopiar. Buena parte del código simplemente se copió del ejemplo anterior, mas se han añadido la declaración de un objeto RECT y la llamada a la api GetWindowsRect; esto es para poder obtener las dimensiones de la ventana. Las dimensiones de la imagen se guardan en el objeto BITMAP llamado bm. Para este ejemplo he usado la imagen llamada p1.bmp declarada como IDB_BITMAP1.



El código incluye dos bucles anidados, los que recorren el ancho y el alto de la ventana en incrementos iguales al ancho y al alto de la imagen a copiar, la cual es mucho más pequeña que la ventana donde va a mostrarse. Dentro de los bucles se va alternado una llamada a la api BitBlt que copia exactamente la imagen a la ventana, y otra llamada a la misma api que copia la imagen pero con los colores invertidos. El resultado es:





Tercer Ejemplo: Manipular los píxeles de una imagen. Uso de las apis GetDIBits y SetDIBits.

Para este ejemplo declaramos una segunda función llamada ImagenManipular, la cual recibe los mismos parámetros que ImagenCopiar, convertirá la imagen p1.bmp a escala de grises y la mostrará en la ventana de la aplicación.




Para poder procesar individualmente cada píxel de p1.bmp, debemos extraerlos, guardarlos temporalmente en un array, realizar las operaciones necesarias con ellos, y devolverlos a una nueva imagen la cual se mostrará en la ventana.

Cada píxel de una imagen está compuesto de 3 ó 4 bytes que corresponden a los colores rojo, verde y azul, más el canal alpha que indica la transparencia, el cual no es usado en todos los formatos de imagen. En este ejemplo sólo se trabajará con los valores de los colores. Para el canal alpha existen otras apis como TransparentBlt ó AlphaBlend (la última es la más recomendable de usar) pero no nos ocuparemos de ellas en este ejemplo.


El código de ImagenManipular es el siguiente:




Usando la api GetDIBits extraemos los píxeles de la imagen, como esta api también necesita de una "cabecera" donde esté la información de la imagen (bits por píxel, dimensiones, compresión, etc), esta información la guardamos en las variables tipo BITMAPINFO, una para la imagen original y otra para la imagen ya convertida a escala de grises.. Las dimensiones de p1.bmp las obtenemos con la api GetObject. Leyendo tutoriales y otros códigos de ejemplo, supe que a las variables BITMAPINFO se les establece la altura de la imagen como un valor negativo ya que su sistema de coordenadas para la altura está invertido.


Las dos imágenes (la original y la que estará en escala de grises) necesitan su propio handle y su propio Device Context.

Para recorrer los píxeles de la imagen uso dos punteros: uno que siempre apuntará al inicio del array (buf2), y otro (buf) que lo recorrerá incrementando su valor. Luego coloco los píxeles en otra imagen mediante la api SetDIBits, la cual requiere también otra variable tipo BITMAPINFO para "construir" la nueva imagen en escala de grises.

SetDIBits crea (o "dibuja") la imagen en escala de grises en un espacio en memoria. Aún falta colocarla en la ventana para poder verla. Esto se hace con una simple copia usando la api BitBlt.


El resultado es:





Si llamo a la función ImagenCopiar y luego a ImagenManipular el resultado será:





Esto es porque ImagenManipular copia a p1.bmp encima de lo que ha sido dibujado por ImagenCopiar.


Una imagen cualquiera posee mucha más información que sólo los píxeles que la componen. Todos los archivos digitales poseen una cabecera que le dice al sistema operativo o a una aplicación qué tipo de archivo es. En el caso de imágenes, la cabecera almacena información de los bits por píxel, dimensiones, formato, tipo de compresión etc. E el caso de archivos jpg, también se almacena la Data Exif, que contiene información del dispositivo de captura (como una cámara digital), el software de edición, la fecha de creación, etc.  Las apis GetDIBits y SetDIBits nos permiten obtener sólo los píxeles de una imagen y guardarlos en un array para después procesarlos.


El proyecto con los tres ejemplos se puede descargar de aquí. Se compila con Visual C++ Express 2008.


Un detalle más: el código de estos ejemplos no está optimizado. Lo ideal es que las imágenes procesadas se guarden en un Device Context global y simplemente copiarlo al Device Context de la ventana cada vez que ésta se repinta.

sábado, 14 de diciembre de 2013

Cómo Convertir de long a LPCWSTR (puntero largo a Wide String constante) en Visual C++

Todo empezó cuando quise aprender a usar las apis GetWindowRect y GetClientRect. La primera devuelve las coordenadas que ocupa la ventana, la segunda devuelve las coordenadas que ocupa la ventana sin la barra de título.

Como práctica, quise calcular el alto de la barra de título de una ventana.

El valor calculado es del tipo long. Luego debía convertirlo a LPCWSTR para pasarlo como parámetro a la api setWindowText que lo escribirá en lugar del título de la ventana.

He usado la información que hallé en estas webs:
La función ltoa (reemplazada por _ltoa)
Convertir de char* a LPCWSTR
La función MultiByteToWideChar

El código es el siguiente:

RECT r1, r2;

GetWindowRect(hWnd, &r1);

GetClientRect(hWnd, &r2);

long alto1 = r1.bottom - r1.top;

long alto2 = r2.bottom - r2.top;

long alto3 = alto1-alto2;

// convertir long a Wide WCHAR*

int size=sizeof(long)*8+1;

char* texto = new char[size];

WCHAR* texto2 = new WCHAR[size];

_ltoa(alto3,texto,10);

MultiByteToWideChar(CP_UTF8, 0, texto, -1, texto2, size);

// fin de la conversión. Para convertir de WCHAR* a LPCWSTR (puntero largo a Const Wide String) basta un casteo

SetWindowText(hWnd, (LPCWSTR)texto2);


En el caso de una aplicación tipo win32, coloqué este código dentro de la función que procesa los mensajes de la ventana (WndProc), exactamente en el mensaje que se envía al crearla:


Al ejecutar el programa, mostrará el alto de la barra de título en el texto de... la barra de título.


miércoles, 23 de octubre de 2013

Cómo evadir la limitación de tamaño de una ventana en Windows

Por diseño, Microsoft Windows limita el tamaño de las ventanas a (en píxeles):
(12 + ancho de la resolución de pantalla) x (12 + alto de la resolución de pantalla)

Más información en StackOverflow.

Por allí hallé quienes afirman que se puede superar esta restricción. Y entonces me topé con este código, de la misma Microsoft, en Visual Basic 6.

Lo copio aquí para que no se vaya a perder:

En un módulo bas:

 Option Explicit

      Private Const GWL_WNDPROC = -4
      Private Const WM_GETMINMAXINFO = &H24

      Private Type POINTAPI
          x As Long
          y As Long
      End Type

      Private Type MINMAXINFO
          ptReserved As POINTAPI
          ptMaxSize As POINTAPI
          ptMaxPosition As POINTAPI
          ptMinTrackSize As POINTAPI
          ptMaxTrackSize As POINTAPI
      End Type

      Global lpPrevWndProc As Long
      Global gHW As Long

      Private Declare Function DefWindowProc Lib "user32" Alias _
         "DefWindowProcA" (ByVal hwnd As Long, ByVal wMsg As Long, _
          ByVal wParam As Long, ByVal lParam As Long) As Long
      Private Declare Function CallWindowProc Lib "user32" Alias _
         "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
          ByVal hwnd As Long, ByVal Msg As Long, _
          ByVal wParam As Long, ByVal lParam As Long) As Long
      Private Declare Function SetWindowLong Lib "user32" Alias _
         "SetWindowLongA" (ByVal hwnd As Long, _
          ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
      Private Declare Sub CopyMemoryToMinMaxInfo Lib "KERNEL32" Alias _
         "RtlMoveMemory" (hpvDest As MINMAXINFO, ByVal hpvSource As Long, _
          ByVal cbCopy As Long)
      Private Declare Sub CopyMemoryFromMinMaxInfo Lib "KERNEL32" Alias _
         "RtlMoveMemory" (ByVal hpvDest As Long, hpvSource As MINMAXINFO, _
          ByVal cbCopy As Long)

      Public Sub Hook()
          'Start subclassing.
          lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _
             AddressOf WindowProc)
      End Sub

      Public Sub Unhook()
          Dim temp As Long

          'Cease subclassing.
          temp = SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc)
      End Sub

      Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, _
         ByVal wParam As Long, ByVal lParam As Long) As Long
          Dim MinMax As MINMAXINFO

          'Check for request for min/max window sizes.
          If uMsg = WM_GETMINMAXINFO Then
              'Retrieve default MinMax settings
              CopyMemoryToMinMaxInfo MinMax, lParam, Len(MinMax)

              'Specify new minimum size for window.
              MinMax.ptMinTrackSize.x = 200
              MinMax.ptMinTrackSize.y = 200

              'Specify new maximum size for window.
              MinMax.ptMaxTrackSize.x = 500
              MinMax.ptMaxTrackSize.y = 500

              'Copy local structure back.
              CopyMemoryFromMinMaxInfo lParam, MinMax, Len(MinMax)

              WindowProc = DefWindowProc(hw, uMsg, wParam, lParam)
          Else
              WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, _
                 wParam, lParam)
          End If
      End Function


El truco es fácil: donde están los números en rojo, escribimos nuestros números enormes favoritos. Yo le puse por código a una ventana (Form) dimensiones de 2050*1600 píxeles, en una resolución de pantalla de 1024*768 (la restricción por defecto sería 1036*780).

Para hacer esto en el form ponemos:

      Option Explicit

      Private Sub Form_Load()
          'Save handle to the form.
          gHW = Me.hwnd

          'Begin subclassing.
          Hook 
          ' le ponemos un tamaño ENORME 
          Me.Height = 1600
          Me.Width = 2050
      End Sub

      Private Sub Form_Unload(Cancel As Integer)
          'Stop subclassing.
          Unhook
      End Sub 


Y funcionó.

Ya no intenté con dimensiones mayores porque una ventana con aproximadamente el doble de alto y ancho que la resolución de pantalla parece ser suficientemente grande. Deduzco que el máximo que puede aguantar el sistema operativo depende de la memoria máxima que le puede asignar a semejante ventanón y antes que la RAM empiece a echar humo.

Como son puras llamadas a Apis de WIndows, se puede traducir este código a Visual C++ o a .Net usando DLLImport.

jueves, 11 de abril de 2013

VB6: Cómo saber si existe una webcam conectada a la PC

Primero declaramos las funciones SendMessage y capCreateCaptureWindow (si se declaran en un módulo .bas las declaraciones deben ser públicas, para este ejemplo asumiremos que todo el código está dentro de un Form):

Private Declare Function capCreateCaptureWindow Lib "avicap32.dll" _
    Alias "capCreateCaptureWindowA" ( _
    ByVal lpszWindowName As String, _
    ByVal dwStyle As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal nWidth As Long, _
    ByVal nHeight As Long, _
    ByVal hwndParent As Long, _
    ByVal nID As Long) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
    ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long


Private Const CONNECT As Long = 1034


El código que nos dice si hay una webcam conectada es:

Dim mCapHwnd as Long

mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, 320, 240, Me.hwnd, 0)
DoEvents

If SendMessage(mCapHwnd, CONNECT, 0, 0) <> 0 Then
     
MsgBox "Se ha detectado una webcam conectada y lista para usarse"
Else
      MsgBox "No se ha detectado una webcam"
End If



Basado en este código de Recursos Visual Basic

lunes, 11 de marzo de 2013

Visual C++: Cómo hacer que una ventana aparezca maximizada al cargarla

Para una aplicación tipo Win32 se debe buscar en la función Initinstance la línea donde dice ShowWindow:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   hInst = hInstance; // Almacenar identificador de instancia en una variable global

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)  
      return FALSE;   


  // Visual C++ nos crea esta línea:
  // ShowWindow(hWnd, nCmdShow);

  // debemos reemplazarla por esta otra línea:
   ShowWindow(hWnd, SW_MAXIMIZE);
   UpdateWindow(hWnd);
   return TRUE;
}


Los valores del parámetro que se le pasa a la api ShowWindow están declarados en WinUser.h y son los siguientes:

#define SW_HIDE             0
#define SW_SHOWNORMAL       1
#define SW_NORMAL           1
#define SW_SHOWMINIMIZED    2
#define SW_SHOWMAXIMIZED    3
#define SW_MAXIMIZE         3
#define SW_SHOWNOACTIVATE   4
#define SW_SHOW             5
#define SW_MINIMIZE         6
#define SW_SHOWMINNOACTIVE  7
#define SW_SHOWNA           8
#define SW_RESTORE          9
#define SW_SHOWDEFAULT      10
#define SW_FORCEMINIMIZE    11
#define SW_MAX              11