=============================================================================
    - ARGOMENTI DALLA RIGA DI COMANDO IN MODALITA’ CONSOLE E WIN32 IN C -

             Scritto da cH!cus, Giovedì 13 Aprile 2006
                  eMail: thechicus@gmail.com
                  Sito: http://thechicus.nigx.net
=============================================================================



INTRODUZIONE
============
Molti programmi, anche in C per esempio, quando eseguiti hanno la funzionalità di gestire gli input che gli vengono mandati tramite la riga di comando. Questi input di cui parlo si chiamano Argomenti (oppure Parametri). Per fare un esempio citiamo un comando del dos (che poi alla fine è un programma a se stante). Il comando che vi propongo è il DIR del dos. Cosa fa questo comando? Questo comando non fa altro che elencare la lista dei file e delle subdirectory che sono presenti all’interno della directory dove ci troviamo ad operare. Bene, questo comando se chiamato con degli argomenti genera risultati diversi rispetto a quando lo chiamiamo senza argomenti. Provate, andate in console e digitate “Dir”, osservate il risultato poi provate ad aggiungere un parametro al comando, cosi digitate “Dir /p”. Notate che a seconda di cosa gli passiamo come argomento lui fa il suo lavoro di modo diverso. Questa funzionalità è molto comoda. Questo era solo per darvi una panoramica, adesso vedremo come si gestisce in C.


ENTRY-POINT DEL PROGRAMMA
=========================
Iniziamo con dire che quando si chiama un programma fatto in C, in sintesi il sistema operativo carica il programma in memoria, gli passa il controllo e poi lo esegue iniziando a eseguire le istruzioni del programma dal suo entry-point. L’entry-point è il punto di entrata al programma, tutto incomincia ad essere eseguito da li. L’entry-point dei programmi creati in C è la funzione main(). Come tutti i programmatori  sanno, i programmi in C iniziano da quella funzione, poi al suo interno possono essere chiamate più funzione che svolgono i compiti del programma, ma main() è obbligatoria.

int main() {     /* <-------- ENTRY-POINT */
   return 0;
}


GLI ARGOMENTI DEL MAIN IN CONSOLE
=================================
Quando viene chiamato un programma il sistema operativo legge gli argomenti dalla riga di comando e gli passa all’entry-point del programma, nel nostro caso main(), ed essa gli gestisce attraverso variabili dichiarate tra le parentesi tonde della funzione stessa. Gli argomenti passati sono tre. Il primo si chiama argc, che starebbe per argument count, ossia il numero degli argomenti passati (variabile int). Il secondo argomento si chiama *argv[], che starebbe per argument vector. Esso è un vettore di stringhe contenente tutti gli argomenti con cui abbiamo chiamato il programma. Su questo c’è da precisare che in posizione argv[0] c’è sempre una stringa contenente il percorso del programma più il nome dell’eseguibile, poi da argv[1] in poi seguono gli argomenti con cui abbiamo chiamato il programma. Il terzo argomento si chiama *envp[], ed anch’esso è un vettore di stringhe che però contiene le variabili d’ambiente del sistema operativo. Gli argomenti del main() sono tutti facoltativi. Si può decidere di non usare nessuno di questi argomenti mettendo solamente void...

int main(void) {...
In tal caso il programma non gestirà ne gli argomenti passati dalla riga di comando ne le variabili di ambiente. Se decidiamo di fare un programma con supporto per acquisire gli argomenti della riga di comando allora basterebbe solamente dichiarare argc e *argv[] cosi...

int main(int argc, char *argv[]) {...

Voglio precisare che se il programma non necessita di argomenti della riga di comando dentro le parentesi tonde di main() si mette void, sennò si dichiara argc e *argv[] per gestire gli argomenti, oppure si dichiara argc, argv e envp per gestire gli argomenti e le variabile di ambiente. Altre combinazioni non avrebbero molto senso, tipo come usare argc senza poi usare *argv[].

Se per esempio nella cartella C:\programmi ho un file chiamato test.exe e lo faccio partire da console in questo modo...

C:\programmi>test.exe argomento1 argomento2 tre

Ne risulterà che il vettore di stringhe *argv[] che contiene gli argomenti passati assumerà una forma del genere...

Argv[0] --------> C:\programmi\test.exe
Argv[1] --------> argomento1
Argv[2] --------> argomento2
Argv[3] --------> tre

Come penso avrete notato un argomento è una o più lettere di seguito. Quando il sistema operativo legge gli argomenti gli spezza in base agli spazi. Tanto per scrivere un po’ di codice ecco un piccolo programma che illustra come possono venire processati con un for gli argomenti e le variabili di ambiente...

-------------------------------------code------------------------------------
/* Parametri che possono essere passati alla main del programma:
  
   argc, contiene un valore intero che indica il numero di argomenti passati.
   argv, è un vettore di puntatori che contiene gli argomenti stessi passati.
   envp, è un vettore di puntatori che contiene le variabili di ambiente.
*/
  
#include <stdio.h>

int main(int argc, char *argv[], char *envp[]) {
         int i, j;
        
         printf("---------> ARGOMENTI PASSATI <---------\n\n");
         for (i = 0; i < argc; i++)
             printf("%s\n", argv[i]);
            
         printf("\n\n---------> VARIABILI DI AMBIENTE <---------\n\n");        
         /* Controlla e stampa le variabili di ambiente fin che non si arriva alla fine del
         vettore di puntatori. Infatti nel costrutto for la condizione di avanzamento è
         envp[j] = '\0', ovvero se l'elemento attuale è un NULL esci dal ciclo.*/
         for (j = 0; envp[j] != '\0'; j++) {
             printf("%s\n", envp[j]);
         }

         return 0;
}
--------------------------------------end------------------------------------


ANALISI DELLA WINMAIN IN WIN32    
==============================
Qui il discorso cambia. Intanto bisogna dire che l’entry-point di un programma di win32 non è più main(), ma è WinMain(). Ecco come si dichiara la WinMain()...

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
   LPSTR lpCmdLine, int nCmdShow) {...

Questa ha un pò più parametri, ma non vi spaventate :D. Intanto iniziamo con il dire che come main() torna un valore int. Poi c’è quel WINAPI che ad un principiante della programmazione di windows potrebbe sembrare una cosa un po’ strana. Per tranquillizzarvi vi informo che quel WINAPI non è nient’altro che una macro fatta con #define dichiarata nell’header windows.h (mi pare). Essa è dichiarata cosi...

#define WINAPI _stdcall

...e rappresenta una convenzione di chiamata necessaria per il programma, quindi non scordate di inserire WINAPI quando create un programma per win32. Come potete osservare la WinMain() rispetto a main() richiede più parametri, potrei spiegarne l’utilizzo... ma visto che questa è una guida che spiega come utilizzare gli argomenti della riga di comando non sto a perderci tempo sopra. Detto questo vi dico subito che nei programmi Win32 gli argomenti della riga di comando con cui è stato chiamato il programma, il sistema operativo gli inserisce tutti insieme dentro un’unica stringa. La stringa in questione è il terzo parametro della WinMain(), ovvero lpCmdLine. La variabile lpCmdLine, fate attenzione, contiene solamente gli argomenti passati divisi da uno spazio, NON CONTIENE IL PERCORSO E IL NOME DELL’ESEGUIBILE. Come ricavare in Win32 il nome del programma in esecuzione lo spiego più avanti. Questa variabile è dichiarata come LPSTR. Il tipo LPSTR è dichiarato con un typedef (se non sapete cos’è cercate su internet), e non è altro che un puntatore a stringa, infatti l’acronimo di LPSTR è Long Pointer String. LPSTR è uno dei tanti tipi che si usano solitamente nella programmazione di windows. Sotto dos non sono utilizzati. HISTANCE ne è un altro esempio ma nel nostro caso non ci interessa.


RICAVARE PERCORSO E NOME DELL’ESEGUIBILE IN WIN32
=================================================
Con le seguenti righe di codice è possibile ottenere in una stringa il percorso e il nome dell’eseguibile del programma in esecuzione. Tutto usando due API di windows...

-------------------------------------code------------------------------------
/* Stringa contenente il percorso e il nome dell’eseguibile */
char szFileName[MAX_PATH];

/* Passando a GetModuleHandle il parametro NULL si ottiene l’handle del          processo corrente */
HINSTANCE hInstance = GetModuleHandle(NULL);

/* Una volta che sappiamo l’handle, e una volta passato a GetModuleFileName, questa funzione ne trovare il percorso e l’eseguibile e inserisce il tutto dentro una stringa, nel nostro caso szFileName*/
GetModuleFileName(hInstance, szFileName, MAX_PATH);
--------------------------------------end------------------------------------

Spero che questo articolo vi sia piaciuto. Ciao alla prossima!!!

cH!cus