Newsgroup: it.comp.lang.c++
Titolo: funzione callback in una DLL

> 
http://www.geocities.com/sunsetstrip/stage/7787
 <<
<<<<<<<<<<<< 
DucaCr...@iol.it
 >>>>>>>>>>>>>>

Questo dipende dal fatto che la DLL viene mappata sia nello spazio di
indirizzamento del processo che chiama SetHook, che in quello del processo
a cui ti vuoi agganciare. Questo significa che le variabili globali saranno
istanziate due volte e non ci sarŕ alcuna relazione tra di esse; da questo,
la perdita dell'inizializzazione. Una soluzione č l'utilizzo della memoria
condivisa; esempio:
#pragma data_seg ("memo") 
HWND g_hwndProcess = NULL;
UINT g_nMenuPos = 0;
#pragma data_seg ()
La sezione puň avere un nome qualunque (possibilmente entro gli otto
caratteri).
Due cose da notare:
1) inizializzare sempre le variabili residenti in una sezione di memoria
condivisa;
2) inserire nella riga del linker (nel dlg Settings):   -SECTION:memo,rws
Questo dovrebbe risolvere il problema, ammesso che non ci sia qualcos'altro
che non va.
Stefano.

sapere a MyHookProc il valore delle variabili g_hwndProcess e g_nMenuPos?
Hai vari schemi possibili.  Uno, di cui postai un esempio un mesetto
fa o poco piu`, e` che la MyHookProc si accorga, basandosi su di
una sua variabile globale, quando gira per la prima volta in un dato
processo (perche` trova la variabile a zero, quindi sa che non e` stata
ancora inizializzata); chiama allora la sua funzione d'inizializzazione,
che trova ad esempio la finestra di riferimento con EnumWindows e
le spedice con SendMessage opportuni WM_USER+qualcosa per
ottenere i dati necessari (hook, ecc); oppure, la procedura
d'inizializzaione
potrebbe accedere esplicitamente a un blocco di memoria condivisa
(MapViewOfFile, ecc) -- attenzione in questo caso a usare le salvaguardie
necessarie perche` istanze separate non si pestino i piedi (mutex).
In alternativa, l'inizializzazione puo` essere delegata alla DllMain della
DLL, con una piccola potenziale ottimizzazione.  Una volta che ti e`
chiaro che ogni istanza deve inizializzare separatamente i propri dati
"globali" (non globali a TUTTE le istanze, visto l'uso in processi diversi
e separati -- se non, usando esplicitamente shared memory!), non ti
dovrebbe essere difficile usare uno qualsiasi dei mille schemi di IPC
(inter-process communication) di Windows per ottenere i dati che ti
servono -- dalle semplici SendMessage, alla shared memory, al DDE,
per non tacere di COM (DCOM), socket, e via elencando.
Alex


grazie Stefano, grazie Alex......ora è tutto chiaro e ho risolto con la
memoria condivisa.
avrei un'altro problemino però: si riescono a fare un sacco di cose
interessanti e divertenti con questo hook che ho installato in un'altro
processo.... ma c'è una cosa che non ho capito come fare:
il tipo di hook che ho installato è WH_GETMESSAGE e fa delle cose in
risposta a messaggi WM_COMMAND che vengono generati quando l'utente clikka
su un menu che ho aggiunto all'applicazione "ospite" del mio hook..... ma
come faccio ad evitare che il messaggio WM_COMMAND che il mio hook ha
processato (solo quello) venga tolto dalla coda dell'applicazione "ospite"?
esempio: l'applicazione ospite ha un menu con ID 100, un mio programma
aggiunge al programma ospite un'altro menu con lo stesso ID e ci installa il
hook in risposta al mex corrispondente al quel menu.
Cosa succede se l'utente clikka sul menu con ID=100? di certo viene eseguita
prima
la mia procedura hook, ma poi il controllo passa per forza alla winproc del
programma ospite oppure no?
come posso fare per scegliere un ID per il mio menu "aggiunto", in modo da
essere sicuro di non cadere nel caso descritto qui sopra? per ora ho messo
ID=65535 che è il massimo utilizzabile, pensando che ci siano poche
probabilità che questo ID sia usato dalla applicazione.
Che ne dite?


2) inserire nella riga del linker (nel dlg Settings):   -SECTION:memo,rws
Wow, bello... si impara sempre qualcosa!
Colgo l'occasione per ricordare che, se questo attributo permette
effettivamente di condividere memoria-dati fra diversi processi (non
l'ho testato, ma questo dice la doc del linker), non fornisce pero`
nessuna protezione ai dati stessi rispetto all'accesso concorrente
(cosi` come non la fornisce lo schema di farsi "a mano" la memoria
condivisa, con MapViewOfFile ecc).  Se i dati sono in pratica delle
costanti, cioe` read-only per tutti, no problem; se no, bisogna pur
sempre rivestire gli accessi a questi dati con un meccanismo di
di sincronizzazione, tipo i mutex (o usare, per modificare questi
dati, SOLO le varie operazioni del tipo InterlockedX).
Alex

probabilità che questo ID sia usato dalla applicazione.
potresti implementare una routine che ispezioni gli ID di tutte le voci di
menu presenti nell'applicazione e scelga di conseguenza l'ID della voce
aggiuntiva; il vantaggio principale è che (essendo l'ID assegnato 
dinamicamente ogni volta), salvo altri imprevisti, il tutto dovrebbe
funzionare anche con versioni successive dell'applicazione (che potrebbero
includere nuove voci di menu e quindi nuovi ID). La fregatura è che per
poterla rendere utilizzabile nel caso generale (cioè nel caso di menu
ramificati fino all'inverosimile), sarebbe meglio implementarla come
funzione ricorsiva (si sa che non tutti le amano - io le odio). Le funzioni
da impiegare in questa routine sono: GetMenu, GetSubMenu, GetMenuItemCount
e GetMenuItemID.
Stefano.

> >2) inserire nella riga del linker (nel dlg Settings):  
-SECTION:memo,rws
adesso che mi viene in mente, è molto più comodo scrivere:
#pragma comment(linker,"-SECTION:memo,rws")
nel sorgente piuttosto che andare a modificare la riga di comando del
linker.
Stefano.

funzione ricorsiva (si sa che non tutti le amano - io le odio). Le funzioni
Se interessa, ho appena scritto ("tanto per gradire", nell'ambito
di una piccola utility che sto preparando) una versione non recursiva
di una funzione che costruisce un albero (usando uno stack esplicito,
vari loop annidati, alcuni goto, ecc), specificamente un albero in
memoria che duplica la struttura d'albero dei folder/subfolder di un
disco; naturalmente, la versione recursiva e` molto piu` semplice,
chiara, corta, eccetera, ma, se per qualche ragione misteriosa uno
"odia" le funzioni recursive, lo schema/struttura che ho usato puo`
venir comodo per eliminarle (personalmente, non le odio affatto,
volevo solo vedere se mi ricordavo come si faceva quando si
programmava in Fortran, anche se questa funzione e` in C++!).
Fatemi sapere, se del caso la posto volentieri.
Alex

e GetMenuItemID.
Le funzioni ricorsive non mi spaventano, anche se non ho mai avuto occasione
di usarle al di fuori dell'ambito scolastico..... il problema più grosso è
come gestire i risultati della ricerca.... e come decidere come vada scelto
il mio ID....
ho a disposizione un range di possibili ID che è 0-->65525 !!!!!
per risolvere il problema in modo completo dovrei allocare un vettore di
BOOL lungo 65525 e associare vero o falso ad ogni elemento a seconda che
l'ID corrispondente sia libero o no...ma un BOOL è grosso 4 byte!!! mi
verrebbe fuori una struttura di circa 256 Kb....!!!
altrimenti un modo migliore sarebbe fare un vettore di 65525/8=8191 BYTE che
sono circa 7Kb.... decisamente più accettabile... ma temo che ci siano delle
difficoltà ad indicizzare i singoli byte del vettore, giusto?
Quali funzioni offre il c++ per manipolare i dati a livello di bit? una
volta che ho:
BYTE vettID[8191];
con vettID[0] accedo ai primi 8 elementi tutti insieme.... ma come faccio a
settare o a leggere un solo bit?
Altrimenti supponendo che gli ID del programma siano tutti valori bassi,
basta trovare il massimo ID... ma questo equivale a prendere il max
disponibile.... e quindi il problema dell'algoritmo non si pone.... ma non
sarebbe una soluzione completa (nel senso che potrebbe portare a conflitti
di ID)


BOOL lungo 65525 e associare vero o falso ad ogni elemento a seconda che
Stai pensando in termini di una implementazione, invece che della
funzionalita` astratta che ti serve; ragionare in termini delle funzioni
richieste, e POI vedere come implementarle, e` spesso piu` produttivo.
Qui, quello che desideri e` un "insieme" ("set"); il C++ Standard offre
anzitutto, a questo scopo, la classe template std::set<tipo> (nel tuo
caso, immagino, std::set<unsigned short>).  In un set puoi inserire
elementi (ci pensa lui a evitare la duplicazione), chiedere se un dato
elemento esista, enumerare gli elementi presenti; per ottenere un
elemento NON nel set, hai varie possibilita`, tipo cominciare dall'alto
a chiedere se un dato elemento sia presente, ripetendo sinche` non
ne trovi uno "libero".  Ciascuna di queste operazioni deve, per specifica
dello standard, svolgersi in tempo, al peggio, proporzionale a log(N),
dove N e` il numero di elementi dell'insieme.  Puoi anche (in un tempo
costante, e brevissimo) chiedere quanti elementi l'insieme abbia, per
garantirti che qualcuno sia libero prima di iniziare a cercarlo.
Esiste anche una specializzazione di std::vector<> per elementi
bool, e inoltre anche la possibilita` di accedere ai singoli bit con
operazioni di shift-and-mask, e su questi "mattoni" di livello piu`
o meno basso non e` difficile costruire versioni specializzate del
set, se dovesse servire per migliorare le prestazioni e/o ridurre le
risorse consumate; come per tutte le ottimizzazioni, conviene,
pero`, _predisporre_, si`, l'architettura del proprio codice in modo
che sia possibile in seguito introdurre l'ottimizzazione (tipicamente
incapsulando gli accessi in modo da ridurre l'impatto di successive
reimplementazioni di una certa funzionalita`) -- ma NON realizzare
l'ottimizzazione sinche` la necessita` non ne viene dimostrata
("l'ottimizzazione prematura e` la radice di tutti i mali", disse, mi
pare, Knuth, e, mentre la frase e` deliberatamente "troppo forte"
[poiche` svariati mali vengono da cause diverse!], ha certo un
importante "nocciolo" di validita`).
Alex

"odia" le funzioni recursive, lo schema/struttura che ho usato puo`
Il mio odio per le funzioni ricorsive risale a quando andavo a scuola;
all'epoca non ebbi molta fortuna (le solite ricorsioni fino a esaurimento
di stack). Credo che sia un fatto psicologico, come quella volta, quando
avevo 5 o 6 anni, che sono rimasto bloccato in ascensore; ancora oggi (ne
ho 24), se posso, evito di andarci da solo; per fortuna abito al primo
piano! In effetti saranno 6 o 7 anni che non provo a scrivere funzioni
ricorsive; dovrei riprovarci, da allora sono un po' più pratico di
programmazione e in fondo sono molto meno spaventose di un ascensore.
Stefano.

La soluzione proposta da Alex (std::set) mi sembra la più adeguata. A parte
questo però, quando ti ho suggerito l'enumerazione degli ID, non pensavo
all'allocazione di un vettore di 65525 elementi. Piuttosto pensavo di
raccogliere in un vettore (decisamente più piccolo) gli ID delle voci di
menu presenti nell'applicazione (in realtà sarebbe il caso di enumerare
anche gli ID di eventuali pulsanti di comando presenti nella finestra
dell'applicazione e/o in una eventuale toolbar, visto che anch'essi
generano messaggi WM_COMMAND e ciò potrebbe effettivamente provocare
conflitti). Da qui in poi sarebbe stato facile trovare un ID non presente
nel vettore; avresti potuto ad esempio ordinare il vettore e, come dicevi
tu, trovare il maggiore e magari limitarsi a sommare un'unità; non mi pare
che questo generi dei conflitti (se c'è qualche risvolto che non ho
considerato ti prego di farmelo notare).
Alex dice che il template std::set offre questi servizi. Bene, questo,
unitamente al fatto che si è offerto, molto gentilmente, di postare la sua
routine, significa che il lavoro da fare non è molto; in pratica devi solo
adattare la routine al caso specifico.
Ciao,
Stefano.


Questo metodo va bene per condividere memoria tra istanze diverse della mia
dll e funziona benone...
Ma se voglio condividere dati tra l'applicazione che ha lanciato il hook e
la dll che lo contiene (l'istanza della dll eseguita nel processo ospite)
come faccio?
Vi spiego meglio: chiamiamo Pippo la mia applicazione, la quale installa una
hook procedure (MyHookProc) in un altro processo ospite tramite una dll.
La MyHookProc ad un certo punto deve in qualche modo invocare una funzione
del programma Pippo e "infilare" i dati che Pippo genera nella applicazione
ospite.
In pratica MyHookProc potrebbe mandare un messaggio WM_USER a Pippo e poi
mettersi in attesa dei dati però... quanto deve attendere? e dove li va a
pescare?
Spulciando la documentazione ho trovato il messaggio WM_COPYDATA che sembra
utile per i miei scopi, ma non ho ancora provato...
quello che vi chiedo sono consigli sul metodo da usare (la dimensione dei
dati che Pippo deve mandare a MyHookProc sono dell'ordine di qualche riga di
testo, poca roba quindi).
Grazie anticipate!


Il metodo che vuoi adottare mi sembra buono: avvisi Pippo che è ora di
spedire i dati con un messaggio di finestra; Pippo invia i dati all'app.
ospite con un messaggio WM_COPYDATA; l'app ospite li riceve elaborando
questo messaggio (all'interno di MyHookProc).
ATTENZIONE: non puoi più usare WH_MSGFILTER, devi tornare ad usare
WH_GETMESSAGE.
Un altro problema è che l'applicazione potrebbe già elaborare per conto suo
WM_COPYDATA (potrebbe essere abilitata a ricevere dati proprio in questo
modo), quindi potrebbe essere dannoso inviarle dei dati che non si aspetta.
Per riparare a questo, trovi un ottimo alleato proprio in WH_GETMESSAGE
che, come diceva Alex, ti permette di modificare il messaggio prima di
passarlo alla winproc. In questo caso dovremo fare in modo che la winproc
non riceva il messaggio WM_COPYDATA; dovrebbe essere sufficiente
trasformarlo in un WM_NULL (più innocuo di così) senza curarsi dei
parametri:
((MSG *)lParam)->message = WM_NULL;
Sempre per il fatto che l'app. potrebbe essere predisposta alla ricezione
di dati tramite WM_COPYDATA, devi assicurarti di non elaborare e annullare
questo messaggio a meno che non sia proprio quello inviato da Pippo,
altrimenti potresti compromettere una o più funzionalità, oltre che
appropriarti di dati che non ti competono e gestirli come se fossero
indirizzati a te. Per ovviare a questo puoi usare il membro dwData della
struttura COPYDATASTRUCT; ti scegli un numero, lo inserisci in dwData e
spedisci il tutto; dall'altra parte (in MyHookProc) controlli questo membro
e decidi se elaborare o no. Per quanto improbabile, è possibile che l'app.
riceva WM_COPYDATA con dei flag in dwData che vengano proprio a formare il
numero che ti sei scelto, quindi un'altra soluzione potrebbe essere quella
di far precedere una stringa ai dati veri e propri, ad esempio 'MetalDave'
(una sorta di firma); in questo modo dovresti essere sicuro al 99,9% di
poter distinguere i messaggi indirizzati a te da quelli indirizzati
all'applicazione.
Stefano.

nell'immediato è più utile a Dave, ma mi piacerebbe poterle analizzare.
OK, pronti.  Allora, l'esempio specifico e` quello di percorrere
l'albero dei directory su di un disco, usando le funzioni delle
API di Windows FindFirstFile/FindNextFile/FindClose, ma,
naturalmente, la struttura della funzione sarebbe identica per
qualsiasi altro compito in cui bisogna percorrere (ad esempio
per costruirne un modello in memoria) un albero appoggiandosi
su funzioni analoghe ("trova il primo figlio del nodo corrente",
"trova il prossimo nodo", "Ok, con l'esame dei figli di questo
nodo ho finito").  Per concretezza, lo scopo della funzione di
esempio e` il calcolo dell'occupazione complessiva di disco
da parte dell'intero albero, in termini di numero di byte; come
albero interessa quello che inizia dal directory corrente, e la
specifica del prototipo della funzione e`:
    long getOccupiedBytes(void);
Partiamo, anzitutto, dalla versione basata su di una funzione
recursiva, basata sull'idea: il numero di byte occupati da un
sottoalbero e` la sommatoria dei byte dei file che contiene
il suo directory-radice, piu` i byte occupati da ciascun
sottoalbero la cui radice e` un sottodirectory della radice --
recursivamente.  Usiamo inoltre una funzione ausiliaria
"bool isDummy(const char* directoryName)" che ritorna
true per "." e "..", tipici directory-dummy nei quali non si
vuole entrare, banalmente, ad esempio:
    bool isDummy(const char* directoryName) {
        if(0==strcmp(directoryName,".")) return true;
        if(0==strcmp(directoryName,"..")) return true;
        return false;
    }
Infine, ignoriamo per semplicita` il fatto che "un file di 3
byte" non occupa 3 byte, bensi` un intero cluster (solo
perche` qui interessa mostrare la *struttura*...!), come
pure i file piu` grandi di 4 gigabyte, la diagnostica di
errori, ecc ecc.
long getOccupiedBytes(void)
{
    long result=0;
    WIN32_FIND_DATA wfd;
    HANDLE hFind = FindFirstFile("*.*",&wfd);
    for(;;) {
        result += wfd.nFileSizeLow;
        if(wfd&FILE_ATTRIBUTE_DIRECTORY) {
            if(!isDummy(wfd.cFileName)) {
                SetCurrentDirectory(wfd.cFileName);
                // la chiamata recursiva:
                result += getOccupiedBytes();
                SetCurrentDirectory("..);
            }
        }
        if(!FindNextFile(hFind,&wfd)) {
            FindClose(hFind);
            return result;
        }
    }
}
Per rimuovere la singola chiamata recursiva, bisogna
trovare un altro modo, alternativo alla recursione, di
dire, in quel punto: "salva i dati che non vanno alterati,
e ricomincia l'elaborazione dall'inizio"; e, dove ora
abbiamo il "return", bisogna invece avere un altro
modo, compmementare, di dire "recupera i dati che
in precedenza erano stati salvati, e riprendi l'esecuzione
dal punto del precedente ``ricomincia''" (verificando,
naturalmente, se _esistono_ ancora "dati in precedenza
salvati"; se no, occorre fare return dall'intera funzione).
Siamo qui molto fortunati, dal punto di vista della semplicita`
di rimuovere la recursione, perche` c'e` un singolo punto di
chiamata recursiva, e un singolo punto di ritorno; inoltre,
la funzione e` priva di argomenti (una bella semplificazione!),
_E_, altra cosa che ci facilita molto, c'e` UN SOLO dato da
salvare: la HANDLE hFind.  Infatti, "result" e` usato in
modo tale che basta continuare a sommargli i dati,
mentre wfd e` un "buffer" sovrascritto da ogni chiamata
a FindNextFile -- non mantiene dati "importanti" da un
ciclo all'altro.
long getOccupiedBytes(void)
{
    long result=0;
    WIN32_FIND_DATA wfd;
    std::vector<HANDLE> savestack;
    for(;;) {
        HANDLE hFind = FindFirstFile("*.*",&wfd);
        for(;;) {
            result += wfd.nFileSizeLow;
            if(wfd&FILE_ATTRIBUTE_DIRECTORY) {
                if(!isDummy(wfd.cFileName)) {
                    SetCurrentDirectory(wfd.cFileName);
                    savestack.push_back(hFind);
                    break;    // dal loop interno torna all'esterno
                }
            }
            while(!FindNextFile(hFind,&wfd)) {
                FindClose(hFind);
                SetCurrentDirectory("..");
                if(savestack.empty())
                    return result;
                hFind = savestack.back();
                savestack.pop_back();
            }
        }
    }
}
La cosa quasi miracolosa e` che qui siamo riusciti
a rimuovere la recursione senza nessun esplicito
"goto"...!  Di solito (con piu` di un punto di chiamata
recursiva, e/o piu` di un punto di ritorno), non se ne
potra` fare a meno.
Certo, la "amplificazione di complessita`" del flusso
di controllo della funzione resta pur sempre paurosa,
con tre loop annidati: il for(;;) esterno e` dove si
inizia l'analisi di ogni sub-directory, quello interno
il "normale" loop sui file (e sub-dir) di ogni directory,
e il "while" al suo interno (mutazione dell'"if" della
originale routine recursiva) forse il piu` "trucchistico"
di tutti, poiche` serve a diagnosticare la fine di un
directory e anche a "riprendere" la "normale"
analisi del suo directory-padre.  Ma forse, a pensarci
bene, la cosa piu` strana e` il break del for(;;) interno,
che non e` la FINE di una qualche sequenza di
operazioni, bensi` l'INIZIO di una ricerca "annidata"
(infatti, uscendo dal for interno, il controllo passa al
for esterno, che riparte con FindFirstFIle).
Normalmente, rimuovere la recursione richiede
molte piu` complicazioni, e, in particolare, rende
inevitabile l'uso di espliciti "goto" (un'alternativa
equivalente ad essi e` un modello con esplicita
"macchina a stati finiti" -- un for(;;) con dentro
uno switch() -- che qui lasciamo come esercizio
per il lettore).
Le prestazioni della versione non recursiva sono
indistinguibili, in termini di misurazioni, da quelle
della versione recursiva.  Nondimeno, in teoria,
un vantaggio ci dovrebbe essere (se le recursioni
sono annidate MOLTO profondamente), poiche`,
nella versione non-recursiva, possiamo (e, in
effetti, DOBBIAMO) decidere esplicitamente
COSA salvare (qui, la sola HANDLE hFind),
mentre una chiamata recursiva "salva tutto"
quello che vive nello stack, sia le nostre esplicite
variabili, sia dei "dati impliciti" gestiti dal compilatore
(frame pointer, instruction pointer alla chiamata
per poter eseguire il return "giusto").  In situazioni
di memoria _estremamente_ scarsa, e anche di
recursioni _estremamente_ profonde, questa
unica caratteristica positiva del "recursion
removal" puo` essere proprio quella che "ci salva
la ghirba".
Come per tutte le tecniche di ottimizzazione, vale,
naturalmente, il motto "impara l'arte, e mettila da
parte"; l'ottimizzazione *prematura* e` causa di
molti mali (il codice ottimizzato e` piu` difficile da
capire, manutenere, modificare, eccetera).  In
generale sara` piu` semplice, piu` produttivo, ed
equivalente in termini di prestazioni, scrivere il
programma usando dapprima la recursione; se
si evidenziano problemi di prestazioni (memoria),
allora si puo` rimboccarsi le maniche e rimuovere
la recursione -- lasciando, come commento, la
chiara e nitida versione recursiva, ma passando
a quella, piu` complessa e fragile, non-recursiva,
per risparmiare qualche prezioso byte, o qualche
prezioso ciclo di CPU.
Alex

.
Alex

> 
http://www.geocities.com/sunsetstrip/stage/7787
 <<
<<<<<<<<<<<< 
DucaCr...@iol.it
 >>>>>>>>>>>>>>
PS se avete commenti sul codice che ho scritto non esitate a mandarmeli.....
(della serie c'è sempre da imparare dai propri errori!)

(della serie c'è sempre da imparare dai propri errori!)
Non mi pare che ci siano qui degli "errori", comunque, ho
offerto i miei consigli stilistici relativi al tuo codice, cosi`
che tu possa, se vuoi, prenderli in considerazione.
Alex

Grazie mille...
Stefano.

che tu possa, se vuoi, prenderli in considerazione.
grazie, sono sicuramente costruttivi per me..... visto che di "stile" di
programmazione ne so proprio poco... ;-))


procedura... o no?
Perche` non _misuri_ le cose...?  QueryPerformanceCounter
(e QueryPerformanceFrequency per calibrarla) ti permettono
di misurare con una precisione del microsecondo o giu` di li`.
Le "impressioni" dei programmatori sulle prestazioni dei loro
programmi sono notoriamente soggette a gravi errori...
Piu` in generale, l'approccio dovrebbe essere: scrivi il codice
nel modo GIUSTO, con stile, chiarezza, correttezza semantica;
poi, se _verifichi con la misurazione_ che il tuo compilatore
non e` in grado di ottenere le dovute prestazioni dal codice
come l'hai scritto, penserai a ristrutturazioni ottimizzanti (e
lascerai il piu` chiaro/limpido codice originale come commento).
Qui, poi, stiamo parlando di una funzione di *inizializzazione*,
quindi, eseguita UNA volta per esecuzione dell'intero programma;
di conseguenza, e` MOLTO improbabile che le considerazioni di
prestazioni avessero il minimo peso, anche se ve ne fossero
"in assoluto"...!
Alex

fuori.  Credo che si dovrebbero editare gli header di sistema,
Forse io devo considerarmi un fortunato!
Io ho usato un metodo che è suggerito da MS stessa in un qualche esempio di
usa della "Libreria Standard" nella documentazione di VC++5. Faccio in modo
che ogni unità di compilazione
presenti all'inizio una pragma così:
#pragma warning(disable:4786)
Per esempio se ho una classe che usa la "Libreria Standard" del .cpp
includerò il relativo .h e lì ci sarà in testa la pragma. Così anche
eventuali "clienti" della classe sfuggiranno al warning.
Questo ha sempre funzionato con i miei progetti. Forse però non avevo
strutture complesse di inclusione.
Livio Cavallo

programmi sono notoriamente soggette a gravi errori...
ok.... Seguirò questo tuo consiglio in futuro
;-)
