Guida al porting: Spy++Porting Guide: Spy++

Questo case study relativo al porting contiene informazioni generali sul funzionamento di un tipico progetto di porting e sui tipi di problemi che è possibile riscontrare, oltre ad alcuni suggerimenti e trucchi per la risoluzione dei problemi di porting.This porting case study is designed to give you an idea of what a typical porting project is like, the types of problems you might encounter, and some general tips and tricks for addressing porting problems. Questa non intende essere una guida definitiva, perché l'esperienza del porting di un progetto dipende in larga misura dalle specifiche del codice.It's not meant to be a definitive guide to porting, since the experience of porting a project depends very much on the specifics of the code.

Spy++Spy++

Spy++ è uno strumento di diagnostica GUI ampiamente usato per il desktop di Windows, che fornisce ogni tipo di informazione sugli elementi dell'interfaccia utente sul desktop di Windows.Spy++ is a widely used GUI diagnostic tool for the Windows desktop that provides all sorts of information about user interface elements on the Windows desktop. Spy++ mostra la completa gerarchia di finestre e fornisce l'accesso ai metadati su ogni finestra e controllo.It shows the complete hierarchy of windows and provides access to metadata about each window and control. Questa utile applicazione è fornita da molti anni con Visual Studio.This useful application has shipped with Visual Studio for many years. È stata trovata una vecchia versione compilata per l'ultima volta in Visual C++ 6.0 ed è stata convertita per Visual Studio 2015.We found an old version of it that was last compiled in Visual C++ 6.0 and ported it to Visual Studio 2015. L'esperienza per Visual Studio 2017 dovrebbe essere quasi identica.The experience for Visual Studio 2017 should be almost identical.

Questo è considerato un tipico caso di porting di applicazioni desktop Windows che usano MFC e l'API Win32, in special modo in caso di progetti obsoleti che non sono stati aggiornati con ogni versione di Visual C++ fin da Visual C++ 6.0.We considered this case to be typical for porting Windows desktop applications that use MFC and the Win32 API, especially for old projects that have not been updated with each release of Visual C++ since Visual C++ 6.0.

Passaggio 1. Step 1. Conversione del file di progettoConverting the project file.

Il file di progetto, due vecchi file con estensione dsw di Visual C++ 6.0, è stato facilmente convertito, senza alcun problema che richiedesse ulteriore attenzione.The project file, two old .dsw files from Visual C++ 6.0, converted easily with no issues that require further attention. Un progetto è l'applicazione Spy++,One project is the Spy++ application. l'altro è SpyHk, scritto in C, una DLL di supporto.The other is SpyHk, written in C, a supporting DLL. Potrebbe non essere altrettanto facile eseguire l'aggiornamento di progetti più complessi, come discusso qui.More complex projects might not upgrade as easily, as discussed here.

Dopo l'aggiornamento dei due progetti, la soluzione era simile alla seguente:After upgrading the two projects, our solution looked like this:

Soluzione Spy++The Spy++ Solution

Sono disponibili due progetti: uno con un ingente numero di file C++, l'altro rappresentato da una DLL scritta in C.We have two projects, one with a large number of C++ files, and another a DLL that's written in C.

Passaggio 2. Step 2. Problemi del file di intestazioneHeader file problems

Durante la compilazione di un progetto appena convertito, uno dei primi aspetti spesso osservati è che i file di intestazione usati dal progetto non vengono rilevati.Upon building a newly converted project, one of the first things you'll often find is that header files that your project uses are not found.

Uno dei file non trovati in Spy++ è verstamp.h.One of the files that couldn't be found in Spy++ was verstamp.h. Da una ricerca in Internet, è stato rilevato che questo file proveniva da un SDK di DAO, una tecnologia di dati obsoleta.From an Internet search, we determined that this came from a DAO SDK, an obsolete data technology. L'obiettivo era scoprire quali simboli venivano usati da tale file di intestazione per verificare se esso fosse davvero necessario oppure se quei simboli fossero definiti altrove, quindi è stato aggiunto un commento alla dichiarazione del file di intestazione, che è stata poi ricompilata.We wanted to find out what symbols were being used from that header file, to see if that file was really needed or if those symbols were defined elsewhere, so we commented out the header file declaration and recompiled. Il risultato è che solo il simbolo VER_FILEFLAGSMASK è davvero necessario.It turns out there is just one symbol that is needed, VER_FILEFLAGSMASK.

1>C:\Program Files (x86)\Windows Kits\8.1\Include\shared\common.ver(212): error RC2104: undefined keyword or key name: VER_FILEFLAGSMASK  

Il modo più semplice per trovare un simbolo nei file di inclusione disponibili consiste nell'usare Cerca nei file (Ctrl + Maiusc + F) e specificare Directory di inclusione di Visual C++.The easiest way to find a symbol in the available include files is to use Find in Files (Ctrl+Shift+F) and specify Visual C++ Include Directories. Il simbolo è stato trovato nel file ntverp.h.We found it in ntverp.h. Il file di inclusione verstamp.h è stato sostituito con il file ntverp.h e l'errore è scomparso.We replaced the verstamp.h include with ntverp.h and this error disappeared.

Passaggio 3. Step 3. Configurazione della proprietà OutputFile del linkerLinker OutputFile setting

I progetti più vecchi talvolta inseriscono file in percorsi non convenzionali che possono causare problemi dopo l'aggiornamento.Older projects sometimes have files placed in unconventional locations that can cause problems after upgrading. In questo caso, è necessario aggiungere $(SolutionDir) al percorso di inclusione nelle proprietà del progetto per assicurarsi che Visual Studio riesca a trovare alcuni file di intestazione qui inseriti, piuttosto che in una delle cartelle di progetto.In this case, we have to add $(SolutionDir) to the Include path in the project properties to ensure that Visual Studio can find some header files that are placed there, rather than in one of the project folders.

MSBuild rileva che la proprietà Link.OutputFile non corrisponde ai valori TargetPath e TargetName, emettendo l'errore MSB8012.MSBuild complains that the Link.OutputFile property does not match the TargetPath and TargetName values, issuing MSB8012.

warning MSB8012: TargetPath(...\spyxx\spyxxhk\.\..\Debug\SpyxxHk.dll) does not match the Linker's OutputFile property value (...\spyxx\Debug\SpyHk55.dll). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Link.OutputFile).warning MSB8012: TargetName(SpyxxHk) does not match the Linker's OutputFile property value (SpyHk55). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Link.OutputFile).  

Link.OutputFile è l'output di compilazione (ad esempio, EXE, DLL) e in genere viene creato da $(TargetDir)$(TargetName)$(TargetExt) e restituisce il percorso, il nome file e l'estensione.Link.OutputFile is the build output (EXE, DLL, for example), and it is normally constructed from $(TargetDir)$(TargetName)$(TargetExt), giving the path, filename and extension. Si tratta di un errore che si verifica comunemente durante la migrazione dei progetti dal vecchio strumento di compilazione di Visual C++ (vcbuild.exe) al nuovo strumento di compilazione (MSBuild.exe).This is a common error when migrating projects from the old Visual C++ build tool (vcbuild.exe) to the new build tool (MSBuild.exe). Dal momento che la modifica dello strumento di compilazione ha avuto luogo in Visual Studio 2010, potrebbe essere possibile riscontrare questo problema ogni volta che si esegue la migrazione di un progetto precedente alla versione 2010 a una versione 2010 o successiva.Since the build tool change occurred in Visual Studio 2010, you might encounter this issue whenever you migrate a pre-2010 project to a 2010 or later version. Il problema fondamentale è che la migrazione guidata del progetto non aggiorna il valore Link.OutputFile perché non è sempre possibile determinare su cosa deve basarsi il relativo valore rispetto alle altre impostazioni del progetto.The basic problem is that the project migration wizard doesn’t update the Link.OutputFile value since it’s not always possible to determine what its value should be based on the other project settings. Di conseguenza, è in genere necessario impostarlo manualmente.Therefore, you usually have to set it manually. Per altre informazioni, vedere questo post nel blog di Visual C++.For more details, see this post on the Visual C++ blog.

In questo caso la proprietà Link.OutputFile è stata impostata su .\Debug\Spyxx.exe e .\Release\Spyxx.exe per il progetto Spy++, a seconda della configurazione.In this case, the Link.OutputFile property in the converted project was set to .\Debug\Spyxx.exe and .\Release\Spyxx.exe for the Spy++ project, depending on the configuration. La soluzione migliore consiste nel sostituire semplicemente questi valori hardcoded con $(TargetDir)$(TargetName)$(TargetExt) per tutte le configurazioni.The best bet is to simply replace these hardcoded values with $(TargetDir)$(TargetName)$(TargetExt) for All Configurations. Se il problema persiste, è possibile personalizzare da questa posizione oppure modificare le proprietà nella sezione Generale in cui tali valori vengono impostati (le proprietà sono Directory di output, Nome destinazione e Estensione di destinazione).If that doesn’t work, you can customize from there, or change the properties in the General section where those values are set (the properties are Output Directory, Target Name, and Target Extension. Tenere presente che se la proprietà che si sta visualizzando usa macro, è possibile scegliere Modifica dall'elenco a discesa per visualizzare la finestra di dialogo che visualizza la stringa finale con le sostituzioni di macro effettuate.Remember that if the property you are viewing uses macros, you can choose Edit in the dropdown list to bring up a dialog box that shows the final string with the macro substitutions made. È possibile visualizzare le macro disponibili e i relativi valori correnti scegliendo il pulsante Macro.You can view all available macros and their current values by choosing the Macros button.

Passaggio 4. Step 4. Aggiornamento della versione di Windows di destinazioneUpdating the Target Windows Version

Il successivo errore indica che la versione WINVER non è supportata in MFC.The next error indicates that WINVER version is no longer supported in MFC. WINVER per Windows XP è 0x0501.WINVER for Windows XP is 0x0501.

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxv_w32.h(40): fatal error C1189: #error:  MFC does not support WINVER less than 0x0501.  Please change the definition of WINVER in your project properties or precompiled header.  

Windows XP non è più supportato da Microsoft, quindi anche se in Visual Studio 2015 è consentita la selezione di questo sistema operativo come destinazione, il relativo supporto dovrebbe essere gradualmente eliminato nelle applicazioni, incoraggiando gli utenti ad adottare le nuove versioni di Windows.Windows XP is no longer supported by Microsoft, so even though targeting it is allowed in Visual Studio 2015, you should be phasing out support for it in your applications, and encouraging your users to adopt new versions of Windows.

Per correggere l'errore, definire WINVER aggiornando l'impostazione Proprietà progetto alla versione meno recente di Windows che si vuole usare come destinazione.To get rid of the error, define WINVER by updating the Project Properties setting to the lowest version of Windows you currently want to target. Una tabella di valori per le varie versioni di Windows è disponibile qui.Find a table of values for various Windows releases here.

Il file stdafx.h contiene alcune di queste definizioni di macro.The stdafx.h file contained some of these macro definitions.

#define WINVER       0x0500  // these defines are set so that we get the  
#define _WIN32_WINNT 0x0500  // maximum set of message/flag definitions,  
#define _WIN32_IE    0x0400  // from both winuser.h and commctrl.h.  

WINVER verrà impostato su Windows 7.WINVER we will set to Windows 7. È più semplice leggere il codice in un secondo momento se si usa la macro per Windows 7 (_WIN32_WINNT_WIN7), anziché il valore stesso (0x0601).It’s easier to read the code later if you use the macro for Windows 7 (_WIN32_WINNT_WIN7), rather than the value itself (0x0601).

#define WINVER _WINNT_WIN32_WIN7 // Minimum targeted Windows version is Windows 7  

Passaggio 5. Step 5. Errori del linkerLinker Errors

Con queste modifiche, il progetto SpyHk (DLL) viene compilato producendo un errore del linker.With these changes, the SpyHk (DLL) project builds but produces a linker error.

LINK : warning LNK4216: Exported entry point _DLLEntryPoint@12  

Il punto di ingresso per una DLL non deve essere esportato.The entry point for a DLL should not be exported. Il punto di ingresso è destinato solo a essere chiamato dal caricatore quando la DLL viene caricata inizialmente in memoria, quindi non deve trovarsi nella tabella di esportazione, che è pertinente ad altri chiamanti.The entry point is only intended to be called by the loader when the DLL is first loaded into memory, so it should not be in the export table, which is for other callers. È necessario assicurarsi che la direttiva __declspec(dllexport) non sia collegata.We just need to make sure it does not have the __declspec(dllexport) directive attached to it. In spyxxhk.c, è necessario rimuoverlo da due posizioni: la dichiarazione e la definizione di DLLEntryPoint.In spyxxhk.c, we have to remove it from two places, the declaration and definition of DLLEntryPoint. Non è mai stato consigliabile usare questa direttiva, ma le versioni precedenti del linker e del compilatore non la contrassegnavano come problema.It never made sense to use this directive, but previous versions of the linker and compiler did not flag it as problem. Le versioni più recenti del linker inviano un avviso.The newer versions of the linker give a warning.

// deleted __declspec(dllexport)  
BOOL WINAPI DLLEntryPoint(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);  

Il progetto DLL in C, SpyHK.dll, ora viene compilato e collegato senza errori.The C DLL project, SpyHK.dll, now builds and links without error.

Passaggio 6. Step 6. Altri file di intestazione obsoletiMore outdated header files

A questo punto si inizia a elaborare il progetto eseguibile principale, Spyxx.At this point we start working on the main executable project, Spyxx.

Non sono stati trovati altri due file di inclusione: ctl3d.h e penwin.h.A couple of other include files could not be found: ctl3d.h and penwin.h. Nonostante possa essere utile cercare in Internet per provare a identificare cosa includesse l'intestazione, in alcuni casi le informazioni non sono granché utili.While it might be helpful to search the Internet to try to identify what included the header, sometimes the information isn’t that helpful. È stato rilevato che ctl3d.h faceva parte di Exchange Development Kit e offriva il supporto per un determinato stile di controlli in Windows 95, mentre penwin.h si riferisce a Window Pen Computing, un'API obsoleta.We found out that ctl3d.h was part of the Exchange Development Kit and provided support for a certain style of controls on Windows 95, and penwin.h relates to Window Pen Computing, an obsolete API. In questo caso, è sufficiente commentare la riga #include e gestire i simboli non definiti come già fatto con il file verstamp.h.In this case, we simply comment out the #include line, and deal with the undefined symbols as we did with verstamp.h. Tutto ciò che si riferisce ai controlli 3D o Pen Computing è stato rimosso dal progetto.Everything that relates to 3D Controls or Pen Computing was removed from the project.

Dato un progetto con molti errori di compilazione che si stanno eliminando gradualmente, non è realistico trovare immediatamente tutti gli usi di un'API obsoleta quando si rimuove la direttiva #include.Given a project with many compilation errors that you are gradually eliminating, it's not realistic to find all the uses of an outdated API right away when you remove the #include directive. Anche se non rilevato immediatamente, a un certo punto si è verificato l'errore WM_DLGBORDER non definito.We didn't detect it immediately, but rather at some later point came to an error that WM_DLGBORDER was undefined. Si tratta semplicemente di uno dei tanti simboli indefiniti provenienti dal file clt3d.h.It is actually just one many undefined symbols that come from ctl3d.h. Dopo avere determinato che si riferisce a un'API non aggiornata, sono stati rimossi tutti i relativi riferimenti nel codice.Once we've determined that it relates to an outdated API, we removed all references in code to it.

Passaggio 7. Step 7. Aggiornamento del vecchio codice iostreamUpdating old iostreams code

L'errore successivo è comune nel vecchio codice C++ che usa iostream.The next error is common with old C++ code that uses iostreams.

mstream.h(40): errore irreversibile C1083: Impossibile aprire file di inclusione: 'iostream.h': Nessun file o directory di questo tipomstream.h(40): fatal error C1083: Cannot open include file: 'iostream.h': No such file or directory

Il problema è che la libreria iostream precedente è stata rimossa e sostituita.The issue is that the old iostreams library has been removed and replaced. È necessario sostituire la vecchia libreria iostream con gli standard più recenti.We have to replace the old iostreams with the newer standards.

#include <iostream.h>  
#include <strstrea.h>  
#include <iomanip.h>  

Questi sono i file di inclusione aggiornati:These are the updated includes:

#include <iostream>  
#include <sstream>  
#include <iomanip>  

Con questa modifica si verificano problemi con ostrstream, che non è più usata.With this change, we have problems with ostrstream, which is no longer used. La sostituzione appropriata è ostringstream.The appropriate replacement is ostringstream. Si è provato ad aggiungere una definizione typedef per ostrstream per evitare di dover modificare eccessivamente il codice, almeno come inizio.We try adding a typedef for ostrstream to avoid modifying the code too much, at least as a start.

typedef std::basic_ostringstream<TCHAR> ostrstream;  

Attualmente il progetto viene creato usando MBCS (Multi-byte Character Set), dunque char è il tipo di dati carattere appropriato.Currently the project is building using MBCS (Multi-byte Character Set), so char is the appropriate character data type. Tuttavia, per facilitare l'aggiornamento del codice a UTF-16 Unicode, viene aggiornato a TCHAR, che si risolve in char o wchar_t a seconda che la proprietà Set di caratteri nelle impostazioni del progetto sia impostata su MBCS o su Unicode.However, to allow an easier update the code to UTF-16 Unicode, we update this to TCHAR, which resolves to char or wchar_t depending on whether the Character Set property in the project settings is set to MBCS or Unicode.

È necessario aggiornare alcuni altri blocchi di codice.A few other pieces of code need to be updated. La classe base ios è stata sostituita da ios_base e ostream è stata sostituita da basic_ostream<T>.We replaced the base class ios with ios_base, and we replaced ostream is by basic_ostream<T>. Si aggiungono altre due definizioni typedef e la sezione viene compilata.We add two additional typedefs, and this section compiles.

typedef std::basic_ostream<TCHAR> ostream;  
typedef ios_base ios;  

L'uso di queste definizioni typedef è solo una soluzione temporanea.Using these typedefs is just a temporary solution. Per una soluzione definitiva, è possibile aggiornare ogni riferimento all'API rinominata oppure obsoleta.For a more permanent solution, we could update each reference to the renamed or outdated API.

Di seguito viene riportato l'errore successivo.Here’s the next error.

error C2039: 'freeze': is not a member of 'std::basic_stringbuf<char,std::char_traits<char>,std::allocator<char>>'  

Il problema successivo è che basic_stringbuf non ha un metodo freeze.The next issue is that basic_stringbuf doesn’t have a freeze method. Il metodo freeze viene usato per evitare una perdita di memoria nella libreria ostream precedente.The freeze method is used to prevent a memory leak in the old ostream. Per il momento non è necessario, visto che si sta usando la nuova libreria ostringstream.We don’t need it now that we’re using the new ostringstream. È possibile eliminare la chiamata da bloccare.We can delete the call to freeze.

//rdbuf()->freeze(0);  

I due errori successivi si sono verificati nelle righe adiacenti.The next two errors occurred on adjacent lines. Il primo errore è relativo all'uso di ends, cioè il vecchio manipolatore IO della libreria iostream, che aggiunge un terminatore null a una stringa.The first complains about using ends, which is the old iostream library’s IO manipulator that adds a null terminator to a string. Il secondo errore spiega che l'output del metodo str non può essere assegnato a un puntatore non-const.The second of these errors explains that the output of the str method can’t be assigned to a non-const pointer.

// Null terminate the string in the buffer and  
// get a pointer to it.  
//  
*this << ends;  
LPSTR psz = str();  
2>mstream.cpp(167): error C2065: 'ends': undeclared identifier2>mstream.cpp(168): error C2440: 'initializing': cannot convert from 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' to 'LPSTR'  

Usando la nuova libreria stream, ends non è necessario, perché la stringa termina sempre con null, quindi è possibile rimuovere tale riga.Using the new stream library, ends is not needed since the string is always null-terminated, so that line can be removed. Riguardo al secondo errore, il problema è che ora str() non restituisce un puntatore a una matrice di caratteri per una stringa, ma restituisce il tipo std::string.For the second issue, the problem is that now str() doesn’t return a pointer to the character array for a string; it returns the std::string type. La soluzione al secondo errore consiste nel modificare il tipo in LPCSTR e usare il metodo c_str() per richiedere un puntatore.The solution to the second is to change the type to LPCSTR and use the c_str() method to request the pointer.

//*this << ends;  
LPCTSTR psz = str().c_str();  

Un errore che sembrava irrisolvibile si è verificato in questo codice.An error that puzzled us for a while occurred on this code.

MOUT << _T(" chUser:'") << chUser  
<< _T("' (") << (INT)(UCHAR)chUser << _T(')');  

La macro MOUT si risolve in *g_pmout, cioè un oggetto di tipo mstream.The macro MOUT resolves to *g_pmout which is an object of type mstream. La classe mstream deriva dalla classe di stringa di output standard, std::basic_ostream<TCHAR>.. Tuttavia, con _T nei pressi del valore letterale stringa, che è stato inserito in preparazione alla conversione in Unicode, la risoluzione dell'overload per l'operatore >> non riesce, con il messaggio di errore seguente:The mstream class is derived from the standard output string class, std::basic_ostream<TCHAR>. However with _T around the string literal, which we put in in preparation for converting to Unicode, the overload resolution for operator << fails with the following error message:

1>winmsgs.cpp(4612): error C2666: 'mstream::operator <<': 2 overloads have similar conversions
1>  c:\source\spyxx\spyxx\mstream.h(120): note: could be 'mstream &mstream::operator <<(ios &(__cdecl *)(ios &))'
1>  c:\source\spyxx\spyxx\mstream.h(118): note: or       'mstream &mstream::operator <<(ostream &(__cdecl *)(ostream &))'
1>  c:\source\spyxx\spyxx\mstream.h(116): note: or       'mstream &mstream::operator <<(ostrstream &(__cdecl *)(ostrstream &))'
1>  c:\source\spyxx\spyxx\mstream.h(114): note: or       'mstream &mstream::operator <<(mstream &(__cdecl *)(mstream &))'
1>  c:\source\spyxx\spyxx\mstream.h(109): note: or       'mstream &mstream::operator <<(LPTSTR)'
1>  c:\source\spyxx\spyxx\mstream.h(104): note: or       'mstream &mstream::operator <<(TCHAR)'
1>  c:\source\spyxx\spyxx\mstream.h(102): note: or       'mstream &mstream::operator <<(DWORD)'
1>  c:\source\spyxx\spyxx\mstream.h(101): note: or       'mstream &mstream::operator <<(WORD)'
1>  c:\source\spyxx\spyxx\mstream.h(100): note: or       'mstream &mstream::operator <<(BYTE)'
1>  c:\source\spyxx\spyxx\mstream.h(95): note: or       'mstream &mstream::operator <<(long)'
1>  c:\source\spyxx\spyxx\mstream.h(90): note: or       'mstream &mstream::operator <<(unsigned int)'
1>  c:\source\spyxx\spyxx\mstream.h(85): note: or       'mstream &mstream::operator <<(int)'
1>  c:\source\spyxx\spyxx\mstream.h(83): note: or       'mstream &mstream::operator <<(HWND)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1132): note: or       'CDumpContext &operator <<(CDumpContext &,COleSafeArray &)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1044): note: or       'CArchive &operator <<(CArchive &,ATL::COleDateTimeSpan)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1042): note: or       'CDumpContext &operator <<(CDumpContext &,ATL::COleDateTimeSpan)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1037): note: or       'CArchive &operator <<(CArchive &,ATL::COleDateTime)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1035): note: or       'CDumpContext &operator <<(CDumpContext &,ATL::COleDateTime)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1030): note: or       'CArchive &operator <<(CArchive &,COleCurrency)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(1028): note: or       'CDumpContext &operator <<(CDumpContext &,COleCurrency)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(955): note: or       'CArchive &operator <<(CArchive &,ATL::CComBSTR)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(951): note: or       'CArchive &operator <<(CArchive &,COleVariant)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxdisp.h(949): note: or       'CDumpContext &operator <<(CDumpContext &,COleVariant)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(248): note: or       'CArchive &operator <<(CArchive &,const RECT &)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(247): note: or       'CArchive &operator <<(CArchive &,POINT)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(246): note: or       'CArchive &operator <<(CArchive &,SIZE)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(242): note: or       'CDumpContext &operator <<(CDumpContext &,const RECT &)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(241): note: or       'CDumpContext &operator <<(CDumpContext &,POINT)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afxwin.h(240): note: or       'CDumpContext &operator <<(CDumpContext &,SIZE)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(1639): note: or       'CArchive &operator <<(CArchive &,const CObject *)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(1425): note: or       'CArchive &operator <<(CArchive &,ATL::CTime)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(1423): note: or       'CDumpContext &operator <<(CDumpContext &,ATL::CTime)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(1418): note: or       'CArchive &operator <<(CArchive &,ATL::CTimeSpan)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(1416): note: or       'CDumpContext &operator <<(CDumpContext &,ATL::CTimeSpan)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\ostream(694): note: or       'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::operator <<<wchar_t,std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,const char *)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\ostream(741): note: or       'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::operator <<<wchar_t,std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,char)'
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\ostream(866): note: or       'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::operator <<<wchar_t,std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,const _Elem *)'
1>          with
1>          [
1>              _Elem=wchar_t
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\ostream(983): note: or       'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::operator <<<wchar_t,std::char_traits<wchar_t>,wchar_t[10]>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &&,const _Ty (&))'
1>          with
1>          [
1>              _Ty=wchar_t [10]
1>          ]
1>  C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\ostream(1021): note: or       'std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &std::operator <<<wchar_t,std::char_traits<wchar_t>>(std::basic_ostream<wchar_t,std::char_traits<wchar_t>> &,const std::error_code &)'
1>  winmsgs.cpp(4612): note: while trying to match the argument list '(CMsgStream, const wchar_t [10])'  

Ci sono talmente tante definizioni di operatore >> che questo tipo di errore può incutere timore.There are so many operator << definitions that this kind of error can be intimidating. Dopo un'osservazione più attenta degli overload disponibili, è possibile dedurre che molti di essi sono irrilevanti ed esaminando più da vicino la definizione di classe mstream è stata identificata la funzione seguente, che dovrebbe essere chiamata in questo caso.After looking more closely at the available overloads, we can see that most of them are irrelevant, and looking more closely at the mstream class definition, we identified the following function that we think should be called in this case.

mstream& operator<<(LPTSTR psz)  
{  
  return (mstream&)ostrstream::operator<<(psz);  
}  

Il motivo per cui non viene chiamata è che il valore letterale stringa ha il tipo const wchar_t[10], come è possibile vedere dall'ultima riga di quel lungo messaggio di errore, dunque la conversione a un puntatore non-const non è automatica.The reason it isn't called is because the string literal has the type const wchar_t[10] as you can see from the last line of that long error message, so the conversion to a non-const pointer is not automatic. Tuttavia, l'operatore non dovrebbe modificare il parametro di input, dunque il parametro più appropriato è LPCTSTR (const char* durante la compilazione come MBCS e const wchar_t* come Unicode), non LPTSTR (char* durante la compilazione come MBCS e wchar_t* come Unicode).However that operator should not modify the input parameter, so the more appropriate parameter type is LPCTSTR (const char* when compiling as MBCS, and const wchar_t* as Unicode), not LPTSTR (char* when compiling as MBCS, and wchar_t* as Unicode). Tale modifica consente di correggere l'errore.Making that change fixes this error.

Questo tipo di conversione era consentito con il compilatore precedente, meno rigoroso, ma le modifiche di conformità più recenti richiedono un codice più corretto.This type of conversion was allowed under the older, less strict compiler, but more recent conformance changes require more correct code.

Passaggio 8. Step 8. Conversioni più rigorose del compilatoreThe compiler's more strict conversions

Vengono visualizzati anche molti errori come il seguente:We also get many errors like the following:

error C2440: 'static_cast': cannot convert from 'UINT (__thiscall CHotLinkCtrl::* )(CPoint)' to 'LRESULT (__thiscall CWnd::* )(CPoint)'  

L'errore si verifica in una mappa messaggi che è semplicemente una macro:The error occurs in a message map that is simply a macro:

BEGIN_MESSAGE_MAP(CFindToolIcon, CWnd)  
// other messages omitted...  
ON_WM_NCHITTEST() // Error occurs on this line.  
END_MESSAGE_MAP()  

Passando alla definizione di questa macro, si nota che fa riferimento alla funzione OnNcHitTest.Going to the definition of this macro, we see it references the function OnNcHitTest.

#define ON_WM_NCHITTEST() \  
{ WM_NCHITTEST, 0, 0, 0, AfxSig_l_p, \  
(AFX_PMSG)(AFX_PMSGW) \  
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(CPoint) > (&ThisClass :: OnNcHitTest)) },  

Il problema ha a che fare con la mancata corrispondenza nel puntatore ai tipi di funzione membro.The problem has to do with the mismatch in the pointer to member function types. Il problema non è la conversione dal tipo di classe CHotLinkCtrl al tipo di classe CWnd, perché è una valida conversione derivata dalla base.The problem isn’t the conversion from CHotLinkCtrl as a class type to CWnd as the class type, since that is a valid derived-to-base conversion. Il problema è il tipo restituito: UINT rispetto a LRESULT.The problem is the return type: UINT vs. LRESULT. LRESULT si risolve in LONG_PTR che è un puntatore a 64 bit o un puntatore a 32 bit, a seconda del tipo binario di destinazione, quindi UINT non viene convertito in questo tipo.LRESULT resolves to LONG_PTR which is a 64-bit pointer or a 32-bit pointer, depending on the target binary type, so UINT does not convert to this type. Ciò non è insolito quando si aggiorna il codice scritto prima del 2005, perché il tipo restituito di molti metodi di mappa del messaggio sono cambiati da UINT a LRESULT in Visual Studio 2005, come parte delle modifiche di compatibilità a 64 bit.This is not uncommon when upgrading code written before 2005 since the return type of many message map methods changed from UINT to LRESULT in Visual Studio 2005 as part of the 64-bit compatibility changes. Si modifica il tipo restituito nel codice seguente da UINT a LRESULT:We change the return type from UINT in the following code to LRESULT:

afx_msg UINT OnNcHitTest(CPoint point);  

Dopo la modifica è visibile il codice riportato di seguito:After the change we have the following code:

afx_msg LRESULT OnNcHitTest(CPoint point);  

Dal momento che esistono circa dieci occorrenze di questa funzione in diverse classi derivate da CWnd, è opportuno usare Vai a definizione (tastiera: F12) e Vai a dichiarazione (tastiera: Ctrl + F12) quando il cursore si trova nella funzione nell'editor per trovare le occorrenze e passare ad esse dalla finestra degli strumenti Trova simbolo.Since there are about ten occurrences of this function all in different classes derived from CWnd, it’s helpful to use Go to Definition (Keyboard: F12) and Go to Declaration (Keyboard: Ctrl+F12) when the cursor is on the function in the editor to locate these and navigate to them from the Find Symbol tool window. Vai a definizione è in genere l'opzione più utile.Go to Definition is usually the more useful of the two. Vai a dichiarazione trova le dichiarazioni diverse dalla dichiarazione di classe di definizione, ad esempio le dichiarazioni di classe friend o i riferimenti in avanti.Go to Declaration will find declarations other than the defining class declaration, such as friend class declarations or forward references.

Passaggio 9. Step 9. Modifiche MFCMFC Changes

L'errore successivo riguarda anche a un tipo di dichiarazione modificato e si verifica anche in una macro.The next error also relates to a changed declaration type and also occurs in a macro.

error C2440: 'static_cast': cannot convert from 'void (__thiscall CFindWindowDlg::* )(BOOL,HTASK)' to 'void (__thiscall CWnd::* )(BOOL,DWORD)'  

Il problema è che il secondo parametro di CWnd::OnActivateApp è cambiato da HTASK a DWORD.The issue is that the second parameter of CWnd::OnActivateApp changed from HTASK to DWORD. Questa modifica si è verificata nella versione 2002 di Visual Studio, Visual Studio .NET.This change occurred in the 2002 release of Visual Studio, Visual Studio .NET.

afx_msg void OnActivateApp(BOOL bActive, HTASK hTask);  

È necessario aggiornare le dichiarazioni di OnActivateApp nelle classi derivate di conseguenza come segue:We have to update the declarations of OnActivateApp in derived classes accordingly as follows:

afx_msg void OnActivateApp(BOOL bActive, DWORD dwThreadId);  

A questo punto, è possibile compilare il progetto.At this point, we are able to compile the project. Tuttavia, occorre gestire alcuni avvisi e componenti facoltativi dell'aggiornamento, ad esempio la conversione da MBCS a Unicode o il miglioramento della sicurezza usando le funzioni CRT sicure.There are a few warnings to work through, however, and there are optional parts of the upgrade, such as converting from MBCS to Unicode or improving security by using the Secure CRT functions.

Passaggio 10. Step 10. Risoluzione degli avvisi del compilatoreAddressing compiler warnings

Per ottenere l'elenco completo degli avvisi, invece di una compilazione normale è necessario eseguire l'opzione Ricompila tutto nella soluzione, per assicurarsi che tutto ciò che è stato compilato in precedenza verrà ricompilato, dato che vengono visualizzati solo i report di avviso della compilazione corrente.To get a full list of warnings, you should do a Rebuild All on the solution rather than an ordinary build, just to make sure that everything that previously compiled will be recompiled, since you only get warning reports from the current compilation. L'altra questione riguarda se accettare il livello di avviso corrente o usare un maggiore livello di avviso.The other question is whether to accept the current warning level or use a higher warning level. Durante il porting di una grande quantità di codice, specialmente codice obsoleto, l'uso di un maggiore livello di avviso potrebbe essere appropriato.When porting a lot of code, especially old code, using a higher warning level might be appropriate. Può anche essere utile iniziare con il livello di avviso predefinito e quindi aumentare il livello di avviso fino a ricevere tutti gli avvisi.You might also want to start with the default warning level and then increase the warning level to get all warnings. Se si usa l'opzione /Wall, si riceveranno alcuni avvisi nei file di intestazione del sistema; molti utenti usano l'opzione /W4 per rivedere gran parte degli avvisi relativi al codice senza ricevere quelli relativi alle intestazioni del sistema.If you use /Wall, you get some warnings in the system header files, so many people use /W4 to get the most warnings on their code without getting warnings for system headers. Se si vuole che gli avvisi vengano visualizzati come errori, aggiungere l'opzione /WX.If you want warnings to show up as errors, add the /WX option. Queste impostazioni sono nella sezione C/C++ della finestra di dialogo Proprietà del progetto.These settings are in the C/C++ section of the Project Properties dialog box.

Uno dei metodi nella classe CSpyApp genera un avviso relativo a una funzione che non è più supportata.One of the methods in the CSpyApp class produces a warning about a function that is no longer supported.

void SetDialogBkColor() {CWinApp::SetDialogBkColor(::GetSysColor(COLOR_BTNFACE));}  

L'avviso è indicato di seguito:The warning is as follows.

warning C4996: 'CWinApp::SetDialogBkColor': CWinApp::SetDialogBkColor is no longer supported. Instead, handle WM_CTLCOLORDLG in your dialog  

Il messaggio WM_CTLCOLORDLG è già stato gestito nel codice di Spy++, quindi l'unica modifica necessaria era eliminare i riferimenti a SetDialogBkColor, che non è più necessario.The message WM_CTLCOLORDLG was already handled in Spy++ code, so the only change required was to delete any references to SetDialogBkColor, which is no longer needed.

L'avviso successivo era facile da risolvere commentando il nome della variabile.The next warning was straightforward to fix by commenting out the variable name. È stato ricevuto l'avviso seguente:We received the following warning:

warning C4456: declaration of 'lpszBuffer' hides previous local declaration  

Il codice che produce questo avviso include una macro.The code that produces this involves a macro.

DECODEPARM(CB_GETLBTEXT)  
{  
  P2WPOUT();  

  P2LPOUTPTRSTR;  
  P2IFDATA()  
  {  
    PARM(lpszBuffer, PPACK_STRINGORD, ED2);  

    INDENT();  

    P2IFISORD(lpszBuffer)  
    {  
      P2OUTORD(lpszBuffer);  
    }  
    else  
    {  
      PARM(lpszBuffer, LPTSTR, ED2);  
      P2OUTS(lpszBuffer);  
    }  
  }  
}  

Un uso intensivo di macro come in questo codice tende a rendere il codice più difficile da gestire.Heavy use of macros as in this code tends to make code harder to maintain. In questo caso, le macro includono le dichiarazioni delle variabili.In this case, the macros include the declarations of the variables. La macro PARM è definita come segue:The macro PARM is defined as follows:

#define PARM(var, type, src)type var = (type)src  

Di conseguenza, la variabile lpszBuffer viene dichiarata due volte nella stessa funzione.Therefore the lpszBuffer variable gets declared twice in the same function. Non è così lineare risolvere questo avviso come lo sarebbe se il codice non stesse usando le macro; ad ogni modo, basta semplicemente rimuovere la seconda dichiarazione del tipo.It's not that straightfoward to fix this as it would be if the code were not using macros (simply remove the second type declaration). Allo stato dei fatti, si ha l'ingrato compito di dover decidere se riscrivere il codice della macro come codice ordinario (un'attività tediosa e soggetta a errori) oppure disabilitare l'avviso.As it is, we have the unfortunate choice of having to decide whether to rewrite the macro code as ordinary code (a tedious and possibly error-prone task) or disable the warning.

In questo caso, si è scelto di disabilitare l'avviso.In this case, we opt to disable the warning. A tale scopo, aggiungere un pragma come segue:We can do that by adding a pragma as follows:

#pragma warning(disable : 4456)  

Quando si disabilita un avviso, è possibile limitare l'effetto della disattivazione al solo codice che genera l'avviso, per evitare di eliminare l'avviso quando può invece fornire informazioni utili.When disabling a warning, you might want to restrict the disabling effect to just the code you that produces the warning, to avoid suppressing the warning when it might provide useful information. Si aggiunge il codice per ripristinare l'avviso poco dopo la riga che lo genera o, ancora meglio, siccome questo avviso viene generato in una macro, usare la parola chiave __pragma, che funziona nelle macro (#pragma non funziona nelle macro).We add code to restore the warning just after the line that produces it, or better yet, since this warning occurs in a macro, use the __pragma keyword, which works in macros (#pragma does not work in macros).

#define PARM(var, type, src)__pragma(warning(disable : 4456))  \  
type var = (type)src \  
__pragma(warning(default : 4456))  

L'avviso successivo richiede alcune revisioni del codice.The next warning requires some code revisions. Win32 API GetVersion (e GetVersionEx) è deprecata.The Win32 API GetVersion (and GetVersionEx) is deprecated.

warning C4996: 'GetVersion': was declared deprecated  

Il codice seguente illustra come viene ottenuta la versione.The following code shows how the version is obtained.

// check Windows version and set m_bIsWindows9x/m_bIsWindows4x/m_bIsWindows5x flags accordingly.  
DWORD dwWindowsVersion = GetVersion();  

È seguito da una grande quantità di codice che esamina il valore dwWindowsVersion per determinare se è in esecuzione su Windows 95 e quale versione di Windows NT.This is followed by a lot of code that examines the dwWindowsVersion value to determine whether we're running on Windows 95, and which version of Windows NT. Dal momento che sono tutte obsolete, è necessario rimuovere il codice e gestire tutti i riferimenti a tali variabili.Since this is all outdated, we remove the code and deal with any references to those variables.

L'articolo Operating system version changes in Windows 8.1 and Windows Server 2012 R2 (Modifiche alla versione del sistema operativo in Windows 8.1 e Windows Server 2012 R2) illustra la situazione.The article Operating system version changes in Windows 8.1 and Windows Server 2012 R2 explains the situation.

Esistono metodi nella classe CSpyApp che interrogano la versione del sistema operativo: IsWindows9x, IsWindows4x e IsWindows5x.There are methods in the CSpyApp class that query the operating system version: IsWindows9x, IsWindows4x and IsWindows5x. Un buon punto di partenza consiste nel presupporre che le versioni di Windows che si intende supportare (Windows 7 e versioni successive) siano tutte simili a Windows NT 5 per quanto riguarda le tecnologie usate da questa applicazione.A good starting point is to assume that the versions of Windows that we intend to support (Windows 7 and later) are all close to Windows NT 5 as far the technologies used by this older application is concerned. Questi metodi venivano usati per gestire le limitazioni dei sistemi operativi meno recentiThe uses of these methods were to deal with limitations of the older operating systems. e sono stati quindi modificati in modo da restituire TRUE per IsWindows5x e FALSE per gli altri.So we changed those methods to return TRUE for IsWindows5x and FALSE for the others.

BOOL IsWindows9x() {/*return(m_bIsWindows9x);*/ return FALSE;  }  
BOOL IsWindows4x() {/*return(m_bIsWindows4x);*/ return FALSE;  }  
BOOL IsWindows5x() {/*return(m_bIsWindows5x);*/ return TRUE;  }  

Sono dunque rimaste solo alcune situazioni in cui le variabili interne veniva usate direttamente.That left only a few places where the internal variables were used directly. Dal momento che tali variabili sono state rimosse, si sono verificati alcuni errore che devono essere gestiti in modo esplicito.Since we removed those variables, we get a few errors that have to deal with explicitly.

error C2065: 'm_bIsWindows9x': undeclared identifier  
void CSpyApp::OnUpdateSpyProcesses(CCmdUI *pCmdUI)  
{  
  pCmdUI->Enable(m_bIsWindows9x || hToolhelp32 != NULL);  
}  

È stato possibile sostituirla con una chiamata al metodo o semplicemente passare TRUE e rimuovere il precedente caso speciale per Windows 9x.We could replace this with a method call or simply pass TRUE and remove the old special case for Windows 9x.

void CSpyApp::OnUpdateSpyProcesses(CCmdUI *pCmdUI)  
{  
  pCmdUI->Enable(TRUE /*!m_bIsWindows9x || hToolhelp32 != NULL*/);  
}  

L'avviso finale al livello predefinito (3) ha a che fare con un campo di bit.The final warning at the default level (3) has to do with a bitfield.

treectl.cpp(1656): warning C4463: overflow; assigning 1 to bit-field that can only hold values from -1 to 0  

Il codice che lo attiva è il seguente:The code that triggers this is as follows.

m_bStdMouse = TRUE;  

La dichiarazione di m_bStdMouse indica che è un campo di bit.The declaration of m_bStdMouse indicates that it is a bitfield.

class CTreeListBox : public CListBox  
{  
  DECLARE_DYNCREATE(CTreeListBox)  

  CTreeListBox();  

  private:  
  int ItemFromPoint(const CPoint& point);  

  class CTreeCtl* m_pTree;  
  BOOL m_bGotMouseDown : 1;  
  BOOL m_bDeferedDeselection : 1;  
  BOOL m_bStdMouse : 1;  

Questo codice è stato scritto prima che il tipo bool predefinito fosse supportato in Visual C++.This code was written before the built-in bool type was supported in Visual C++. In tale codice, BOOL era un typedef per int. Il tipo int è un tipo con segno e la rappresentazione di bit di un tipo signed int consiste nell'usare il primo bit come bit di segno, in modo che un campo di bit di tipo int possa essere interpretato come 0 o -1, in modo probabilmente diverso da quanto previsto.In such code, BOOL was a typedef for int. The type int is a signed type, and the bit representation of a signed int is to use the first bit as a sign bit, so a bitfield of type int could be interpreted as representing 0 or -1, probably not what was intended.

Con una semplice osservazione del codice non si direbbe che questi siano campi di bit.You wouldn't know by looking at the code why these are bitfields. Forse l'intento era di mantenere le dimensioni dell'oggetto ridotte, oppure in qualche punto è stato usato il layout binario dell'oggetto?Was the intent to keep the size of the object small, or is there anywhere where the binary layout of the object is used? I campi di bit sono stati modificati in membri BOOL ordinari, perché non vi era alcun motivo di usare un campo di bit.We changed these to ordinary BOOL members since we didn't see any reason for the use of a bitfield. L'uso dei campi di bit per mantenere ridotte le dimensioni di un oggetto non sempre funziona:Using bitfields to keep an object's size small isn't guaranteed to work. dipende dalla modalità di layout del tipo da parte del compilatore.It depends on how the compiler lays out the type.

Ci si potrebbe chiedere se usare sempre il tipo bool standard possa essere utile.You might wonder if using the standard type bool throughout would be helpful. Molti dei modelli di codice precedenti, ad esempio il tipo BOOL, furono inventati per risolvere i problemi che sono stati risolti in seguito in C++ standard, quindi la modifica da BOOL al tipo incorporato bool è solo un esempio di una modifica che si potrebbe considerare dopo aver iniziato l'esecuzione del codice nella nuova versione.Many of the old code patterns such as the BOOL type were invented to solve problems that were later solved in standard C++, so changing from BOOL to the bool built-in type is just one example of such a change that you consider doing after you get your code initially running in the new version.

Dopo aver gestito tutti gli avvisi visualizzati a livello predefinito (livello 3), è stato eseguito il passaggio al livello 4 per ricevere alcuni avvisi aggiuntivi.Once we've dealt with all the warnings that appear at the default level (level 3) we changed to level 4 to catch a few additional warnings. Il primo avviso visualizzato era il seguente:The first to appear was as follows:

warning C4100: 'nTab': unreferenced formal parameter  

Il codice che ha generato l'avviso è indicato di seguito.The code that produced this warning was as follows.

virtual void OnSelectTab(int nTab) {};  

Sembra abbastanza innocuo, ma siccome serviva una raccolta pulita con il set di opzioni /W4 e /WX, è stato aggiunto un commento con il nome della variabile, lasciato a scopo di leggibilità.This seems harmless enough, but since we wanted a clean compilation with /W4 and /WX set, we simply commented out the variable name, leaving it for the sake of readability.

virtual void OnSelectTab(int /*nTab*/) {};  

Altri avvisi pervenuti si sono resi utili per la pulizia generale del codice.Other warnings we received were useful for general code cleanup. Esiste una serie di conversioni implicite da int o unsigned int a WORD (un typedef per unsigned short),There are a number of implicit conversions from int or unsigned int to WORD (which is a typedef for unsigned short). che implicano una possibile perdita di dati.These involve a possible loss of data. In questi casi, è stato aggiunto un cast a WORD.We added a cast to WORD in these cases.

Un altro avviso di livello 4 ricevuto per questo codice è stato:Another level 4 warning we got for this code was:

warning C4211: nonstandard extension used: redefined extern to static  

Il problema si verifica quando una variabile è stata inizialmente dichiarata extern, quindi dichiarata in un secondo momento static.The problem occurs when a variable was first declared extern, then later declared static. I significati di questi due identificatori di classe di archiviazione si escludono a vicenda, ma ciò è consentito come estensione Microsoft.The meaning of these two storage class specifiers is mutually exclusive, but this is allowed as a Microsoft extension. Se si voleva che il codice fosse portabile ad altri compilatori o si voleva eseguire la compilazione con /Za (compatibilità ANSI), sarebbe stato necessario modificare le dichiarazioni in modo da avere identificatori di classe di archiviazione corrispondenti.If you wanted the code to be portable to other compilers, or you wanted to compile it with /Za (ANSI compatibility), you would change the declarations to have matching storage class specifiers.

Passaggio 11. Step 11. Porting da MBCS a UnicodePorting from MBCS to Unicode

Quando ci si riferisce a Unicode in ambito Windows in genere si intende UTF-16.Note that in the Windows world, when we say Unicode, we usually mean UTF-16. Altri sistemi operativi, ad esempio Linux, usano UTF-8, ma Windows in genere non lo usa.Other operating systems such as Linux use UTF-8, but Windows generally does not. Prima di eseguire effettivamente il porting del codice MBCS in UTF-16 Unicode, potrebbe essere opportuno eliminare temporaneamente gli avvisi che informano che il formato MBCS è deprecato, per poter eseguire altre operazioni o posticipare il porting fino a un momento appropriato.Before taking the step to actually port MBCS code to UTF-16 Unicode, we might want to temporarily eliminate the warnings that MBCS is deprecated, in order to do other work or postpone the porting until a convenient time. Il codice corrente usa il formato MBCS e per continuare è necessario scaricare la versione MBCS di MFC.The current code uses MBCS and to continue with that we need to download the MBCS version of MFC. Questa libreria piuttosto imponente è stata rimossa dall'installazione predefinita di Visual Studio, quindi deve essere scaricata separatamente.This rather large library was removed from the default Visual Studio installation, so it must be downloaded separately. Vedere Componente aggiuntivo DLL MBCS MFC.See MFC MBCS DLL Add-on. Dopo averla scaricata e aver riavviato Visual Studio, è possibile compilarla e collegarla alla versione MBCS di MFC, ma per eliminare gli avvisi relativi a MBCS è necessario aggiungere anche NO_WARN_MBCS_MFC_DEPRECATION all'elenco di macro predefinite nella sezione Preprocessor delle proprietà del progetto oppure all'inizio del file di intestazione stdafx.h o di un altro file di intestazione comune.Once you download this and restart Visual Studio, you can compile and link with the MBCS version of MFC, but to get rid of the warnings about MBCS, you should also add NO_WARN_MBCS_MFC_DEPRECATION to your list of predefined macros in the Preprocessor section of project properties, or at the beginning of your stdafx.h header file or other common header file.

Verranno ora presi in esame alcuni errori del linker.We now have some linker errors.

fatal error LNK1181: cannot open input file 'mfc42d.lib'  

L'errore LNK1181 si verifica perché una versione obsoleta di libreria statica di MFC è inclusa nell'input del linker.LNK1181 occurs because an outdated static library version of mfc is included on the linker input. Ciò non è più necessario dal momento che è possibile eseguire un collegamento dinamico a MFC, dunque occorre solo rimuovere tutte le librerie statiche di MFC dalla proprietà Input nella sezione Linker delle proprietà del progetto.This isn’t required anymore since we can link MFC dynamically, so we just need to remove all MFC static libraries from the Input property in the Linker section of the project properties. Questo progetto usa anche l'opzione /NODEFAULTLIB ed elenca invece tutte le dipendenze della libreria.This project is also using the /NODEFAULTLIB option, and instead it lists all the library dependencies.

msvcrtd.lib;msvcirtd.lib;kernel32.lib;user32.lib;gdi32.lib;advapi32.lib;Debug\SpyHk55.lib;%(AdditionalDependencies)  

È possibile aggiornare effettivamente il codice MBCS (Multi-byte Character Set) a Unicode.Now let us actually update the old Multi-byte Character Set (MBCS) code to Unicode. Dal momento che si tratta di un'applicazione Windows, strettamente connessa alla piattaforma desktop di Windows, verrà eseguito il porting alla codifica Unicode UTF-16 usata da Windows.Since this is a Windows application, intimately tied to the Windows desktop platform, we will port it to UTF-16 Unicode that Windows uses. Se si scrive il codice multipiattaforma o si esegue il porting di un'applicazione Windows su un'altra piattaforma, può essere opportuno considerare il porting alla codifica UTF-8, ampiamente usata sugli altri sistemi operativi.If you are writing cross-platform code or porting a Windows application to another platform, you might want to consider porting to UTF-8, which is widely used on other operating systems.

Per il porting alla codifica UTF-16 Unicode, è necessario decidere se si vuole ancora che l'opzione compili il set MBCS.Porting to UTF-16 Unicode, we must decide whether we still want the option to compile to MBCS or not. Se si vuole che l'opzione supporti MBCS, è necessario usare la macro TCHAR come tipo di carattere, che si risolve in char o wchar_t, a seconda che durante la compilazione si sia definito _MBCS o _UNICODE.If we want to have the option to support MBCS, we should use the TCHAR macro as the character type, which resolves to either char or wchar_t, depending on whether _MBCS or _UNICODE is defined during compilation. Il passaggio a TCHAR e alle versioni TCHAR delle varie API invece di wchar_t e le relative API associate significa che è possibile ripristinare una versione MBCS del codice semplicemente definendo la macro _MBCS invece di _UNICODE.Switching to TCHAR and the TCHAR versions of various APIs instead of wchar_t and its associated APIs means that you can get back to an MBCS version of your code simply by defining _MBCS macro instead of _UNICODE. Oltre a TCHAR, esiste un'ampia gamma di versioni TCHAR come typedef, macro e funzioni ampiamente usati,In addition to TCHAR, a variety of TCHAR versions of such as widely used typedefs, macros, and functions exists. ad esempio, LPCTSTR invece LPCSTR e così via.For example, LPCTSTR instead of LPCSTR, and so on. Nella finestra di dialogo Proprietà del progetto, sotto Proprietà di configurazione, nella sezione Generale modificare la proprietà Set di caratteri da Utilizza set di caratteri multibyte a Utilizza set di caratteri Unicode.In the project properties dialog, under Configuration Properties, in the General section, change the Character Set property from Use MBCS Character Set to Use Unicode Character Set. Questa impostazione incide su quale macro viene definita durante la compilazione.This setting affects which macro is predefined during compilation. Esiste sia una macro UNICODE sia una macro _UNICODE.There is both a UNICODE macro and a _UNICODE macro. La proprietà del progetto influisce su entrambe in modo coerente.The project property affects both consistently. Le intestazioni di Windows usano il formato UNICODE se le intestazioni di Visual C++ come MFC usano _UNICODE, ma una è definito, l'altra è sempre definito.Windows headers use UNICODE where Visual C++ headers such as MFC use _UNICODE, but when one is defined, the other is always defined.

È disponibile un'utile guida al porting da MBCS a UTF-16 Unicode mediante TCHAR.A good guide to porting from MBCS to UTF-16 Unicode using TCHAR exists. È la direzione scelta per questo articolo.We choose this route. In primo luogo si converte la proprietà Set di caratteri in Utilizza set di caratteri Unicode e il progetto viene ricompilato.First, we change the Character Set property to Use Unicode Character Set and rebuild the project.

TCHAR era già usato in alcuni punti del codice, apparentemente in previsione di un eventuale supporto di Unicode,Some places in the code were already using TCHAR, apparently in anticipation of eventually supporting Unicode. mentre in altri non era presente.Some were not. È stata eseguita una ricerca delle istanze di CHAR, cioè un typedef per char, sostituendo la gran parte di esse con TCHAR.We searched for instances of CHAR, which is a typedef for char, and replaced most of them with TCHAR. È stata anche eseguita la ricerca di sizeof (CHAR).Also, we looked for sizeof (CHAR). Ogni volta che si è passato da CHAR a TCHAR, in genere era necessario passare a sizeof(TCHAR) perché era spesso usato per determinare il numero di caratteri in una stringa.Whenever we changed from CHAR to TCHAR, we usually had to change to sizeof(TCHAR) since this was often used to determine the number of characters in a string. L'uso di un tipo errato in questo caso non produce un errore del compilatore, quindi vale la pena prestare un po' d'attenzione.Using the wrong type here does not produce a compiler error, so it's worth paying a bit of attention to this case.

Questo tipo di errore è molto comune solo dopo il passaggio a Unicode.This type of error is very common just after switching to Unicode.

error C2664: 'int wsprintfW(LPWSTR,LPCWSTR,...)': cannot convert argument 1 from 'CHAR [16]' to 'LPWSTR'  

Ecco un esempio di codice che genera questo errore:Here’s an example of code that produces this:

wsprintf(szTmp, "%d.%2.2d.%4.4d", rmj, rmm, rup);  

È stata inserita la macro _T nei pressi del valore letterale stringa per rimuovere l'errore.We put _T around the string literal to remove the error.

wsprintf(szTmp, _T("%d.%2.2d.%4.4d"), rmj, rmm, rup);  

La macro _T fa sì che il valore letterale stringa venga compilato come stringa char o stringa wchar_t, a seconda dell'intestazione di MBCS o UNICODE.The _T macro has the effect of making a string literal compile as a char string or a wchar_t string, depending on the setting of MBCS or UNICODE. Per sostituire tutte le stringhe con _T in Visual Studio, aprire prima Sostituzione veloce (tastiera: Ctrl + F) oppure Sostituisci nei file (tastiera: Ctrl + Maiusc + H), quindi scegliere la casella di controllo Utilizza espressioni regolari.To replace all strings with _T in Visual Studio, first open the Quick Replace (Keyboard: Ctrl+F) box or the Replace In Files (Keyboard: Ctrl+Shift+H), then choose the Use Regular Expressions checkbox. Immettere ((\".*?\")|('.+?')) come testo di ricerca e _T($1) come testo di sostituzione.Enter ((\".*?\")|('.+?')) as the search text and _T($1) as the replacement text. Se la macro _T è già presente in alcune stringhe, questa procedura l'aggiungerà di nuovo e potrebbe anche trovare alcuni casi in cui non è disponibile, ad esempio quando si usa #include; di conseguenza, è preferibile usare Sostituisci successivo anziché Sostituisci tutto.If you already have the _T macro around some strings, this procedure will add it again, and it might also find cases where you don't want _T, such as when you use #include, so it's best to use Replace Next rather than Replace All.

Questa funzione particolare, wsprintf, viene effettivamente definita nelle intestazioni di Windows e la relativa documentazione consiglia di non usarla, a causa del possibile sovraccarico del buffer.This particular function, wsprintf, is actually defined in the Windows headers, and the documentation for it recommends that it not be used, due to possible buffer overrun. Non è specificata una dimensione per il buffer szTmp, quindi non esiste alcun modo per la funzione di verificare che il buffer possa contenere tutti i dati da scrivere.No size is given for the szTmp buffer, so there is no way for the function to check that the buffer can hold all the data to be written to it. Vedere la sezione successiva sul porting alle funzioni CRT sicure, in cui è possibile risolvere altri problemi simili.See the next section about porting to the Secure CRT, in which we fix other similar problems. Alla fine è stata sostituita da _stprintf_s.We ended up replacing it with _stprintf_s.

Un altro errore comune che si può osservare nella conversione in Unicode è il seguente.Another common error you’ll see in converting to Unicode is this.

error C2440: '=': cannot convert from 'char *' to 'TCHAR *'  

Il codice che lo genera è il seguente:The code that produces it is as follows:

pParentNode->m_szText = new char[strTitle.GetLength() + 1];  
_tcscpy(pParentNode->m_szText, strTitle);  

Anche se è stata usata la funzione _tcscpy, cioè la funzione strcpy TCHAR per la copia di una stringa, il buffer allocato era un buffer char.Even though the _tcscpy function was used, which is the TCHAR strcpy function for copying a string, the buffer that was allocated was a char buffer. È possibile modificarla semplicemente in TCHAR.This is easily changed to TCHAR.

pParentNode->m_szText = new TCHAR[strTitle.GetLength() + 1];  
_tcscpy(pParentNode->m_szText, strTitle);  

Analogamente, sono stati modificati LPSTR (puntatore di tipo long a STRing) e LPCSTR (puntatore di tipo long a Constant STRing) rispettivamente in LPTSTR (puntatore di tipo long a TCHAR STRing) e LPCTSTR (puntatore di tipo long a Constant TCHAR STRing), quando garantito da un errore del compilatore.Similarly, we changed LPSTR (Long Pointer to STRing) and LPCSTR (Long Pointer to Constant STRing) to LPTSTR (Long Pointer to TCHAR STRing) and LPCTSTR (Long Pointer to Constant TCHAR STRing) respectively, when warranted by a compiler error. Si è scelto di non effettuare tali sostituzioni usando operazioni globali di ricerca e sostituzione, perché ogni situazione doveva essere esaminata singolarmente.We chose not to make such replacements by using global search and replace, because each situation had to be examined individually. In alcuni casi, è necessaria la versione char, ad esempio durante l'elaborazione di alcuni messaggi di Windows che usano strutture di Windows con il suffisso A.In some cases, the char version is wanted, such as when processing certain Windows messages which use Windows structures that have the A suffix. Nell'API di Windows, il suffisso A significa ASCII o ANSI (e si applica anche a MBCS) e il suffisso W significa caratteri estesi o UTF-16 Unicode.In the Windows API, the suffix A means ASCII or ANSI (and also applies to MBCS), and the suffix W means wide characters, or UTF-16 Unicode. Questo modello di denominazione viene usato nelle intestazioni di Windows, ma è stato anche adottato nel codice di Spy++, quando si è reso necessario aggiungere una versione Unicode di una funzione che era già stata definita solo in una versione di MBCS.This naming pattern is used in the Windows headers, but we also followed it in the Spy++ code when we had to add a Unicode version of a function that was already defined in only an MBCS version.

In alcuni casi è stato necessario sostituire un tipo in modo da usare una versione che si risolvesse correttamente (WNDCLASS anziché WNDCLASSA, ad esempio).In some cases we had to replace a type to use a version that resolves correctly (WNDCLASS instead of WNDCLASSA for example).

In molti casi si è reso necessario usare la versione generica (macro) di un'API Win32, come GetClassName (anziché GetClassNameA).In many cases we had to use the generic version (macro) of a Win32 API like GetClassName (instead of GetClassNameA). Nell'istruzione switch del gestore messaggi, alcuni messaggi sono specifici di MBCS o Unicode; in quei casi si è reso necessario modificare il codice in modo da chiamare esplicitamente la versione MBCS, perché sono state sostituite le funzioni dai nomi generici con le funzioni specifiche A e W, quindi è stata aggiunta una macro per il nome generico che si risolve nel nome A o W corretto, in base all'eventuale definizione di UNICODE.In message handler switch statement, some messages are MBCS or Unicode specific, in those cases, we had to change the code to explicitly call the MBCS version, because we replaced the generically named functions with A and W specific functions, and added a macro for the generic name that resolves to the correct A or W name based on whether UNICODE is defined. In molte parti del codice, quando si è eseguito il passaggio alla definizione di _UNICODE, viene scelta la versione W anche se l'obiettivo è la versione A.In many parts of the code, when we switched to define _UNICODE, the W version is now chosen even when the A version is what's wanted.

In alcuni casi è stato necessario adottare misure speciali.There are a few places where special actions had to be taken. Qualsiasi uso di WideCharToMultiByte o MultiByteToWideChar potrebbe richiedere uno sguardo più attento.Any use of WideCharToMultiByte or MultiByteToWideChar might require a closer look. Ecco un esempio del punto in cui veniva usato WideCharToMultiByte.Here's one example where WideCharToMultiByte was being used.

BOOL C3dDialogTemplate::GetFont(CString& strFace, WORD& nFontSize)  
{  
  ASSERT(m_hTemplate != NULL);  

  DLGTEMPLATE* pTemplate = (DLGTEMPLATE*)GlobalLock(m_hTemplate);  
  if ((pTemplate->style & DS_SETFONT) == 0)  
  {  
    GlobalUnlock(m_hTemplate);  
    return FALSE;  
  }  

  BYTE* pb = GetFontSizeField(pTemplate);  
  nFontSize = *(WORD*)pb;  
  pb += sizeof (WORD);  
  WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pb, -1,  
  strFace.GetBufferSetLength(LF_FACESIZE), LF_FACESIZE, NULL, NULL);  
  strFace.ReleaseBuffer();  
  GlobalUnlock(m_hTemplate);  
  return TRUE;  
}  

Per risolvere questo problema, è stato necessario comprendere che è il motivo di questa operazione era copiare una stringa di caratteri "wide" che rappresenta il nome di un tipo di carattere nel buffer interno di un oggetto CString, cioè strFace.To address this, we had to understand that the reason this was done was to copy a wide character string representing the name of a font into the internal buffer of a CString, strFace. Questa operazione richiedeva un codice leggermente diverso per le stringhe multibyte di CString così come per le stringhe wide-char CString, quindi è stato aggiunto #ifdef in questo caso.This required slightly different code for multibyte CString strings as for wide character CString strings, so we added an #ifdef in this case.

#ifdef _MBCS  
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pb, -1,  
strFace.GetBufferSetLength(LF_FACESIZE), LF_FACESIZE, NULL, NULL);  
strFace.ReleaseBuffer();  
#else  
wcscpy(strFace.GetBufferSetLength(LF_FACESIZE), (LPCWSTR)pb);  
strFace.ReleaseBuffer();  
#endif  

Naturalmente, anziché wcscpy è stato effettivamente usato wcscpy_s, la versione più sicura.Of course, instead of wcscpy we really should use wcscpy_s, the more secure version. Nella sezione successiva viene risolto questo problema.The next section addresses this.

Come verifica del lavoro, è necessario reimpostare il set di caratteri in modo da usare il set di caratteri multibyte e assicurarsi che il codice venga compilato usando MBCS oltre a Unicode.As a check on our work, we should reset the Character Set to Use Multibyte Character Set and make sure that the code still compiles using MBCS as well as Unicode. Inutile a dirsi, è necessario eseguire un test completo sull'app ricompilata dopo tutte queste modifiche.Needless to say, a full test pass should be executed on the recompiled app after all these changes.

Nel lavoro effettuato con questa soluzione Spy++, uno sviluppatore di C++ medio ha impiegato due giorni lavorativi per convertire il codice in Unicode,In our work with this Spy++ solution, it took about two working days for an average C++ developer to convert the code to Unicode. esclusi i tempi di riesecuzione del test.That did not include the retesting time.

Passaggio 12. Step 12. Porting per l'uso di funzioni CRT sicurePorting to use the Secure CRT

A questo punto è possibile eseguire il porting del codice per usare le versioni sicure (cioè quelle con il suffisso _s) delle funzioni CRT.Porting the code to use the secure versions (the versions with the _s suffix) of CRT functions is next. In questo caso, la strategia generale consiste nel sostituire la funzione con la versione _s e quindi, in genere, aggiungervi i parametri di dimensione del buffer aggiuntivi richiesti.In this case, the general strategy is to replace the function with the _s version and then, usually, add the required additional buffer size parameters. In molti casi si tratta di una procedura molto semplice, perché le dimensioni sono note.In many cases this is straightforward since the size is known. In altri casi, in cui la dimensione non è immediatamente disponibile, è necessario aggiungere altri parametri alla funzione che usa la funzione CRT, o forse esaminare l'utilizzo del buffer di destinazione e vedere quali sono i limiti delle dimensioni appropriate.In other cases, where the size is not immediately available, it’s necessary to add additional parameters to the function that’s using the CRT function, or perhaps examine the usage of the destination buffer and see what the appropriate size limits are.

Visual C++ offre uno stratagemma per semplificare l'ottenimento del codice protetto senza aggiungere altrettanti parametri delle dimensioni, che consiste nell'usare gli overload dei modelli.Visual C++ provides a trick to make it easier to get code secure without adding as many size parameters, and that is by using the template overloads. Dal momento che questi overload sono modelli, sono disponibili solo durante la compilazione come C++, non come C. Spyxxhk è un progetto in C, quindi lo stratagemma non funzionerà a tale scopo,Since these overloads are templates, they are only available when compiling as C++, not as C. Spyxxhk is a C project, so the trick won't work for that. tuttavia è possibile usarlo in Spyxx, che non è un progetto in C.However, Spyxx is not and we can use the trick. Lo stratagemma consiste nell'aggiungere una riga simile a questa in una posizione tale da consentirne la compilazione in ogni file del progetto, ad esempio in stdafx.h:The trick is to add a line like this in a place where it will be compiled in every file of the project, such as in stdafx.h:

#define _CRT_SECURE_TEMPLATE_OVERLOADS 1  

Quando si definisce questa posizione, ogni volta che il buffer è una matrice, anziché un puntatore non elaborato, la relativa dimensione viene dedotta dal tipo di matrice e usata come parametro di dimensione, senza che sia necessario fornirla direttamente.When you define that, whenever the buffer is an array, rather than a raw pointer, its size is inferred from the array type and that is used as the size parameter, without you having to supply it. Ciò consente di ridurre la complessità della riscrittura del codice.That helps to cut down the complexity of rewriting the code. Sarà ancora necessario sostituire il nome della funzione con la versione _s, ma spesso è possibile farlo con una semplice operazione di ricerca e sostituzione.You still have to replace the function name with the _s version, but that can often be done by a search and replace operation.

I valori restituiti di alcune funzioni sono cambiati.The return values of some functions changed. Ad esempio, _itoa_s (e _itow_s e la macro _itot_s) restituisce un codice di errore (errno_t) anziché la stringa.For example, _itoa_s (and _itow_s and the macro _itot_s) returns an error code (errno_t), rather than the string. In questi casi, è necessario spostare la chiamata a _itoa_s su una riga separata e sostituirla con l'identificatore del buffer.So in those cases, you have to move the call to _itoa_s onto a separate line and replace it with the buffer's identifier.

Alcuni casi comuni: per memcpy, quando si passa a memcpy_s, spesso è stata aggiunta la dimensione della struttura in cui si esegue la copia.Some of the common cases: for memcpy, when switching to memcpy_s, we frequently added the size of the structure being copied to. Analogamente, per la maggior parte delle stringhe e dei buffer, le dimensioni della matrice o del buffer sono facilmente determinate dalla dichiarazione del buffer o con la ricerca della posizione di allocazione originale del buffer.Similarly, for most strings and buffers, the size of the array or buffer is easily determined from the declaration of the buffer or by finding where the buffer was originally allocated. Per alcune situazioni, è necessario stabilire l'effettiva dimensione disponibile di un buffer e, qualora tali informazioni non fossero disponibili nell'ambito della funzione che si vuole modificare, aggiungerla come parametro aggiuntivo e modificare il codice in modo da fornire le informazioni.For some situations, you need to determine how big of a buffer is actually available, and if that information is not available in the scope of the function that you’re modifying, it should be added as an additional parameter and the calling code should be modified to provide the information.

Con queste tecniche, ci è voluta circa mezza giornata per convertire il codice per usare le funzioni CRT sicure.With these techniques, it took about half a day to convert the code to use the secure CRT functions. Se si sceglie di non usare gli overload dei modelli e di aggiungere manualmente i parametri di dimensione, si impiegherà probabilmente il doppio o il triplo del tempo.If you choose not to the template overloads and to add the size parameters manually, it would probably take twice or three times more time.

Passaggio 13. Step 13. /Zc:forScope-è deprecato/Zc:forScope- is deprecated

A partire da Visual C++ 6.0, il compilatore è conforme allo standard corrente, che limita l'ambito delle variabili dichiarate in un ciclo all'ambito del ciclo.Since Visual C++ 6.0, the compiler conforms to the current standard, which limits the scope of variables declared in a loop to the scope of the loop. L'opzione del compilatore /Zc:forScope (Imponi conformità nell'ambito di un ciclo For nelle proprietà del progetto) controlla se questo viene segnalato come errore.The compiler option /Zc:forScope (Force Conformance for Loop Scope in the project properties) controls whether or not this is reported as an error. Si dovrebbe aggiornare il codice in modo che sia conforme e aggiungere dichiarazioni all'esterno del ciclo.We should update our code to be conformant, and add declarations just outside the loop. Per evitare di apportare le modifiche al codice, è possibile modificare questa impostazione nella sezione Language delle proprietà del progetto C++ in No (/Zc:forScope-).To avoid making the code changes, you can change that setting in the Language section of the C++ project properties to No (/Zc:forScope-). Tuttavia, tenere presente che /Zc:forScope- potrebbe essere rimossa in una versione futura di Visual C++, quindi dopo un certo periodo sarà necessario modificare il codice per conformarlo allo standard.However, keep in mind that /Zc:forScope- might be removed in a future release of Visual C++, so eventually your code will need to change to conform to the standard.

Questi problemi sono relativamente semplici da risolvere, ma a seconda del codice potrebbe riguardare una grande quantità di codice.These issues are relatively easy to fix, but depending on your code, it might affect a lot of code. Di seguito è indicato un problema tipico.Here's a typical issue.

int CPerfTextDataBase::NumStrings(LPCTSTR mszStrings) const  
{  
  for (int n = 0; mszStrings[0] != 0; n++)  
  mszStrings = _tcschr(mszStrings, 0) + 1;  
  return(n);  
}  

Il codice precedente produce l'errore:The above code produces the error:

'n': undeclared identifier  

Ciò si verifica perché il compilatore ha deprecato un'opzione del compilatore consentiva codice non più conforme allo standard C++.This occurs because the compiler has deprecated a compiler option that allowed code that no longer complies with the C++ standard. Nelle procedure standard, dichiarare una variabile all'interno di un ciclo ne limita l'ambito al solo ciclo, quindi la prassi comune di uso di un contatore cicli all'esterno del ciclo richiede che anche la dichiarazione del contatore sia spostata all'esterno del ciclo, come nel seguente codice modificato:In the standard, declaring a variable inside a loop restricts its scope to the loop only, so the common practice of using a loop counter outside of the loop requires that the declaration of the counter also be moved outside the loop, as in the following revised code:

int CPerfTextDataBase::NumStrings(LPCTSTR mszStrings) const  
{  
  int n;  
  for (n = 0; mszStrings[0] != 0; n++)  
  mszStrings = _tcschr(mszStrings, 0) + 1;  
  return(n);  
}  

RiepilogoSummary

Il porting di Spy++ dal codice Visual C++ 6.0 originale al compilatore più recente ha richiesto circa 20 ore di codifica in meno nel corso di circa una settimana.Porting Spy++ from the original Visual C++ 6.0 code to the latest compiler took about 20 hours of coding time over the course of about a week. L'aggiornamento è stato effettuato direttamente su otto versioni del prodotto, passando da Visual Studio 6.0 a Visual Studio 2015.We upgraded directly through eight releases of the product from Visual Studio 6.0 to Visual Studio 2015. Questo è l'approccio consigliato per tutti gli aggiornamenti in progetti di qualsiasi dimensione.This is now the recommended approach for all upgrades on projects large and small.

Vedere ancheSee Also

Porting e aggiornamento: esempi e case study Porting and Upgrading: Examples and Case Studies
Case study precedente: COM SpyPrevious case study: COM Spy