Introduction
This project is a Windows Explorer Shell Extension (right mouse menu) for quickly changing the version of a Visual Studio Solution File. The project source shows you how to create a Explorer Shell Extension and how to convert between the solution versions. The basis for this Shell Extension is Michael Dunn's The Complete Idiot's Guide to Writing Shell Extensions - Part II.
Visual Studio Solution File Versions
Here you have an overview of the Visual Studio Versions with the File and Product Versions as it might be a little confusing seeing through all the different versions involved.
Visual Studio Version | File Version | Product Version |
Microsoft Visual Studio .NET (2002) | 7.00 | 7 |
Visual Studio 2003 | 8.00 | 7.1 |
Visual Studio 2005 | 9.00 | 8 |
Visual Studio 2008 | 10.00 | 9 |
Visual Studio 2010 | 11.00 | 10 |
Solution File Version 8.00
Till version 8.00, all you need to change is the version number on the second line in the *.sln file:
Solution File Version 9.00
In version 9.00 and above, you have to change the second line as well as the third:
Using the Code
A VC++ project compiled with Visual Studio .NET 2008 but also can be compiled with VS2003. It's an ATL ComObject which has to be registered with regsvr32. The code shows you how to:
- Create self registering COM Object with ATL
- Convert Microsoft VS *.sln files between versions
- Create submenus with icon in Explorer context menu
First we analyze the version and save the original file to an internal structure (vector of string):
SlnVersions CVersionChanger::AnalyzeFileVersion(const string_list* pFileList) {
m_pFileList = pFileList;
SlnVersions slnFoundVersion = SlnVersions::NA;
int nSize = m_pFileList->size();
vector< vector<string>> v_arr(nSize);
vector<int> v_lines;
vector<double> v_versions;
int filePos = 0;
for ( string_list::const_iterator it = m_pFileList->begin();
it != m_pFileList->end(); it++ ){
ifstream ifs(it->c_str());
string temp;
int linePos = 0;
bool lineFound = false;
while(getline(ifs, temp )){
v_arr[filePos].push_back(temp);
const char* sTmpLine = strstr(temp.c_str(), FORMAT_VERSION_TAG);
if(sTmpLine != 0){
v_lines.push_back(linePos);
string sLine = temp.c_str();
string str2 = sLine.substr(strlen(FORMAT_VERSION_TAG)+1,
sLine.length() - strlen(FORMAT_VERSION_TAG)-1);
double dVar = 0;
dVar = atof(str2.c_str());
v_versions.push_back(dVar);
switch((int)dVar){
case 11:
slnFoundVersion = SlnVersions::eleven;
break;
case 10:
slnFoundVersion = SlnVersions::ten;
break;
case 9:
slnFoundVersion = SlnVersions::nine;
break;
case 8:
slnFoundVersion = SlnVersions::eight;
break;
case 7:
slnFoundVersion = SlnVersions::seven;
break;
default:
slnFoundVersion = SlnVersions::NA;
}
v_slnVersions.push_back((int)slnFoundVersion);
lineFound = true;
}
linePos++;
}
if(!lineFound)
v_lines.push_back(-1);
ifs.close();
filePos++;
}
v_lineContent = v_arr;
v_linePos = v_lines;
v_fileVersions = v_versions;
return slnFoundVersion;
}
With the following function, we can convert the file(s) to any version we need.
void CVersionChanger::ChangeVersion(SlnVersions slnChangeTo){
int nCounter = 0;
for ( string_list::const_iterator it = m_pFileList->begin();
it != m_pFileList->end(); it++ ) {
if(cfgMgr.bBackup)
ff.CreateBakFile(it->c_str(), cfgMgr.sBakExt);
string sTmpExt = cfgMgr.sNewExt;
string sTmpOut = it->c_str();
if(!cfgMgr.bOverwrite)
sTmpOut = sTmpOut + sTmpExt;
ofstream outputFile(sTmpOut.c_str());
if(outputFile.is_open())
{
for(int i = 0; i < v_lineContent[nCounter].size(); i++){
string sOut = "";
int nOutLen = 0;
if(i == v_linePos.at(nCounter)){
char* sTmpOutput = new char[strlen(FORMAT_VERSION_TAG) +
(v_fileVersions.at(nCounter)>= 10 ? 4 : 3) + 2];
sprintf(sTmpOutput, "%s %i.00\n", FORMAT_VERSION_TAG,
(int)slnChangeTo);
sOut = sTmpOutput;
nOutLen = strlen(sTmpOutput);
}else{
sOut = v_lineContent[nCounter].at(i) + "\n";
nOutLen = v_lineContent[nCounter].at(i).length()+1;
}
outputFile.write((char*)sOut.c_str(), nOutLen);
if(i == v_linePos.at(nCounter)){
if(slnChangeTo == SlnVersions::eleven){
outputFile.write(VS2010, strlen(VS2010));
if(v_fileVersions.at(nCounter) !=
(int)SlnVersions::eight | v_fileVersions.at(nCounter) !=
(int)SlnVersions::seven)
i++;
}else if(slnChangeTo == SlnVersions::ten){
outputFile.write(VS2008, strlen(VS2008));
if(v_fileVersions.at(nCounter) !=
(int)SlnVersions::eight | v_fileVersions.at(nCounter) !=
(int)SlnVersions::seven)
i++;
}else if(slnChangeTo == SlnVersions::nine){
outputFile.write(VS2005, strlen(VS2005));
if(v_fileVersions.at(nCounter) !=
(int)SlnVersions::eight | v_fileVersions.at(nCounter) !=
(int)SlnVersions::seven)
i++;
}else if(slnChangeTo == SlnVersions::eight){
if(v_fileVersions.at(nCounter) !=
(int)SlnVersions::eight | v_fileVersions.at(nCounter) !=
(int)SlnVersions::seven)
i++;
}else if(slnChangeTo == SlnVersions::seven){
if(v_fileVersions.at(nCounter) !=
(int)SlnVersions::eight | v_fileVersions.at(nCounter) !=
(int)SlnVersions::seven)
i++;
}
}
}
}
else if(!outputFile.is_open())
{
printf("I couldn't open %s for creation!\n", "outputFilePath");
v_bSucceeded.push_back(false);
return 0;
}
outputFile.close();
v_bSucceeded.push_back(true);
v_slnNewVersions.push_back(slnChangeTo);
nCounter++;
}
return;
}
Here a snippet of QueryContextMenu()
. With this function, you build up the menu structure with the context menu lable text loaded from the registry.
HRESULT CVSSolutionVersionChangerDll::QueryContextMenu (
HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
UINT uCmdID = uidFirstCmd;
SlnVersions slnVer = vc.AnalyzeFileVersion(&m_lsFiles);
if ( uFlags & CMF_DEFAULTONLY )
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
HMENU mPopUp = CreatePopupMenu();
UINT nFlags = MF_STRING | MF_BYPOSITION;
if(m_lsFiles.size() == 1 && slnVer == SlnVersions::seven)
nFlags = nFlags | MF_GRAYED;
InsertMenu ( mPopUp,uMenuIndex, nFlags, uCmdID++, cMgr.s2002.c_str() );
uMenuIndex++;
nFlags = MF_STRING | MF_BYPOSITION;
if(m_lsFiles.size() == 1 && slnVer == SlnVersions::eight)
nFlags = nFlags | MF_GRAYED;
InsertMenu ( mPopUp,uMenuIndex, nFlags, uCmdID++, cMgr.s2003.c_str() );
uMenuIndex++;
nFlags = MF_STRING | MF_BYPOSITION;
if(m_lsFiles.size() == 1 && slnVer == SlnVersions::nine)
nFlags = nFlags | MF_GRAYED;
InsertMenu ( mPopUp,uMenuIndex, nFlags, uCmdID++, cMgr.s2005.c_str() );
uMenuIndex++;
...
Config
On the config dialog, you can set your preferred conversion settings, whether to backup and/or overwrite the original file or you can set the context menu text.
Credits
- Michael Dunn - The Complete Idiot's Guide to Writing Shell Extensions - Part II
History
- April 18. 2010 - Version 1.0.0.2
- New release with support for VS version 2002-2010 and more config options
- April 03. 2010 - Version 1.0.0.0
- Initial revision with converter for solution file version 7.00 (7), 8.00 (7.1), 9.00 (8) and 10.00 (9) // FileVersion (IconVersion)