|
Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.
Temas - Eternal Idol
26
« en: Viernes 7 de Julio de 2006, 19:49 »
Bueno, hoy me llego el ultimo numero de NT Insider y me entere ya oficialmente de algo que se veia venir de hace rato: el SoftIce murio.
¿Alguien lo usaba? En fin, una pena, x64 y el PatchGuard fueron demasiado para Compuware.
27
« en: Martes 27 de Junio de 2006, 22:51 »
La camiseta pesa señores, lo crean o no, la camiseta pesa. Mi mas sentido pesame a la seleccion española, hasta Ucrania llego mas lejos
28
« en: Sábado 17 de Junio de 2006, 11:51 »
29
« en: Jueves 15 de Junio de 2006, 16:21 »
De un tiempo a esta parte note que ya no vemos la hora en que fueron hechos los mensajes sino que solo la fecha. ¿Esto es por algo en particular? Si no es problema yo preferia la version antigua
30
« en: Miércoles 19 de Abril de 2006, 16:53 »
En otro foro alguien dijo que Pascal (Delphi) generaba ejecutables mucho mas pequeños y rapidos que VC++. Espero que esto lo contradiga: simple.cpp: #include <windows.h> #define mainDlg 101 #define okButton 500 HWND mainDlgHandle; int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); int RealEntry() { return WinMain(GetModuleHandle(0), 0, 0, 0); } INT_PTR CALLBACK mainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HICON mainIcon = 0; switch (uMsg) { case WM_CLOSE: EndDialog(hwndDlg, wParam); return TRUE; case WM_INITDIALOG: mainDlgHandle = hwndDlg; mainIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(101)); SetClassLong(mainDlgHandle, GCL_HICON, (LONG)mainIcon); DestroyIcon(mainIcon); break; case WM_COMMAND: switch (LOWORD(wParam)) { case okButton: SendMessage(hwndDlg, WM_CLOSE, 0, 0); break; } break; } return 0; } int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBox(hInstance, MAKEINTRESOURCE(mainDlg), 0, mainDlgProc); }
El script de recursos, about.rc: #include <windows.h> 101 DIALOGEX 0, 0, 229, 50 STYLE DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About ..." LANGUAGE LANG_SPANISH, 0x1 FONT 8, "MS SHELL DLG", 0, 0, 1 { CONTROL "Programa de ejemplo v0.1", 0, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 12, 91, 11 CONTROL "Aceptar", 500, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 157, 7, 60, 14 } 101 ICON MAINICON.ico
Si ponemos un icono de 1024 bytes obtenemos con estas lineas un ejecutable de 3072 bytes: rc about.rc cl /c simple.cpp link simple.obj /NODEFAULTLIB user32.lib kernel32.lib /entry:RealEntry about.res /merge:.rdata=.text
31
« en: Miércoles 24 de Agosto de 2005, 17:03 »
Bueno, para los que se quieran anotar a este muy ambicioso proyecto (cuyo objetivo es infimo a comparacion de un Sistema Operativo profesional) que me manden un privado con su mail (preferentemente Messenger). Cualquier duda o sugerencia ponganla aca asi la ve todo el mundo
32
« en: Jueves 1 de Julio de 2004, 10:58 »
Crear nuestra propia Run Time Library (Tercera Parte) En la MSDN encontré algo interesante sobre la opción /ENTRY del linker: "Type a function name in the Entry-Point Symbol text box (or in the function argument on the command line). The function must be defined with the __stdcall calling convention. The parameters and return value must be defined as documented in the Win32 API for WinMain (for an .EXE file) or DllEntryPoint (for a DLL). It is recommended that you let the linker set the entry point so that the C run-time library is initialized correctly, and C++ constructors for static objects are executed."Vamos a probar un poco de código con objetos estáticos: ESTATICOS.CPP #include <windows.h> void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); } class Man { public: Man() { SayLong(66); } ~Man() { SayLong(99); } }; Man Jacinto; Man Pedrito; void main() { }
Compilando y linkeando este código con la Run Time Library por defecto conseguimos cuatro MessageBox, dos con 66 (el constructor) y dos con 99 (el destructor). Ahora tratamos de compilar y linkear con nuestra Run Time Library y nos encontramos con lo siguiente: estaticos.obj : error LNK2001: unresolved external symbol _atexit estaticos.exe : fatal error LNK1120: 1 unresolved externalsSi vamos a la MSDN podemos ver que lo que hace esta función es ejecutar la función que le es pasada como parametro al final del programa. Usando una estructura de pila (LIFO). Entonces que nos detiene? Que nos imposibilita crear una función simple como esa? Nada, manos a la obra señores. C_ATEXIT.CPP #include <malloc.h> unsigned long *Funcs = 0; int n_funcs = 0; extern "C" int atexit(void (__cdecl *func)(void)) { if (!Funcs) { Funcs = (unsigned long*)malloc(1); } Funcs = (unsigned long*)realloc(Funcs,(n_funcs+1)*4); Funcs[n_funcs] = (unsigned long)func; n_funcs++; return 0; } extern "C" void destructores() { for (int x = n_funcs-1;x >= 0;x--) { void (__cdecl *calla)(void) = (void (__cdecl *)(void))Funcs[x]; calla(); } free(Funcs); }
Hermoso el código verdad? Cada vez que se llama a la función atexit() se comprueba si ya tiene valor la estructura donde vamos a guardar los punteros a las funciones. Se realloca la memoria necesaria y se le asigna al elemento actual el parametro func. Siempre devolvemos cero que indica que no hubo ningún error. Pero este código hermoso que hace exactamente hasta ahora? Ir llenando una lista de punteros a funciones, perfecto pero nos falta algo muy importante, recordemos que atexit() crea una lista de funciones que van a ser llamadas al final del programa. El principio del programa es la función de ENTRADA (solo nos interesan main, WinMain y DllMain ya que si fuera otra no usaría la Run Time Library, ni la nuestra ni la de VC++) que sabemos como se ejecuta, la llamamos nosotros desde nuestra Run Time Library por lo tanto sabemos perfectamente cuando termina de ejecutarse; en la siguiente instrucción a la llamada a main() el programa debe ejecutar la lista de funciones de atexit() y limpiar el buffer utilizado. Estas dos cosas las hace la función destructores() que está también en C_ATEXIT.CPP. Ahora mismo podríamos compilar el programa ESTATICOS.CPP pero no pasaría nada, la compilación y el linkeo serían satisfactorios pero nuestro programa no haría absolutamente nada. La pregunta del millón, quien llama a la función atexit(): la propia Run Time Library la llama. Acertaron? No me mientan eh. Y cuando y porque la llama? Porque el compilador le pasa el puntero del destructor al ejecutar el constructor, por lo tanto, al no tener implementado nuestro código para manejar los constructores estáticos no recibimos ninguna llamada a atexit() y destructores() se ejecuta sin ningún puntero al que llamar. Que hace el compilador para decirnos que existen constructores de objetos estáticos? El compilador crea un función diferente para cada uno de los objetos estáticos (esto lo podemos comprobar viendo las direcciones que le pasa por cada objeto a atexit) y nos pasa dentro del ejecutable las 'etiquetas' (posición en el archivo) de esas funciones a ejecutarse. data_seg #pragma data_seg( ["section-name"[, "section-class"] ] )
Specifies the default section for data. For example:
#pragma data_seg( "MY_DATA" )
causes data allocated following the #pragma statement to be placed in a section called MY_DATA.
Data allocated using the data_seg pragma does not retain any information about its location.
Estas secciones que se definen con #pragma data_seg son escritas por el linker dentro del ejecutable y tienen punteros, a inicializadores de C, constructores de C++, pre-terminadores de C y terminadores de C que forman tablas, estas se guardan en variables que tienen el comienzo y el fin de la tabla y son pasadas a una función llamada _initterm() que se encarga de llamar a todos esos punteros ejecutando su código. Esta es la forma en la que se llama a al constructor de de un objeto estático. Nuestro nuevo C_MAIN.CPP: #include <malloc.h> #include <string.h> extern "C" int __argc = 1; extern "C" char** __argv = 0; extern "C" void __stdcall ExitProcess(unsigned long uExitCode); extern "C" char* __stdcall GetCommandLineA(void); extern int main(int argc, char *argv[],char *env[]); extern "C" int __stdcall MessageBoxA(int a,char *b,char *c,unsigned int p); typedef void (__cdecl *_PVFV)(void); #pragma data_seg(".CRT$XIA") _PVFV __xi_a[] = { 0 }; #pragma data_seg(".CRT$XIZ") _PVFV __xi_z[] = { 0 }; #pragma data_seg(".CRT$XCA") _PVFV __xc_a[] = { 0 }; #pragma data_seg(".CRT$XCZ") _PVFV __xc_z[] = { 0 }; typedef void (* PFV)(void); #pragma data_seg(".CRT$XPA") PFV __xp_a = 0; /* C pre-terminators */ #pragma data_seg(".CRT$XPZ") PFV __xp_z = 0; #pragma data_seg(".CRT$XTA") PFV __xt_a = 0; /* C terminators */ #pragma data_seg(".CRT$XTZ") PFV __xt_z = 0; extern "C" void __cdecl _initterm(_PVFV *, _PVFV *); //__xi_a[], __xi_z[]; /* C initializers */ //__xc_a[], __xc_z[]; /* C++ initializers */ //__xp_a[], __xp_z[]; /* C pre-terminators */ //__xt_a[], __xt_z[]; /* C terminators */ extern "C" void destructores(); #define FALSE 0 #define TRUE 1 extern "C" void mainCRTStartup() { char *parametros = GetCommandLineA(); char *temp = (char*)malloc(2048); memset(temp,0,2048); __argc = 0; __argv = (char**)malloc(4); char Except = FALSE; while(*parametros) { if (*parametros == 34) { if (Except == FALSE) { Except = TRUE; } else { Except = FALSE; if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } parametros++; continue; } if ( (*parametros == 32) && (Except == FALSE) ) { if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } else { unsigned long pos = strlen(temp); temp[pos] = *parametros; temp[pos+1] = 0; } parametros++; } if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } free(temp); _initterm(__xi_a,__xi_z); _initterm(__xc_a,__xc_z); main(__argc,__argv,0); _initterm(&__xp_a,&__xp_z); _initterm(&__xt_a,&__xt_z); destructores(); for (int y = 0;y < __argc;y++) { free(__argv[y]); } free(__argv); ExitProcess(0); } static void __cdecl _initterm(_PVFV *pfbegin,_PVFV *pfend) { while (pfbegin < pfend) { if (*pfbegin) { (**pfbegin)(); } ++pfbegin; } }
Este nuevo main define las diferentes secciones donde se encontraran las benditas tablas, llama a las tablas de inicio justo antes que a main() y a las de terminaciones justo después, además de llamar a nuestra función destructores(). Las tablas son procesadas por la función initterm() que recorre la tablae de punteros a funciones, ejecutando cada uno de ellas, hasta encontrar un puntero nulo. Creamos nuestra libreria y ESTATICOS.EXE: cl /c *.cpp lib *.obj /out:clib.lib cl /c ESTATICOS.CPP link ESTATICOS.OBJ /NODEFAULTLIB USER32.LIB KERNEL32.LIB CLIBCLIB.LIB
Ahora si, tenemos el mismo comportamiento que con la Run Time Library de VC++ para los objetos estáticos. Un regalito en esta parte, vamos a ver como manejar las variables de entorno: C_MAIN.CPP #include <malloc.h> #include <string.h> //entorno extern "C" int vars = 0; extern "C" char** _environ = 0; extern "C" char* __stdcall GetEnvironmentStrings(void); extern "C" bool __stdcall FreeEnvironmentStringsA(char *block); void __cdecl _envinit(void); //parametros extern "C" int __argc = 1; extern "C" char** __argv = 0; extern "C" void __stdcall ExitProcess(unsigned long uExitCode); extern "C" char* __stdcall GetCommandLineA(void); extern int main(int argc, char *argv[],char *env[]); extern "C" int __stdcall MessageBoxA(int a,char *b,char *c,unsigned int p); typedef void (__cdecl *_PVFV)(void); #pragma data_seg(".CRT$XIA") _PVFV __xi_a[] = { 0 }; #pragma data_seg(".CRT$XIZ") _PVFV __xi_z[] = { 0 }; #pragma data_seg(".CRT$XCA") _PVFV __xc_a[] = { 0 }; #pragma data_seg(".CRT$XCZ") _PVFV __xc_z[] = { 0 }; typedef void (* PFV)(void); #pragma data_seg(".CRT$XPA") PFV __xp_a = 0; /* C pre-terminators */ #pragma data_seg(".CRT$XPZ") PFV __xp_z = 0; #pragma data_seg(".CRT$XTA") PFV __xt_a = 0; /* C terminators */ #pragma data_seg(".CRT$XTZ") PFV __xt_z = 0; extern "C" void __cdecl _initterm(_PVFV *, _PVFV *); //__xi_a[], __xi_z[]; /* C initializers */ //__xc_a[], __xc_z[]; /* C++ initializers */ //__xp_a[], __xp_z[]; /* C pre-terminators */ //__xt_a[], __xt_z[]; /* C terminators */ extern "C" void destructores(); #define FALSE 0 #define TRUE 1 extern "C" void mainCRTStartup() { char *parametros = GetCommandLineA(); char *temp = (char*)malloc(2048); memset(temp,0,2048); __argc = 0; __argv = (char**)malloc(4); char Except = FALSE; while(*parametros) { if (*parametros == 34) { if (Except == FALSE) { Except = TRUE; } else { Except = FALSE; if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } parametros++; continue; } if ( (*parametros == 32) && (Except == FALSE) ) { if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } else { unsigned long pos = strlen(temp); temp[pos] = *parametros; temp[pos+1] = 0; } parametros++; } if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } free(temp); _initterm(__xi_a,__xi_z); _initterm(__xc_a,__xc_z); _envinit(); main(__argc,__argv,_environ); _initterm(&__xp_a,&__xp_z); _initterm(&__xt_a,&__xt_z); destructores(); for (int y = 0;y < __argc;y++) { free(__argv[y]); } free(__argv); for (int z = 0;z < vars;z++) { if (_environ[z]) { free(_environ[z]); } } free(_environ); ExitProcess(0); } static void __cdecl _initterm(_PVFV *pfbegin,_PVFV *pfend) { while (pfbegin < pfend) { if (*pfbegin) { (**pfbegin)(); } ++pfbegin; } } void __cdecl _envinit(void) { char *var = GetEnvironmentStrings(); _environ = (char**)malloc(4); while(*var) { _environ = (char**)realloc(_environ,(vars + 1) * 4); _environ[vars] = (char*)malloc(strlen(var) + 1); memset(_environ[vars],0,strlen(var) + 1); strcpy(_environ[vars],var); (char*)var += strlen((char*)var); (char*)var +=1; vars++; } FreeEnvironmentStringsA(var); }
Esta nueva versión del C_MAIN.CPP incorpora la función _envinit, que cre la lista de variables de entorno y le pasa la variable _environ a la función main() de nuestro programa. Ahora vamos a implementar getenv y putenv: C_PUTENV.CPP #include <malloc.h> #include <string.h> extern "C" int vars; extern "C" char** _environ; bool compararPut(char *s1,const char *s2) { while(*s2) { if (*s1 != *s2) { return false; } s1++; s2++; } return true; } extern "C" int putenv(const char *envstring) { for (int x = 0;x < vars;x++) { if (!_environ[x]) { continue; } if (compararPut(_environ[x],envstring) == true) { char *ptr = (char*)envstring; while(*envstring) { if ( (*ptr == '=') && (*++ptr == 0) ) { free(_environ[x]); _environ[x] = 0; return 0; } ptr++; } strcpy(_environ[x],envstring); return 0; } } _environ = (char**)realloc(_environ,(vars + 1) * 4); _environ[vars] = (char*)malloc(strlen(envstring) + 1); memset(_environ[vars],0,strlen(envstring) + 1); strcpy(_environ[vars],envstring); vars++; return 0; }
C_GETENV.CPP #include <string.h> extern "C" int vars; extern "C" char** _environ; bool compararGet(char *s1,const char *s2) { while(*s2) { if (*s1 != *s2) { return false; } s1++; s2++; } if (*s1 == '=') { return true; } return false; } extern "C" char *getenv(const char *varname) { for (int x = 0;x < vars;x++) { if (!_environ[x]) { continue; } if (compararGet(_environ[x],varname) == true) { return (_environ[x]+strlen(varname)+1); } } return 0; }
Podemos probar su correcto funcionamiento con cualquier programa que use el parametro envp o simplemente cambiando variables del entorno como TMP (el path de los archivos temporales). En la cuarta parte veremos como implementar las funciones de entrada y salida por consola. Saludos, Mariano. Autor: Mariano Ventaja http://www.c0d3rz.com.arDescarga: http://c0d3rz.com.ar/foro/viewtopic.php?t=104
33
« en: Martes 22 de Junio de 2004, 09:29 »
Crear nuestra propia Run Time Library (Segunda Parte) Primero que nada vamos a crear el directorio CLIB donde queramos y pasar los archivos que creamos en el primer tutorial ahi (C_ALLOC.CPP, C_MAIN.CPP, C_STR.CPP y C_WMAIN.CPP). Yo uso una función con mucha frecuencia que simplemente muestra un número en pantalla con un MessageBox: void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); }
No tenemos acceso a ltoa al ser una función de la Run Time Library y por supuesto la vamos a implementar: C_LTOA.CPP #include <string.h> extern "C" char *ltoa(long value,char * string,int radix) { char *ptr; unsigned digval; ptr = string; do { digval = (unsigned)(value % radix); value /= radix; *ptr++ = (char)(digval + 48); } while (value > 0); *ptr-- = ' '; strrev(string); return string; }
C_STRREV.CPP extern "C" char *strrev (char *string) { char *start = string; char *left = string; char ch; while (*string++); string -= 2; while (left < string) { ch = *left; *left++ = *string; *string-- = ch; } return(start); }
Tanto ltoa como strrev son funciones muy básicas que trabajan con cadenas de caracteres, vamos a crear nuestra libreria de esta forma: cl /c *.cpp lib *.obj /out:clib.lib
En el final de la primera parte les dije que ibamos a ver como crear los operadores new y delete, bueno vamos a intentar crear este más que simple programa y de paso probar atol y strrev: SIMPLE.CPP (lo creamos en un directorio diferente al de la libreria) #include <windows.h> void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); } class Man { public: Man() { SayLong(66); } ~Man() { SayLong(99); } }; int main() { Man *yo = new Man; return 0; }
Con nuestra Run Time Library: cl /c simple.cpp link simple.obj /nodefaultlib clibclib.lib user32.lib kernel32.lib
Como algunos ya se habrán imaginado esto no es posible, todavía, porque no hemos creado el operador new. Entonces el linker se queja: simple.obj : error LNK2001: unresolved external symbol "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z)Que es realmente lo que realiza el operador new? Es fundamental saber esto antes de intentar emular su funcionamiento; pese a los mitos que dominan el mundo de la informática el operador new no hace practicamente nada, el mismo se limita a llamar a malloc y devolver el puntero asignado por esta función. Eso es todo lo que realiza new, pero no hay que pasar por alto algo muy importante, el compilador llama al constructor del objeto si usamos new. Por eso siempre que usemos objetos dinámicos debemos usar new por lógica. Entonces vamos a crear el C_NEW.CPP teniendo en cuenta lo que ya sabemos: #include <stdlib.h> #include <malloc.h> void *operator new(unsigned int cb) { return malloc(cb); }
En este código simplemente llamamos a malloc (nuestro malloc, el que creamos en C_ALLOC.CPP). Recompilamos nuestra libreria: cl /c *.cpp lib *.obj /out:clib.lib
Volvemos a linkear (no hace falta volver a compilar) el programa anterior con nuestra Run Time Library así: cl /c simple.cpp link simple.obj /nodefaultlib clib.lib user32.lib kernel32.lib
Al ejecutar el programa simple.exe vemos el número 66 en pantalla, eso quiere decir que nuestro constructor fue llamado y como sabemos que nuestro código de new es una linea y no llama a ningún constructor tenemos la prueba fehaciente de que los constructores son llamados por el compilador y no por new. Con este código estamos dejando memoria sin liberar, el objeto creado no está siendo destruido en ninguna parte, así que vamos a deshacer el error: SIMPLE.CPP #include <windows.h> void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); } class Man { public: Man() { SayLong(66); } ~Man() { SayLong(99); } }; int main() { Man *yo = new Man; delete yo; return 0; }
Intentamos compilarlo con nuestra linea de comandos de siempre cl /c simple.cpp link simple.obj /nodefaultlib clib.lib user32.lib kernel32.lib
Y nos encontramos con el siguiente error: simple.obj : error LNK2001: unresolved external symbol "void __cdecl operator delete(void *)" (??3@YAXPAX@Z)Si van siguiendo atentamente este tutorial ya deben saber que delete es simplemente una llamada a free y el compilador implementa la llamada al destructor, vamos a comprobarlo implementando nuestra versión del operador delete: C_DELETE.CPP #include <stdlib.h> #include <malloc.h> void operator delete(void *p) { free(p); }
Creamos la libreria con la linea de comandos pertinente y volvemos a linkear (no hace falta volver a compilar) el programa con nuestra linea de comandos de siempre, como esperabamos el resultado obtenido es un mensaje con el número 66 y otro con el 99, demostrando nuevamente como los constructores y destructores son llamados por el compilador. Vamos a incluir un poco más de complejidad en nuestra clase para probarla: SIMPLE.CPP #include <windows.h> void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); } class Man { public: int edad; int pais; }; int main() { Man *yo = new Man; Man *john = new Man; Man *pedro = new Man; yo->edad = 20; yo->pais = 54; //argentina john->edad = 25; john->pais = 1; //usa pedro->edad = 14; pedro->pais = 34; //españa SayLong(yo->edad); SayLong(john->edad); SayLong(pedro->edad); delete yo; delete john; delete pedro; return 0; }
Volvemos a compilar, linkear y comprobar que el resultado es el esperado, los numeros que salen en pantalla son 20, 25 y 14, en ese mismo orden. Generalmente las aplicaciones de consola usan parametros introducidos por el usuario en la linea de comandos, es una convención que el primer parametro sea el nombre de la aplicación. Como podemos emular el funcionamiento de VC++ que no sólo nos pasa como parametros a nuestra función main, argc y argv sino que también los mantiene como variables estaticas? De esta forma, vamos a reemplazar nuestro C_MAIN.CPP que no hacía nada por este otro que hace bastantes cosas: #include <malloc.h> #include <string.h> extern "C" int __argc = 1; extern "C" char** __argv = 0; extern "C" void __stdcall ExitProcess(unsigned long uExitCode); extern "C" char* __stdcall GetCommandLineA(void); extern int main(int argc, char *argv[],char *env[]); extern "C" void mainCRTStartup() { char *parametros = GetCommandLineA(); char *temp = (char*)malloc(2048); memset(temp,0,2048); __argc = 0; __argv = (char**)malloc(4); char Except = false; while(*parametros) { if (*parametros == 34) { if (Except == false) { Except = true; } else { Except = false; if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } parametros++; continue; } if ( (*parametros == 32) && (Except == false) ) { if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } } else { unsigned long pos = strlen(temp); temp[pos] = *parametros; temp[pos+1] = 0; } parametros++; } if (strlen(temp) > 0) { __argv = (char**)realloc(__argv,4 * (__argc+1)); __argv[__argc] = (char*)malloc(strlen(temp) + 1); memset(__argv[__argc],0,strlen(temp) + 1); strcpy(__argv[__argc],temp); strcpy(temp,""); __argc++; } free(temp); main(__argc,__argv,0); for (int y = 0;y < __argc;y++) { free(__argv[y]); } free(__argv); ExitProcess(0); }
Si intentaramos construir la libreria en este momento no tendríamos ningún problema, pero cada vez que quisieramos utilizarla nos pediría las funciones _realloc y _memset por lo tanto sería una libreria completamente inútil. realloc lo usamos porque no sabemos cuantos elementos va a tener la linea de comandos y memset para dejar un buffer lleno de ceros y no tener datos 'basura'. C_REALLOC.CPP #include <windows.h> void *realloc(void *block, size_t size) { return HeapReAlloc(GetProcessHeap(),NULL,block,size); }
C_MEMSET.CPP extern "C" void *memset(void *s,int c,size_t n) { char *t = (char*)s; while(n--) { *t++ = c; } return s; }
Ahora si podemos crear la libreria con los parametros de siempre. Este código consigue pasarle como parametros argc y argv a la función main utilizando la función GetCommandLine de la API de Windows, usa nuestra implementación de memoria dinámica para guardar los parametros y los libera cuando termina la función main (que es llamada con main(__argc,__argv,0) finalmente llama a la función ExitProcess de la API de Windows. De esta forma teniendo __argc y __argv emulamos perfectamente el funcionamiento de VC++, vamos a comprobarlo con este pequeño código: CMDLINE.CPP #include <windows.h> extern int __argc; extern char **__argv; void SayLong(int number) { char Data[10]; ltoa(number,Data,10); MessageBox(0,Data,"El Numero",0); } void main(int argc,char *argv[]) { SayLong(argc); for (int g = 0;g < argc;g++) { MessageBox(0,argv[g],argv[g],0); } SayLong(__argc); for (int h = 0;h < __argc;h++) { MessageBox(0,__argv[h],__argv[h],0); } }
Compilamos y linkeamos con: cl /c cmdline.cpp link cmdline.obj /nodefaultlib clib.lib user32.lib kernel32.libEl programa funciona perfectamente, argc y __argc coinciden y argv y __argv también, prueben a compilarlo sin nuestra Run Time Library y obtendran los mismos resultados en cuanto a funcionamiento. Bueno gente, eso es todo por ahora, en la tercera parte vamos a ver como implementar constructores y destructores para objetos estáticos. Saludos, Mariano. Autor: Mariano Ventaja http://www.c0d3rz.com.arDescarga: http://www.c0d3rz.com.ar/foro/viewtopic.php?t=94
34
« en: Lunes 14 de Junio de 2004, 17:20 »
Alguna vez se preguntaron por que un programa como este puede llegar a ocupar unos 24KB? int main(int argc, char *argv[],char *env[]) { return 50; }
Este programa no hace NADA, solo nos devuelve un 50 (si recuerdan nuestro último tutorial esto es mov eax,50). El 'problema' es que VC++ por defecto incorpora su Run Time Library que contiene muchisimas funciones indispensables como strlen,malloc,printf y muchas más. Esta Run Time Library ocupa bastantes KB, si hacemos un programa extenso su tamaño no tiene la más mínima importancia, pero si sólo escribimos un par de instrucciones ... Linkeen el código anterior con esta linea de comandos: link codigo.obj /NODEFAULTLIB /ENTRY:mainPaso a explicar, NODEFAULTLIB le indica al linker que quite las librerias que utiliza para resolver referencias externas por defecto. Entry le indica al linker la función que dará comienzo al programa que por defecto nunca es main, nuestras funciones main o WinMain son llamadas por otras que antes hacen inicializacion de la Heap para utilizar malloc y free, que nos devuelven argc, argv y env por ejemplo. Estas funciones se llaman mainCRTStartup para main y WinMainCRTStartup para WinMain. Que hemos logrado con esta linea de comandos? Reducir el tamaño de nuestro ejecutable a nada más y nada menos que 1024 bytes (si no lo consiguieron es porque tienen una versión del linker que usa un align grande prueben con: link codigo.obj /NODEFAULTLIB /ENTRY:main /ALIGN:512 y lo van a conseguir). Esto está muy bien pero en realidad no nos sirve de mucho, ahora tenemos un programa que está discapacitado, no puede llamar ni siquiera a la función strlen(). Pero como el título de este tutorial dice vamos a crear nuestra propia Run Time Library para reemplazar la de VC++, el objetivo no es hacer una mejor que los muchachos de Microsoft, sino tener nuestra propia Run Time Library unicamente con las funciones que necesitemos y de paso aprender MUCHO sobre programación. Primero vamos a ver un ejemplo de Run Time Library muy simple escrito en C++. Nuestro objetivo en este momento es compilar este programa (COMUN.CPP) sin la STDLIB por defecto: #include <windows.h> int main(int argc, char *argv[],char *env[]) { char *dos = (char*)malloc(55); int x = strlen("hola"); wsprintf(dos,"%d",x); MessageBox(0,dos,dos,0); free(dos); return 50; }
Con esta linea de comandos LINK COMUN.OBJ /NODEFAULTLIB user32.lib kernel32.lib nos enteramos que nos faltan un par de funciones, estas cuatro exactamente: COMUN.OBJ : error LNK2001: unresolved external symbol _free COMUN.OBJ : error LNK2001: unresolved external symbol _strlen COMUN.OBJ : error LNK2001: unresolved external symbol _malloc LINKASM : error LNK2001: unresolved external symbol _mainCRTStartup COMUN.exe : fatal error LNK1120: 4 unresolved externals Al crear nuestra libreria la vamos a hacer lo más modular posible, porque cada archivo objeto (código compilado) que sea llamado se cargará completamente, por ejemplo si tenemos todas las funciones dentro de un solo archivo .cpp, lo compilamos y lo solo utilizamos strlen() dentro del programa que usa nuestra Run Time Library también serán cargadas strcpy(),malloc(), free() y todas las funciones que hagamos. Sabiendo esto que es muy importante vamos a crear cuatro archivos, C_STR.CPP con las funciones strlen() y strcpy(), C_MAIN.CPP con mainCRTStartup, C_WMAIN.CPP con WinMainCRTStartup y C_ALLOC.CPP con malloc() y free(). C_STR.CPP: extern "C" size_t strlen(const char *s) { unsigned long Result = 0; while(*s++) { Result++; } return Result; } extern "C" char *strcpy(char *dest, const char *src) { char *cp = dest; while(*cp++ = *src++); return(dest); }
C_MAIN.CPP: extern int main(int argc, char *argv[],char *env[]); extern "C" void mainCRTStartup() { main(0,0,0); }
C_WMAIN.CPP: #include <windows.h> extern WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow); extern "C" void WinMainCRTStartup() { WinMain(GetModuleHandle(NULL),NULL,GetCommandLine(),SW_SHOW); }
C_ALLOC.CPP: #include <windows.h> void *malloc(size_t size) { return HeapAlloc(GetProcessHeap(),NULL,size); } void free(void *block) { HeapFree(GetProcessHeap(),NULL,block); }
Bueno, la mayoria de las cosas las deben entender sin ningún problema, C_STR.CPP tiene dos funciones muy simples strlen() y strcpy() de la STDLIB. C_MAIN.CPP tiene una función que llama al main del programa que estemos escribiendo y le pasa como parametros el argc, argv y env (en este caso solo le pasamos ceros para no complicarnos). C_WMAIN.CPP llama a WinMain() la instancia anterior del programa, la linea de comandos y SW_SHOW. Finalmente C_ALLOC.CPP usa la Heap del proceso para asignar y liberar memoria. Ahora que tenemos todos los archivos de la CLIB (nuestra libreria para reemplazar la Run Time Library de VC++) podemos compilarla y linkearla asi: cl /c C_ALLOC.CPP C_MAIN.CPP C_STR.CPP C_WMAIN.CPP lib /out:CLIB.LIB C_ALLOC.OBJ C_MAIN.OBJ C_STR.OBJ C_WMAIN.OBJEsto nos va a generar nuestra clib.lib. Ahora teniendo el comun.cpp podemos hacer: LINK COMUN.OBJ /NODEFAULTLIB user32.lib kernel32.lib clib.lib y se va a generar un ejecutable que nos va mostrar un MessageBox con el numero 10. Ocupa 2.560 bytes en mi PC. Si prueban a compilar el mismo programa con la STDLIB por defecto verán que lo mínimo que consigan serán 14.484 bytes. En la segunda parte vamos a ver como mejorar nuestra libreria con más rutinas, como por ejemplo implementar los operadores new y delete, entre otras cosas. Saludos, Mariano. Autor: Mariano Ventaja http://www.c0d3rz.com.arDescarga: http://c0d3rz.com.ar/foro/viewtopic.php?t=85
35
« en: Viernes 4 de Junio de 2004, 17:23 »
En la seccion de e-books de http://www.c0d3rz.com.ar hay un par de libros de C\C++ y ensamblador. Saludos, Mariano.
36
« en: Viernes 4 de Junio de 2004, 11:40 »
Bueno, les dejo el link de mi foro donde se tratamos temas de C\C++ y ensamblador. Hay varios tutoriales y e-books también. Saludos, Mariano.
37
« en: Viernes 4 de Junio de 2004, 11:09 »
Bueno supongo que todos saben que la mayoria de los compiladores de C++ permiten incluir instrucciones de ensamblador dentro del codigo fuente y estas son ensambladas directamente en el codigo objeto. A esto se le llama inline assembler generalmente. Pero eso no es siquiera interesante; lo que si es muy interesante es la capacidad de escribir librerias (estaticas y dinamicas) tanto en ensamblador como en C++ (MASM y VC++) y linkearlas en ambos lenguajes. Osea, que por ejemplo podemos crear una libreria en ensamblador y linkearla estaticamente en un programa de C++. Vamos a ver el ejemplo. Codigo de la libreria de ensamblador (en rojo): .386 .model stdcall,flat include windows.inc include kernel32.inc include user32.inc includelib kernel32.lib includelib user32.lib CTEXT MACRO text:VARARG LOCAL TxtName .data TxtName BYTE text,0 .code EXITM <OFFSET TxtName> ENDM SayLong PROTO number:DWORD .code SayLong PROC number:DWORD LOCAL pointer:DWORD invoke GetProcessHeap invoke HeapAlloc,eax,HEAP_ZERO_MEMORY or HEAP_GENERATE_EXCEPTIONS,1024 mov pointer,eax invoke wsprintf,pointer,CTEXT("%d"),number invoke MessageBox,0,pointer,pointer,0 invoke GetProcessHeap invoke HeapFree,eax,0,pointer ret SayLong ENDP End Tranquilidad que ahora paso a explicar el codigo anterior. .386 es una directiva que le indica a MASM que nuestro codigo va optimizado para la arquitectura 386 .model stdcall,flat es una directiva que le indica a MASM que nuestro codigo usa stdcall (convencion de funciones que utiliza la API de Windows) y un modelo de memoria plano (el unico posible en Windows). Los includes justamente incluyen bibliotecas al estilo de los .H de C++, para conseguir estas bibliotecas hay que tener MASM32 MASM32 instalado y con el path configurado. Los includelib nos ahorran pasarle en la linea de comandos parametros de librerias que vamos a linkear, tambien hay que tener el MASM32 o sino crearlas (eso lo dejamos para otro tutorial). El macro CTEXT nos permite utilizar texto al modo de C++ usando CTEXT("TEXTO") cosa que no es posible directamente en ensamblador. La funcion SayLong simplemente muestra un MessageBox con el numero que hayamos especificado como parametro. Ustedes diran que carajo hacemos con este codigo, bueno aca esta la respuesta, ensamblamos y linkeamos con MASM: ml /c /Cp /coff asm_called.asm lib asm_called.obj Con eso conseguimos el archivo asm_called.obj que es el codigo objeto y el archivo asm_called.lib que es el codigo ejecutable que vamos a linkear desde C++. Ahora el codigo de C++ que llama a la funcion de ensamblador (en rojo como siempre) #include <windows.h> extern "C" void __stdcall SayLong(DWORD number); void main() { SayLong(50); } Muy simple este codigo, muy simple. Declara la funcion externa SayLong con el paso de paremetros de stdcall (la misma que usamos en ensamblador). Y la llama desde un main con un parametro de 50. Compilamos y linkeamos con VC++: cl -c calling_asm.cpp link calling_asm.obj asm_called.lib kernel32.lib user32.lib Ahora obtenemos un archivo objeto calling_asm.obj y un archivo ejecutable calling_asm.exe. Ejecuten el archivo calling_asm.exe y veran que aparece un MessageBox con el numero 50, la rutina SayLong (programa en ensamblador) fue llamada desde C++. Espero que les haya gustado el tutorial y hayan aprendido algo nuevo. Saludos, Mariano. Autor: Mariano Ventaja http://www.c0d3rz.com.arDescarga: http://www.c0d3rz.com.ar/foro/viewtopic.php?t=14
38
« en: Viernes 4 de Junio de 2004, 11:04 »
Foro de programación avanzada, C\C++ y ensamblador. Recursos como e-books y mucho más. Autor: Mariano Ventaja http://www.c0d3rz.com.ar
|
|
|