Mostrando entradas con la etiqueta mfc. Mostrar todas las entradas
Mostrando entradas con la etiqueta mfc. Mostrar todas las entradas

jueves, 24 de abril de 2008

mfc threads

Empiezo con esto : http://www.pluralsight.com/articlecontent/cpprep0397.htm

Como la programación multihilo es cada vez mas importante en estos días de visitas de Internet y consultas de bases de datos remotas, es importante para comprender plenamente las ramificaciones de la generación adicional de hilos. Esto es particularmente importante para el programador que usa MFC, tanto porque mfc MFC ayuda al programador con las llamadas de bajo nivel del API de Windows, sino que también porque introduce su propio conjunto de peculiaridades en la forma de hacerlo. Aqui, vamos a ver la programación multihilo usando MFC. En primer lugar, vamos a examinar cómo MFC ayuda a la programación multihilo y veremos algunos ejemplos de crear nuevos hilos en una aplicación. A continuación, vamos a ver entre bastidores el MFC para comprender exactamente lo que está sucediendo en cuando genera los MFC hilos. Por último, vamos a discutir algunos posibles efectos secundarios del soporte MFC a la generación de hilos, y la forma de evitar conflictos con la implementación interna de MFC.


HILOS DE TRABAJO( worker threads)


MFC distingue entre dos tipos de hilos, de trabajo e interfaz de usuario (UI) hilos. Los hilos de trabajo, estan planeados para ser usados como hilos secundarios que realizan unidades de trabajo sin conexion con la interfaz de usuario. Esto significa tecnicamente que los hilos de trabajo no tienen asociada una ventana, por lo que no tienen bucle de mensajes definido, por lo que no existe una directa interacion entre un hilo de trabajo y el usuario. Los hilos de trabajo son típicamente usados para realizar largos procedimientos que se realizan asincronamente mientras el resto del programa continua interactuando con el usuario. Ejemplos comunes de hilos de trabajo pueden ser recalculaciones de hojas de cálculo y compilacion de programas en un entorno de desarrollo.

Los hilos de trabajo son lanzados del mismo modo que los hilos estandard de Win32 son lanzados- especificando la función que queremos que se ejecute en el hilo (funcion de trabajo??). MFC provee una funcion de ayuda global que ayuda a lanzar los hilos (sobrecargada para hilos de trabajo y de usuario), que para hilos de trabajo es algo asi:
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
Un ejemplo de un simple hilo de trabajo es:
UINT DoWork(LPVOID pParam)
{
int* count = static_cast(pParam);
for (int i=0; i<*count; i++);
return 0; }...

int* i = new int(10000);

CWinThread* pThread = AfxBeginThread(DoWork, i);

Cuando la funcion AfxBeginThread() se llama con el puntero a funcion DoWork() como un parameter, la funcion DoWork() se ejecutara en el hilo independiente que se ha creado para ella.
Esto significa que el codigo que está corriendo en el hilo actual, se ejecutara simultanemente con el hilo que se ha creado nuevo. Esta es una de las grandes ventajas de la programación multihilo, la habilidad de generar trabajos si interrumpir la ejecución del programa principal o primario. Esta es una particularidad relevante de la programación en Windows (o otros sistemas basados en mensajes) puesto que de realizar procesos pesados sin hilos, perjudicaría severamente la interface de usuario(Que tiene que estar continuamente leyendo mensajes para poder funcionar N.T.).

Hay tambien que recordar, que la programación multithread pese a ser una atractiva solución a muchos problemas, no lo es sin un coste. El mayor coste es usualmente el añadir complejidad a los programas que gestionan múltiples threads, frecuentemente problemas de sincronización.

Ejecutar aplicaciones miltithreads en systemas de un-procesador tambien incurren en problema de rendimiento debido al gasto de recursos relacionados con la gestión de estos nuevos hilos

HILOS DE INTERFAZ DE USUARIO

Los hilos de Interface de usuario, al contrario de los hilos de trabajo , tienen asociado un bucle de mensajes y una ventana , y por lo tanto pueden directamente interactuar con el usuario. Para crear un hilo UI, se tiene que derivar una nueva clase de CWinThread y sobreescribir la función InitInstance() para initializar la ventana que esta asociada con este hilo. Si la ventana es creada en InitInstance(), se ejecutará (residirá) en el proceso hilo y el bucle de mensajes estará en el hilo(hablamos del bucle de mensajes del hilo) en vez del bucle de mensajes principal.
Una vez que uno se ha derivado una clase CWinThread y sobreescrito InitInstance () para crear o asociar una ventana primaria con el hilo, hay otra versión sobrecargada de AfxBeginThread () que pueden utilizarse para generar hilos de interfaz de usuario:
CWinThread* AfxBeginThread( CRuntimClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
Notar, que en lugar de pasar una funcion como puntero al primer parámetro, se pasa un puntero a un objeto CRuntimeClass que referencia a la clase que hemos derivado de CWinThread. Este objeto permite a la función AfxBeginThread() crear una nueva instancia ( por la funcion CreateObject() de CRuntimeClass). Se puede acceder a la estructura de clase runtime usando la macro RUNTIME_CLASS() con el nombre de la clase. Un ejemplo de un hilo UI simple definido y generado es:

class CUIThread : public CWinThread {
DECLARE_DYNCREATE(CUIThread)
public: // Override InitInstance() only
virtual BOOL InitInstance();

};

IMPLEMENT_DYNCREATE(CUIThread, CWinThread)

BOOL CUIThread::InitInstance()
{
CFrameWnd* wnd = new CFrameWnd;
wnd->Create(NULL, "UI Thread Window");
wnd->ShowWindow(SW_SHOW);
wnd->UpdateWindow();
m_pMainWnd = wnd;
return TRUE; }
CWinThread* pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));


Lo que sucede cuando un hilo de la interfaz de usuario es generado, es muy similar a lo que sucede cuando el hilo principal de su aplicación se inicia. La Figura 1 muestra la secuencia de llamadas a funciones CWinThread virtual (que se puede sobreescribir en su clase CWinThread derivada ) hechas por MFC cuando se crea un hilo de la interfaz de usuario.

InitInstance()
|
V
run() ------> onIdle()
|
V
ExitInstance()


Primero, MFC llama a la funcion InitInstance() de nuestra nueva clase derivada de la clase CWinThread. Si resulta true, procede a llamar a la funcion Run() de nuestro objeto, que si no hemos sobreescrito, ejecutara el bucle de mensajes standard y termina cuando recibe el mensaje WM_QUIT. Durante el bucle de mensajes,la función Run() llamará a la función OnIdle() cada vez que detecte tiempo de descanso de proceso (idle-time) en el hilo, cuando Run() retorna, MFC llama al metodo ExitInstance() , dándole la oportunidad de limpiar todos los recursos que nuestro hilo pueda tener asignados.

Podemos sacar ventaja de conocer como son generados los hilo UI, y usar esta idea para diseñar hilos de trabajo. Por ejemplo, podemos derivar una nueva clase de CWinThread para realizar el trabajo que queríamos con el hilo de trabajo dentro delInitInstance(), y entnoce rotornar falso en InitInstance() para indicar a MFC que no deseamos que continue con el bucle de mensajes.

No es necesario usar la MFC funcion AfxBeginThread() cuando crea hilo UI (o worker thread, de este modo). En lugar de esto pude siemplemente generar una instancia de CWinThread (o su derivada) y lanzar el hilo invocando metodo de CWinThread llamado CreateThread(), de esta forma.

BOOL CWinThread::CreateThread(DWORD dwCreateFlags = 0,
UINT nStackSize = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

Un ejemplo de crear una instancia de su CUIThread clase y generar el hilo es:

CUIThread* pThread = new CUIThread;

pThread->CreateThread();
Esto es esencialmente lo que el AfxBeginThread () hace (como veremos), pero este enfoque nos da más control sobre la forma en que el CWinThread derivado de la clase es instanciada.

HilosUI no son comunmente usados como hilos de trabajo en programa MFC. Los hilos UI se usan normalmente en las interfaces de usuario, como la ventana principal, menu, etc., usualmente ocupa el primer hilo de una aplicación. Hilos de trabajo secundarios son lanzandos cuando pesados procesos necesitan hacerse , o chquear e estados asincronos (como monitorizar algun dispositivo), permitiendo al hilo primario(UI) continuar sin parones, y permitiendo a la interface del usuario proceder sin vacilar.

COMO MFC CREA HILOS

Lo primero que debemos de saber de como MFC crea los hilos, es que estan siempre creados con la función _beginthreadex(), Tanto los UI como los de trabajo. El uso de _beginthreadex() (en vez de _beginthread()) dan a MFC mas flexibilidad en como los hilos son creados, incluida la habilidad de poner atributos de seguridad y modificar el estado de ejecución del hilo.


unsigned long _beginthreadex( void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr );


La razón por la que MFC utiliza el _beginthreadex () en lugar de la API de Win32 función CreateThread (), es que evita que las pequeñas fugas de memoria cuando se vayan realizando las llamadas a las bibliotecas de C en tiempo de ejecución.

Lo segundo que necesitamos saber sobre como crea MFC los hilos, es que siempre debemos usar la función CreateThread() de la clase CWinThread para crear los thread que usa MFC(en lugar de invocar el API directamente). La funcion CreateThread puede ser llamada explicitamente on un CWinThread objeto, o implicitamente creado el hilo usando AfxBeginThread().
Si, por ejemplo, queremos generar un hilo llamando a _beginthread() generara un error de assertion tan pronto se haya generado


Para entender como es de crítico que todos los threads usados en MFC tienen que ser llamados usando CWinThread::CreateThread(), podemos tracear los pasos hechos por MFC cuando un nuevo hilo es generado. Lo primero que hace MFC cuando crea un nuevo hilo usando AfxBeginThread (), es crear una nueva instancia de la clase CWinThread (o derivada) . Como lo hace, depende de si es hilo de trabajo o de usuario. Para el hilo de usuario, la forma es la siguiente:
CWinThread* pThread =
new CWinThread(pfnThreadProc, pParam);
Donde pfnThreadProc y pParam son los parametros padados a AfxBeginThread() tal y como se ve arriba.



Para hilos UI , usa la class runtime pasada en la creación de la nueva instancia derivada de CWinThread :
CWinThread* pThread = (CWinThread*) pThreadClass->CreateObject();

Una vez MFC tiene una instancia de una clase CWinThread a trabajando, se
procede a llamar CreateThread () sobre ese objeto que es cuando se genera el hilo.


pThread->CreateThread(dwCreateFlags | CREATE_SUSPENDED,
nStackSize, lpSecurityAttrs);

Dentro de CreateThread(), se hace la llamada a _beginthreadex() - pero la función que hace esta llamada, no es la funcion AfxBeginThread(). MFC añade aqui un nivel de indirecicion qui que le permite una interna inicialización se añada antes que la funcion que genera el hilo sea invocada. La forma de lanzar el hilo es:

m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs,

nStackSize, &_AfxThreadEntry, &startup, dwCreateFlags |

CREATE_SUSPENDED,

(UINT*)&m_nThreadID);

miércoles, 23 de abril de 2008

TIPOS DE VARIABLES DE CARACTER EN VISUAL C++

Los tipos basicos de C++ para caracteres.

En C tenemos el tipo:
char //(un byte)

En C++ añaden el tipo:
wchar_t //(dos bytes)



TIPOS BASICOS EN WINDOWS VISUAL ESTUDIO

http://msdn2.microsoft.com/en-us/library/ms776419(VS.85).aspx

http://msdn2.microsoft.com/es-es/library/c426s321(VS.80).aspx

TIPOS ESPECÍFICOS DE 1 BYTE
CHAR (unsigned char)
typedef unsigned char CHAR; 


LPSTR y LPCH son punteros unsigned char.
typedef CHAR *LPSTR, *LPCH; 


TIPOS ESPECIFICOS 2 BYTE(wide)

WCHAR (unsigned char)
typedef unsigned wchar_t WCHAR; 


LPSTR y LPCH son punteros unsigned wchar_t.
typedef WCHAR *LPWSTR, *LPWCH; 

TIPOS ESPECIFICOS DE COMPATIBILIDAD UNO O DOS BYTES

TCHAR

Si NO usamos 2 bytes TCHAR equivale a char

Si usamos 2 bytes TCHAR equivale a wchar_t

PELIGRO, WARNING,ACHTUNG
NO CONFUNDIR UNICODE CON EL USO DE CARACTERES DE 2 BYTES
PODEMOS USAR UNICODE CON CARACTERES DE 1 BYTE O DE 2 BYTES
PERO MICROSOFT HA DECIDIDO QUE EL UNICODE LO VA A USAR CON CARACTERES DE DOS BYTES.



#ifdef UNICODE // así decimos al compilador que usamos 2 bytes.

typedef wchar_t TCHAR;
#else
typedef unsigned char TCHAR;
#endif

LPTSTR y LPTCH son punteros a TCHAR (char o wchar_t dependiendo si usamos 1 ó 2 bytes)
typedef TCHAR * LPTSTR, *LPTCH;

lunes, 21 de abril de 2008

mfc. Otra introduccion.

Traducido de aqui
Una aplicación básica con MFC.

La librería MFC es un conjunto de tipos de datos, funciones,clases y constantes usadas para crear aplicaciones para Microsoft Windows.
Clase CWinApp. Iniciando una aplicación con MFC.
Lo primero que hay que hacer para empezar un programa es crear una aplicación. En Win32, una aplicación se crea con una llamada a la función WinMain() y crear una estructura WNDCLASS o WNDCLASSEX .
En MFC, lo asume una clase llamada CWinApp(Class-For-A-WIndows-Application).
Para crear una aplicación, tenemos que derivar nuestra propia clase CWinApp.(N.T. Esta clase tiene funciones abstractas por lo que no se puede instanciar).
Una aplicación es algo vacío que sólo permite que el sistema operativo sepa que se va a crear un programa para ejecutar en el ordenador. Pero el programa no hace nada.
El método InitApplication.
Si buscamos hacer algo con nuestro programa, la clase CWinApp, provee el método InitApplication() que tenemos que escribir en nuestra clase.
InitApplication() es un metodo Booleano. Si se crea correctamente, la aplicación retorna TRUE. Si algo salió mal al intentar crear la aplicación, se devolverá FALSE. El esqueleto mínimo de una aplicación es algo parecido a esto:
class CExerciseApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};

BOOL CExerciseApp::InitInstance()
{
return TRUE;
}
Creando el objeto aplicación de la clase CWinApp.
Despues de implementar la aplicacion, para poder utilizarla, hay que crear el objeto de la clase. Es costumbre llamarla "theApp" pero se puede llamar de cualquier forma que queramos.
CExerciseApp theApp;
Incluyendo afxwin.h

Las clases fundamentales de MFC son declaradas en el fichero afxwin.h. Por lo que, esta es la cabecera primaria que tenemos que añadir a cada una de nuestras aplicaciones.
Una aplicacion básica que no hace nada,puede ser creada como sigue:


#include <afxwin.h>

class CExerciseApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};

BOOL CExerciseApp::InitInstance()
{
return TRUE;
}

CExerciseApp theApp;





Introduccion a los marcos(frames).


Como su nombre indica, un marco de una ventana incluye la localización, y las dimensiones de la ventana.
Hay dos tipos de aplicaciones MFC: Las que usan un marco y aquellas que no lo usan.
Una aplicación marco-basada usa el concepto conocido como arquitectura Documento/Vista. Esto permite al marco servir de espacio reservado para otras partes de la aplicación (como el docuemento y la vista).

El marco CFrameWnd
Para crear un marco, la librería MFC provee varias clases. Una de estas es CFrameWnd y es la clase de marco MFC mas comunmente usada. Si deseamos un marco, podemos derivar nuestra propia clase de CFrameWnd de esta forma:


class CMainFrame : public CFrameWnd
{
};

Como puede haber varios marcos o varios tipos de marcos en la aplicación, el primero o marco principal se llama normalmente CMainFrame, podemos seguir este esquema o llamar a la clase como queramos.

Viendo el marco de la ventana.
El esqueleto de nuestro marco solo sirve como cimiento de nuestra clase. Queremos crear un marco de ventana que muestre algo al usuario. Para crear un marco de ventana, la clase CFrameWnd provee el método Create(). Su sintaxis es:

BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
DWORD dwStyle = WS_OVERLAPPEDWINDOW,
const RECT& rect = rectDefault,
CWnd* pParentWnd = NULL, LPCTSTR lpszMenuName = NULL,
DWORD dwExStyle = 0,
CCreateContext* pContext = NULL );

Como podemos ver, los dos únicos argumentos requeridos son el nombre de la clase y el nombre de la ventana. Volveremos a ver todos estos argumentos cuando se estudien las clases de ventana con más detalles. Por el momento, un mínimo marco puede ser crado simplemente pasando el nombre de la clase com NULL y el nombre de la ventana como un string c-null. Esto es un ejemplo.

class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
};

CMainFrame::CMainFrame()
{
Create(NULL, "MFC Fundamentals");
}

Para proveer de una ventana a la aplicacion, tenemos que crear un hilo(thread). Esto puede hacerse con la clase CWinThread. Para hacer esto mas facil, CWinThread esta equipada con una variable miembro publica llamada m_pMainWnd. Esta variable puede ser usada para crear un hilo para la ventana principal de la aplicación. Una de sus ventajas es que hace seguro que nuestra aplicación termina suavemente cuando el usuario decide cerrarla.(N.T. ????).

CWinThread es la clase base de CWinApp y por lo tanto hace m_pMainWnd visible a cualquier clase CWinThread derivada como CFrameWnd. Basandonos en esto, para crearun hilo para visualizar la ventana principal, podemos asignar un puntero de nuestra clase cuadro a m_pMainWnd. Despues de este asignamiento, m_pMainWnd puede ser usado como un objeto ventana para visualizar el cuadro, que se hace habitualmente con el método ShowWindow() . El programa nos queda de esta manera:

BOOL CExerciseApp::InitInstance()
{
m_pMainWnd = new CMainFrame;
m_pMainWnd->ShowWindow(SW_NORMAL);

return TRUE;
}