Unable to load image during WinApi c++ programming - winapi

I am doing WinApi C++ programming with my colleague. We are using the visual studio. While I was loading the image, there was an error like the one in the picture below.
While debugging, I found out that this is an error caused by the lack of files on the path. You can simply add a file to the path to resolve the error, but you want to import the image from another specified path. The reason why you don't choose a simple method is that even though your colleagues use the same code, they succeed in importing files from different paths, so they don't get errors.
The path where I import the file is 'D:\BreakTogether\OutPut\bin\Res', and the path where my colleague imports the file is 'D:\BreakTogether\bin\Res'. Below is the code used to get the file path.
#include "pch.h"
#include "ResMgr.h"
#include "PathMgr.h"
#include "Image.h"
ResMgr::ResMgr()
{
}
ResMgr::~ResMgr()
{
/*map<wstring, Image*>::iterator iter;
for (iter = m_mapImg.begin(); iter != m_mapImg.end(); ++iter)
{
delete iter->second;
}*/
Safe_Delete_Map(m_mapImg);
}
Image* ResMgr::ImgLoad(const wstring& _strKey, const wstring& _strRelativePath)
{
Image* pImg = ImgFind(_strKey);
if (nullptr != pImg)
{
return pImg;
}
wstring strFilePath = PathMgr::GetInst()->GetRsrcPath();
strFilePath += _strRelativePath;
pImg = new Image;
pImg->Load(strFilePath);
pImg->SetKey(_strKey);
pImg->SetRelativePath(_strRelativePath);
m_mapImg.insert(make_pair(_strKey, pImg));
// m_mapImg.insert({ _strKey , pImg });
return pImg;
}
Image* ResMgr::ImgFind(const wstring& _strKey)
{
auto iter = m_mapImg.find(_strKey);
if (iter == m_mapImg.end())
{
return nullptr;
}
return static_cast<Image*>(iter->second);
}
#include "pch.h"
#include "PathMgr.h"
#include "Core.h"
PathMgr::PathMgr()
: m_szRsrcPath{}
{
}
PathMgr::~PathMgr()
{
}
void PathMgr::Init()
{
GetCurrentDirectory(255, m_szRsrcPath);
int Length = wcslen(m_szRsrcPath);
for (int i = Length - 1; i >= 0; i--)
{
if (m_szRsrcPath[i] == '\\')
{
m_szRsrcPath[i] = '\0';
break;
}
}
wcscat_s(m_szRsrcPath, 255, L"\\bin\\Res\\");
SetWindowText(Core::GetInst()->GetWndHandle(), m_szRsrcPath);
}
I tried adding an image to the path where I load the file. That will solve the problem. However, it is inefficient because my colleague and I have to add the same file twice because we have different paths to load the image.

If you can load the file normally when the image file is in place, there is no problem with your code.
Go to your Project Property Pages -> Debugging -> Working Directory and align your path with your colleagues.
If the problem is not solved, plz tell me more about your problem.
Have a nice day!

Related

The application was unable to start correctly 0xc00007b for an SDL2 application

I am building an SDL application on my x64 Windows 10 machine using Visual Studio.
I have set up the include/library/additional dependencies following this tutorial.
Everything works fine until I add the SDL_ttf library following the example above as guideline, and paste the SDL2_ttf.dll to the debug folder.
I get this error:
"The application was unable to start correctly (0xc000007b). Click Ok to close the application.*
If I remove SDL2_ttf.dll from the folder, I get (only) this error:
I downloaded the .dll (x64 version) from this link.
My SDL2.dll and SDL2_image.dll (both x64) work correctly as my executable runs when I only use those libraries.
What's the issue here? Why could I be getting this error?
Additional info
I checked system32 folder (and the whole Windows parent folder) and I didn't find SDL_ttf.dll there.
Also, this is what my debug folder looks like:
This is the simple code example I am running
//Using SDL and standard IO
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <SDL_ttf.h>
//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
SDL_Window* gWindow = nullptr;
SDL_Surface* gScreenSurface = nullptr;
bool init()
{
//Initialization flag
bool success = true;
IMG_Init(IMG_INIT_PNG);
TTF_Init();
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Create window
gWindow = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (gWindow == NULL)
{
printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
success = false;
}
else
{
//Get window surface
gScreenSurface = SDL_GetWindowSurface(gWindow);
}
}
}
return success;
}
int main(int argc, char* args[])
{
init();
//Destroy window
SDL_DestroyWindow(gWindow);
//Quit SDL subsystems
SDL_Quit();
return 0;
}
EDIT: tidied code
EDIT: I have an idea why I am seeing this issue. I copied some library files to my MinGW folder the other day and I may have overwritten some important dll files that SDL2 uses (behind the scenes). If I get to the bottom of it, I will post. Likewise, I may get a fresh install of MinGW and will post an update if the issue is resolved.
EDIT: I finally have it working. It looks like I have a dll somewhere in my PATH that is not suitable for my x64 application and it wasn't any of the 3 dlls I mentioned above. This is what my working folder looks like.
After a fresh install of MinGW using MinGW installation manager the program started to give different errors indicating which dll was missing. The SDL runtime library folder contained all these folders. I copied the x64 version of them and the application ran successfully.

Section of the code of our c++ DLL crashes wen ran on a Windows 7 SP1

I am developing a desktop Air application that uses an Air Native Extension (ANE). The native part of the ANE is composed only by a DLL written partially in C and partially in C++. The app was compiled with Visual Studio 2010 and requires the MSVCR100.DLL and the MSVCP100.DLL to be on the same directory as the application's exe file.
The app and DLL work great on many computers but on clean Windows 7 SP1 computers, part of its code makes the DLL crash.
I've narrowed down the conflicting code to the following:
// Addresses.
String^ defaultGateway = "Not Found";
String^ interfaceIPAddress = "Not Found";
String^ interfaceMask = "Not Found";
array<NetworkInterface^>^nics = NetworkInterface::GetAllNetworkInterfaces();
if (nics != nullptr || nics->Length > 0)
{
System::Collections::IEnumerator^ myEnum4 = nics->GetEnumerator();
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}
Just to give you more context, I'll copy the entire function were that code is:
FREObject MainInterfaceInfo(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
// Addresses.
String^ defaultGateway = "Not Found";
String^ interfaceIPAddress = "Not Found";
String^ interfaceMask = "Not Found";
array<NetworkInterface^>^nics = NetworkInterface::GetAllNetworkInterfaces();
if (nics != nullptr || nics->Length > 0)
{
System::Collections::IEnumerator^ myEnum4 = nics->GetEnumerator();
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}
String^ result = interfaceIPAddress + ";" + interfaceMask + ";" + defaultGateway;
// Converting the response to const uint8_t *
msclr::interop::marshal_context oMarshalContext;
const char* charResponse = oMarshalContext.marshal_as<const char*>(result);
const uint8_t * resultNativeCharArray = (const uint8_t *)charResponse;
uint32_t resultNativeCharArrayLength = 0;
while (true) {
if (NULL == resultNativeCharArray[resultNativeCharArrayLength]) {
break;
}
else {
resultNativeCharArrayLength++;
}
}
FREObject functionResult;
FRENewObjectFromUTF8(resultNativeCharArrayLength, resultNativeCharArray, &functionResult);
return functionResult;
}
I'm a noob on C and C++ because I only saw it a couple of times 10 years ago so I have no clue about what exactly is making the DLL crash. Can someone tell? Any advice will be more than appreciated.
Editted
I came to realize that the same narrowed code makes the app require the MSVCR100.DLL and the MSVCP100.DLL. If I remove that portion of code, the app can run without them.
MSVCP100.DLL contains the standard C++ Library; MSVCR100.DLL is the C runtime. It's probably something like the safe_cast call that's introducing the dependency. More details about the runtime libraries are here.
I'd suggest using the official redistributable package to deploy the Visual C++ runtimes DLLs instead of deploying them into your app's directory yourself. See Redistributing Visual C++ Files for details... the walkthrough links in there will be helpful to you. That's the sure-fire way to be sure that the target system has all the dependencies you'll need. Your older (non-clean) Win7 systems probably had some other app or Windows Update install that redistributable for you, which is why your code is working there.
Actually, the line of code that was messing everything up was:
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
If that line was deleted, the DLL would run without a problem.
The issue occurred because one of the inactive network interfaces of the system reported '0.0.0.0' on gateway->Address->ToString(). So when trying to unicastIPAddressInformation->IPv4Mask->ToString() the DLL would crash.
To solve the issue I just added an if (adapter->OperationalStatus == OperationalStatus::Up) at the beggining and everything worked great afterwards. The resulting code is as follows:
while (myEnum4->MoveNext())
{
NetworkInterface^ adapter = safe_cast<NetworkInterface ^>(myEnum4->Current);
IPInterfaceProperties^ properties = adapter->GetIPProperties();
if (adapter->OperationalStatus == OperationalStatus::Up)
{
GatewayIPAddressInformationCollection^ gateways = properties->GatewayAddresses;
for each (GatewayIPAddressInformation^ gateway in gateways)
{
if (gateway->Address->AddressFamily == AddressFamily::InterNetwork)
{
defaultGateway = gateway->Address->ToString();
for each (UnicastIPAddressInformation^ unicastIPAddressInformation in properties->UnicastAddresses)
{
if (unicastIPAddressInformation->Address->AddressFamily == AddressFamily::InterNetwork)
{
interfaceIPAddress = unicastIPAddressInformation->Address->ToString();
interfaceMask = unicastIPAddressInformation->IPv4Mask->ToString();
}
}
}
}
}
}

alBufferData() sets AL_INVALID_OPERATION when using buffer ID obtained from alSourceUnqueueBuffers()

I am trying to stream audio data from disk using OpenAL's buffer queueing mechanism. I load and enqueue 4 buffers, start the source playing, and check in a regular intervals to refresh the queue. Everything looks like it's going splendidly, up until the first time I try to load data into a recycled buffer I got from alSourceUnqueueBuffers(). In this situation, alBufferData() always sets AL_INVALID_OPERATION, which according to the official v1.1 spec, it doesn't seem like it should be able to do.
I have searched extensively on Google and StackOverflow, and can't seem to find any reason why this would happen. The closest thing I found was someone with a possibly-related issue in an archived forum post, but details are few and responses are null. There was also this SO question with slightly different circumstances, but the only answer's suggestion does not help.
Possibly helpful: I know my context and device are configured correctly, because loading small wav files completely into a single buffer and playing them works fine. Through experimentation, I've also found that queueing 2 buffers, starting the source playing, and immediately loading and enqueueing the other two buffers throws no errors; it's only when I've unqueued a processed buffer that I run into trouble.
The relevant code:
static constexpr int MAX_BUFFER_COUNT = 4;
#define alCall(funcCall) {funcCall; SoundyOutport::CheckError(__FILE__, __LINE__, #funcCall) ? abort() : ((void)0); }
bool SoundyOutport::CheckError(const string &pFile, int pLine, const string &pfunc)
{
ALenum tErrCode = alGetError();
if(tErrCode != 0)
{
auto tMsg = alGetString(tErrCode);
Log::e(ro::TAG) << tMsg << " at " << pFile << "(" << pLine << "):\n"
<< "\tAL call " << pfunc << " failed." << end;
return true;
}
return false;
}
void SoundyOutport::EnqueueBuffer(const float* pData, int pFrames)
{
static int called = 0;
++called;
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
if(tState == AL_STATIC)
{
Stop();
// alCall(alSourcei(mSourceId, AL_BUFFER, NULL));
}
ALuint tBufId = AL_NONE;
int tQueuedBuffers = QueuedUpBuffers();
int tReady = ProcessedBuffers();
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
void* tConverted = convert(pData, pFrames);
// the fifth time through, we get AL_INVALID_OPERATION, and call abort()
alCall(alBufferData(tBufId, mFormat, tConverted, pFrames * mBitdepth/8, mSampleRate));
alCall(alSourceQueueBuffers(mSourceId, 1, &mBufferId));
if(mBitdepth == BITDEPTH_8)
{
delete (uint8_t*)tConverted;
}
else // if(mBitdepth == BITDEPTH_16)
{
delete (uint16_t*)tConverted;
}
}
void SoundyOutport::PlayBufferedStream()
{
if(!StreamingMode() || !QueuedUpBuffers())
{
Log::w(ro::TAG) << "Attempted to play an unbuffered stream" << end;
return;
}
alCall(alSourcei(mSourceId, AL_LOOPING, AL_FALSE)); // never loop streams
alCall(alSourcePlay(mSourceId));
}
int SoundyOutport::QueuedUpBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tCount));
return tCount;
}
int SoundyOutport::ProcessedBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_PROCESSED, &tCount));
return tCount;
}
void SoundyOutport::Stop()
{
if(Playing())
{
alCall(alSourceStop(mSourceId));
}
int tBuffers;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tBuffers));
if(tBuffers)
{
ALuint tDummy[tBuffers];
alCall(alSourceUnqueueBuffers(mSourceId, tBuffers, tDummy));
}
alCall(alSourcei(mSourceId, AL_BUFFER, AL_NONE));
}
bool SoundyOutport::Playing()
{
ALint tPlaying;
alCall(alGetSourcei(mSourceId, AL_SOURCE_STATE, &tPlaying));
return tPlaying == AL_PLAYING;
}
bool SoundyOutport::StreamingMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STREAMING;
}
bool SoundyOutport::StaticMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STATIC;
}
And here's an annotated screen cap of what I see in my debugger when I hit the error:
I've tried a bunch of little tweaks and variations, and the result is always the same. I've wasted too many days trying to fix this. Please help :)
This error occurs when you trying to fill buffer with data, when the buffer is still queued to the source.
Also this code is wrong.
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
You can fill buffer with data only if it unqueued from source. But your first if block gets tBufId that queued to the source. Rewrite code like so
if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}

Determining object types in Qt

I have a series of QTextEdits and QLineEdits connected to a slot through a QSignalMapper(which emits a textChanged(QWidget*) signal). When the connected slot is called (pasted below), I need to be able to differentiate between the two so I know whether to call the text() or toPlainText() function. What's the easiest way to determine the subclass type of a QWidget?
void MainWindow::changed(QWidget *sender)
{
QTextEdit *temp = qobject_cast<QTextEdit *>(sender);
QString currentText = temp->toPlainText(); // or temp->text() if its
// a QLineEdit...
if(currentText.compare(""))
{
...
}
else
{
...
}
}
I was considering using try-catch but Qt doesn't seem to have very extensive support for Exceptions... Any ideas?
Actually, your solution is already almost there. In fact, qobject_cast will return NULL if it can't perform the cast. So try it on one of the classes, if it's NULL, try it on the other:
QString text;
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
if (textEdit) {
text = textEdit->toPlainText();
} else if (lineEdit) {
text = lineEdit->text();
} else {
// Return an error
}
You can also use sender->metaObject()->className() so you won't make unnecesary casts. Specially if you have a lot of classes to test. The code will be like this:
QString text;
QString senderClass = sender->metaObject()->className();
if (senderClass == "QTextEdit") {
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
text = textEdit->toPlainText();
} else if (senderClass == "QLineEdit") {
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
text = lineEdit->text();
} else {
// Return an error
}
I know is an old question but I leave this answer just in case it would be useful for somebody...

Visual Studio Solutions / Multiple project : How to effectively propagate project properties amongst several C++ projects

I am working with a Visual Studio 2005 C++ solution that includes multiple projects (about 30).
Based upon my experience, it often becomes annoying to maintain all the properties of the projects (i.e include path, lib path, linked libs, code generation options, ...), as you often have to click each and every project in order to modify them.
The situation becomes even worse when you have multiple configurations (Debug, Release, Release 64 bits, ...).
Real life examples:
Assume you want to use a new library, and you need to add the include path to this library to all projects.
How will you avoid to have to edit the properties of each an every project?
Assume you want to test drive a new version of library (say version 2.1beta) so that you need to quickly change the include paths / library path / linked library for a set of projects?
Notes:
I am aware that it is possible to select multiple projects at a time, then make a right click and select "properties". However this method only works for properties that were already exactly identical for the different projects : you can not use it in order to add an include path to a set of project that were using different include path.
I also know that it is possible to modify globally the environment options (Tools/Options/Project And solutions/Directories), however it is not that satisfying since it can not be integrated into a SCM
I also know that one can add "Configurations" to a solutions. It does not helps since it makes another set of project properties to maintain
I know that codegear C++ Builder 2009 offers a viable answer to this need through so called "Option sets" which can be inherited by several projects (I use both Visual Studio and C++ Builder, and I still thinks C++ Builder rocks on certain aspects as compared to Visual Studio)
I expect that someone will suggest an "autconf" such as CMake, however is it possible to import vcproj files into such a tool?
I think you need to investigate properties files, i.e. *.vsprops (older) or *.props (latest)
You do need to add the properties file manually to each project, but once that's done, you have multiple projects, but one .[vs]props file. If you change the properties, all projects inherit the new settings.
I often need to do something similar since I link to the static runtime libraries. I wrote a program to do it for me. It basically scans all of the subdirectories of whatever path you give it and ids any .vcproj files it finds. Then one by one, it opens them modifies them and saves them. Since I only use it rarely, the path is hard coded it, but I think you'll be able to adjust it how you like.
Another approach is to realize that Visual Studio Project files are simply XML files and can be manipulated with your favorite XML class. I've done something using C#'s XmlDocument for updating the include directories when there were A LOT of include directories that I didn't want to type in. :)
I'm including both examples. You will need to modify them to your own needs, but these should get you started.
This is the C++ version:
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/regex.hpp>
#include <boost/timer.hpp>
using boost::regex;
using boost::filesystem::path;
using namespace std;
vector<path> GetFileList(path dir, bool recursive, regex matchExp);
void FixProjectFile(path file);
string ReadFile( path &file );
void ReplaceRuntimeLibraries( string& contents );
void WriteFile(path file, string contents);
int _tmain(int argc, _TCHAR* argv[])
{
boost::timer stopwatch;
boost::filesystem::path::default_name_check(boost::filesystem::native);
regex projFileRegex("(.*)\\.vcproj");
path rootPath("D:\\Programming\\Projects\\IPP_Decoder");
vector<path> targetFiles = GetFileList(rootPath, true, projFileRegex);
double listTimeTaken = stopwatch.elapsed();
std::for_each(targetFiles.begin(), targetFiles.end(), FixProjectFile);
double totalTimeTaken = stopwatch.elapsed();
return 0;
}
void FixProjectFile(path file) {
string contents = ReadFile(file);
ReplaceRuntimeLibraries(contents);
WriteFile(file, contents);
}
vector<path> GetFileList(path dir, bool recursive, regex matchExp) {
vector<path> paths;
try {
boost::filesystem::directory_iterator di(dir);
boost::filesystem::directory_iterator end_iter;
while (di != end_iter) {
try {
if (is_directory(*di)) {
if (recursive) {
vector<path> tempPaths = GetFileList(*di, recursive, matchExp);
paths.insert(paths.end(), tempPaths.begin(), tempPaths.end());
}
} else {
if (regex_match(di->string(), matchExp)) {
paths.push_back(*di);
}
}
}
catch (std::exception& e) {
string str = e.what();
cout << str << endl;
int breakpoint = 0;
}
++di;
}
}
catch (std::exception& e) {
string str = e.what();
cout << str << endl;
int breakpoint = 0;
}
return paths;
}
string ReadFile( path &file ) {
// cout << "Reading file: " << file.native_file_string() << "\n";
ifstream infile (file.native_file_string().c_str(), ios::in | ios::ate);
assert (infile.is_open());
streampos sz = infile.tellg();
infile.seekg(0, ios::beg);
vector<char> v(sz);
infile.read(&v[0], sz);
string str (v.empty() ? string() : string (v.begin(), v.end()).c_str());
return str;
}
void ReplaceRuntimeLibraries( string& contents ) {
regex releaseRegex("RuntimeLibrary=\"2\"");
regex debugRegex("RuntimeLibrary=\"3\"");
string releaseReplacement("RuntimeLibrary=\"0\"");
string debugReplacement("RuntimeLibrary=\"1\"");
contents = boost::regex_replace(contents, releaseRegex, releaseReplacement);
contents = boost::regex_replace(contents, debugRegex, debugReplacement);
}
void WriteFile(path file, string contents) {
ofstream out(file.native_file_string().c_str() ,ios::out|ios::binary|ios::trunc);
out.write(contents.c_str(), contents.length());
}
This is the C# version. Enjoy...
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
namespace ProjectUpdater
{
class Program
{
static public String rootPath = "D:\\dev\\src\\co\\UMC6\\";
static void Main(string[] args)
{
String path = "D:/dev/src/co/UMC6/UMC.vcproj";
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(fs);
XmlNodeList oldFiles = xmldoc.GetElementsByTagName("Files");
XmlNode rootNode = oldFiles[0].ParentNode;
rootNode.RemoveChild(oldFiles[0]);
XmlNodeList priorNode = xmldoc.GetElementsByTagName("References");
XmlElement filesNode = xmldoc.CreateElement("Files");
rootNode.InsertAfter(filesNode, priorNode[0]);
DirectoryInfo di = new DirectoryInfo(rootPath);
foreach (DirectoryInfo thisDir in di.GetDirectories())
{
AddAllFiles(xmldoc, filesNode, thisDir.FullName);
}
List<String> allDirectories = GetAllDirectories(rootPath);
for (int i = 0; i < allDirectories.Count; ++i)
{
allDirectories[i] = allDirectories[i].Replace(rootPath, "$(ProjectDir)");
}
String includeDirectories = "\"D:\\dev\\lib\\inc\\ipp\\\"";
foreach (String dir in allDirectories)
{
includeDirectories += ";\"" + dir + "\"";
}
XmlNodeList toolNodes = xmldoc.GetElementsByTagName("Tool");
foreach (XmlNode node in toolNodes)
{
if (node.Attributes["Name"].Value == "VCCLCompilerTool") {
try
{
node.Attributes["AdditionalIncludeDirectories"].Value = includeDirectories;
}
catch (System.Exception e)
{
XmlAttribute newAttr = xmldoc.CreateAttribute("AdditionalIncludeDirectories");
newAttr.Value = includeDirectories;
node.Attributes.InsertBefore(newAttr, node.Attributes["PreprocessorDefinitions"]);
}
}
}
String pathOut = "D:/dev/src/co/UMC6/UMC.xml";
FileStream fsOut = new FileStream(pathOut, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
xmldoc.Save(fsOut);
}
static void AddAllFiles(XmlDocument doc, XmlElement parent, String path) {
DirectoryInfo di = new DirectoryInfo(path);
XmlElement thisElement = doc.CreateElement("Filter");
thisElement.SetAttribute("Name", di.Name);
foreach (FileInfo fi in di.GetFiles())
{
XmlElement thisFile = doc.CreateElement("File");
String relPath = fi.FullName.Replace(rootPath, ".\\");
thisFile.SetAttribute("RelativePath", relPath);
thisElement.AppendChild(thisFile);
}
foreach (DirectoryInfo thisDir in di.GetDirectories())
{
AddAllFiles(doc, thisElement, thisDir.FullName);
}
parent.AppendChild(thisElement);
}
static List<String> GetAllDirectories(String dir)
{
DirectoryInfo di = new DirectoryInfo(dir);
Console.WriteLine(dir);
List<String> files = new List<String>();
foreach (DirectoryInfo subDir in di.GetDirectories())
{
List<String> newList = GetAllDirectories(subDir.FullName);
files.Add(subDir.FullName);
files.AddRange(newList);
}
return files;
}
static List<String> GetAllFiles(String dir)
{
DirectoryInfo di = new DirectoryInfo(dir);
Console.WriteLine(dir);
List<String> files = new List<String>();
foreach (DirectoryInfo subDir in di.GetDirectories())
{
List<String> newList = GetAllFiles(subDir.FullName);
files.AddRange(newList);
}
foreach (FileInfo fi in di.GetFiles())
{
files.Add(fi.FullName);
}
return files;
}
}
}
As suggested, you should look at Property Sheets (aka .vsprops files).
I wrote a very short introduction to this feature here.
Yes, I'd definitely suggest using CMake. CMake is the best tool (I think I've actually tried them all) which can generate Studio project files.
I also had the issue of converting existing .vcproj files into CMakeLists.txt, and I wrote a Ruby-script which takes care of most of the conversion. The script doesn't handle things like post-build steps and such, so some tweaking is necessary, but it will save you the hassle of pulling all the source file names from the .vcproj files.
*.vcxproj files are msbuild files. So you just take a property you don't want in all your project files and delete it. Then put it in your property sheet. Then make sure all the projects files properly import that property sheet.
This can be incredibly tedious for hundreds of files. I wrote a tool make this interactive:
https://github.com/chris1248/MsbuildRefactor

Resources