在编写简单的游戏引擎框架时,我遇到了一个奇怪的错误,使用std :: wcout在关闭和销毁对象期间记录消息。输出被截断,应用程序不会终止,但是不会抛出异常。我怀疑在消息完全显示之前,某些对象正在被销毁。
以下是应用程序通过“正常”关机序列时的输出。
这是WinMain -
using namespace sge;
extern BaseApp *SGEApp;
LRESULT CALLBACK NullWinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpszCmdLine, int nCmdShow)
{
#if defined(_DEBUG) | !defined(NDEBUG)
SGE_LEAK_CHECK;
SGE_LEAK_CHECK_BREAK(0);
#endif
/////////////////////
// TODO: move this block of code to a console object
if (!GetConsoleWindow()) {
AllocConsole();
FILE *pCout;
FILE *pCerr;
FILE *pCin;
freopen_s(&pCout, "CONOUT$", "w", stdout);
freopen_s(&pCerr, "CONOUT$", "w", stderr);
freopen_s(&pCin, "CONIN$", "r", stdin);
std::wcout.clear();
std::wcerr.clear();
std::wcin.clear();
SetConsoleTitle(L"Framework Console");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
}
SetThreadAffinityMask(GetCurrentThread(), 1);
bool bRun(true);
MSG msg;
if (!SGEApp->initialize())
return -1;
while (bRun) {
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (GetAsyncKeyState(VK_PAUSE) || msg.message == WM_QUIT)
bRun = false;
SGEApp->update();
SGEApp->draw();
}
delete SGEApp;
FreeConsole();
return int(0);
}
申请对象 -
#pragma once
#ifndef _APP_H
#define _APP_H
#ifndef SGE_BASEAPP_H
#include "Application/OpenglApp.h"
#endif
using namespace sge;
class app : public OpenglApp
{
public:
app();
~app();
};
#include "App.h"
BaseApp *SGEApp = new app();
app::app()
{
}
app::~app()
{
}
#pragma once
#ifndef SGE_BASEAPP_H
#define SGE_BASEAPP_H
#ifndef SGE_LOGGER_H
#include "Core/Logger/Logger.h"
#endif
namespace sge
{
class BaseApp
{
public:
BaseApp();
virtual ~BaseApp();
virtual bool initialize();
virtual bool shutDown();
virtual void update();
virtual void draw();
virtual Logger *getLogger() const { return app_logger; }
protected:
Logger *app_logger;
};
} // namespace sge
#endif // !SGE_BASEAPP_H
#include "BaseApp.h"
namespace sge
{
BaseApp::BaseApp()
{
}
BaseApp::~BaseApp()
{
}
bool BaseApp::initialize()
{
return bool(false);
}
bool BaseApp::shutDown()
{
return bool(false);
}
void BaseApp::update()
{
}
void BaseApp::draw()
{
}
} // namespace sge
#pragma once
#ifndef SGE_OPENGLAPP_H
#define SGE_OPENGLAPP_H
#ifndef SGE_BASEAPP_H
#include "BaseApp.h"
#endif
namespace sge
{
class OpenglApp : public BaseApp
{
public:
OpenglApp();
virtual ~OpenglApp();
virtual bool initialize();
virtual bool shutDown();
virtual void update();
virtual void draw();
};
} // namespace sge
#endif // SGE_OPENGLAPP_H
#include "Application/OpenglApp.h"
namespace sge
{
OpenglApp::OpenglApp()
{
}
OpenglApp::~OpenglApp()
{
shutDown();
app_logger->writeLogFile();
delete app_logger;
}
bool OpenglApp::initialize()
{
app_logger = new Logger();
app_logger->log(SGEString(L"Begin initialization."), LOG_TRACE);
app_logger->log(SGEString(L"Initialization succeeded."), LOG_TRACE);
return bool(true);
}
bool OpenglApp::shutDown()
{
SGEString msg();
app_logger->log(SGEString(L"Begin shutdown."), LOG_TRACE);
app_logger->log(SGEString(L"Shutdown succeeded."), LOG_TRACE);
return bool(true);
}
void OpenglApp::update()
{
}
void OpenglApp::draw()
{
}
} // namespace sge
如果我在上面的shutDown()方法中注释两个log()调用,一切正常。
这是记录器类 -
#pragma once
#ifndef SGE_LOGGER_H
#define SGE_LOGGER_H
#ifndef _VECTOR_
#include <vector>
#endif
#ifndef _FSTREAM_
#include <fstream>
#endif
#ifndef SGE_TYPES_H
#include "Common/SGE_Types.h"
#endif
namespace sge
{
const SGEString DEFAULT_LOGFILE(L"Scavenger.log");
enum SGELogLevel : uint32 {
LOG_ERROR = 0x0001,
LOG_WARN = 0x0002,
LOG_INFO = 0x0004,
LOG_TRACE = 0x0008,
LOG_DEBUG = 0x0010,
LOG_CODE = 0x0020,
};
struct SGELogData {
SGELogData(uint32 severity, SGEString msg)
: level(severity)
, message(msg)
{}
uint32 level;
SGEString message;
};
class Logger
{
public:
Logger();
Logger(uint32 loglevel);
~Logger();
bool initialize();
bool shutdown();
void log(SGEString message, uint32 level = LOG_ERROR);
void log(uint32 level = 0xffff);
void setLogFile(SGEString filespec, bool flush = false);
bool writeLogFile(uint32 level = 0xffff);
protected:
SGEString logger_filespec;
uint32 logger_loglevel;
std::vector<SGELogData> logger_logs;
};
} // namespace sge
#endif // !SGE_LOGGER_H
#include "Logger.h"
namespace sge
{
Logger::Logger()
: logger_loglevel(0xffff)
, logger_filespec(L"")
{
}
Logger::Logger(uint32 loglevel)
: logger_loglevel(loglevel)
, logger_filespec(L"")
{
}
Logger::~Logger()
{
shutdown();
}
bool Logger::initialize()
{
return bool(true);
}
bool Logger::shutdown()
{
logger_logs.clear();
return bool(true);
}
void Logger::log(SGEString message, uint32 level)
{
SGEStringstream msg;
SGEString prefix;
if (!logger_loglevel & level)
return;
prefix = L"";
if(level & LOG_CODE)
prefix = L"**CODE :";
if(level & LOG_DEBUG)
prefix = L"**DEBUG:";
if(level & LOG_TRACE)
prefix = L"**TRACE:";
if(level & LOG_INFO)
prefix = L"**INFO :";
if(level & LOG_WARN)
prefix = L"**WARN :";
if(level & LOG_ERROR)
prefix = L"**ERROR:";
msg << prefix << L" " << message;
logger_logs.push_back(SGELogData(level, message));
std::wcout << msg.str().c_str() << std::endl;
}
void Logger::log(uint32 level)
{
if (!logger_loglevel & level)
return;
for (auto i : logger_logs) {
if (level & i.level) {
uint32 l(level & i.level);
SGEStringstream msg;
SGEString prefix;
if (l & LOG_CODE)
prefix = L"**CODE :";
if (l & LOG_DEBUG)
prefix = L"**DEBUG:";
if (l & LOG_TRACE)
prefix = L"**TRACE:";
if (l & LOG_INFO)
prefix = L"**INFO :";
if (l & LOG_WARN)
prefix = L"**WARN :";
if (l & LOG_ERROR)
prefix = L"**ERROR:";
msg << prefix << L" " << i.message;
std::wcout << msg.str() << std::endl;
}
}
}
void Logger::setLogFile(SGEString filespec, bool flush)
{
if (flush)
writeLogFile();
logger_filespec = filespec;
}
bool Logger::writeLogFile(uint32 level)
{
bool result(false);
if (logger_filespec.empty())
logger_filespec = DEFAULT_LOGFILE;
std::wofstream file(logger_filespec, std::ios::in | std::ios::out | std::ios::trunc);
if (!file) {
log(SGEString(L"Unable to create log file!", LOG_ERROR));
return result;
}
file << L"==================================================" << std::endl;
file << L"Scavenger Log created: " << __DATE__ << " " << __TIME__ << std::endl;
file << L"Logger log level: 0x" << std::hex << logger_loglevel << " ";
file << L" File log level : 0x" << std::hex << level << std::endl;
file << L"==================================================" << std::endl;
for (auto i : logger_logs) {
if (level & i.level) {
uint32 l(level & i.level);
//SGEStringstream msg;
SGEString prefix;
if (l & LOG_CODE)
prefix = L"**CODE :";
if (l & LOG_DEBUG)
prefix = L"**DEBUG:";
if (l & LOG_TRACE)
prefix = L"**TRACE:";
if (l & LOG_INFO)
prefix = L"**INFO :";
if (l & LOG_WARN)
prefix = L"**WARN :";
if (l & LOG_ERROR)
prefix = L"**ERROR:";
file << prefix.c_str() << L" " << i.message.c_str() << std::endl;
}
}
file.close();
return bool(true);
}
} // namespace sge
我使用的某些已定义对象的片段 -
typedef std::wstring SGEString;
typedef std::wstringstream SGEStringstream;
typedef std::wostream SGECout;
我正在使用MSVC 2015社区版。当我单步执行关闭时,将显示所有消息并且应用程序正常退出。它只在正常运行时失败。任何帮助将不胜感激。