mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
new CommandLine class
implemented crashdump as a command, fixed dump_running_process.bat to use it attach to console if present instead of always create one
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
pushd "%~dp0"
|
||||
start ModOrganizer.exe --crashdump
|
||||
start ModOrganizer.exe crashdump
|
||||
|
||||
@@ -11,6 +11,7 @@ endif()
|
||||
|
||||
add_filter(NAME src/application GROUPS
|
||||
iuserinterface
|
||||
commandline
|
||||
main
|
||||
moapplication
|
||||
moshortcut
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "commandline.h"
|
||||
#include "env.h"
|
||||
|
||||
namespace cl
|
||||
{
|
||||
|
||||
CommandLine::CommandLine()
|
||||
{
|
||||
createOptions();
|
||||
m_commands.push_back(std::make_unique<CrashDumpCommand>());
|
||||
}
|
||||
|
||||
int CommandLine::run(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
po::variables_map vm;
|
||||
|
||||
auto parsed = po::command_line_parser(argc, argv)
|
||||
.options(m_allOptions)
|
||||
.positional(m_positional)
|
||||
.allow_unregistered()
|
||||
.run();
|
||||
|
||||
po::store(parsed, vm);
|
||||
|
||||
if (vm.count("command")) {
|
||||
const auto cmd = vm["command"].as<std::string>();
|
||||
|
||||
for (auto&& c : m_commands) {
|
||||
if (c->name() == cmd) {
|
||||
auto co = c->options();
|
||||
co.add_options()
|
||||
("help", "shows this message");
|
||||
|
||||
auto opts = po::collect_unrecognized(
|
||||
parsed.options, po::include_positional);
|
||||
|
||||
// remove the command name itself
|
||||
opts.erase(opts.begin());
|
||||
|
||||
parsed = po::command_line_parser(opts)
|
||||
.options(co)
|
||||
.run();
|
||||
|
||||
po::store(parsed, vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
env::Console console;
|
||||
std::cout << usage(c.get()) << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return c->run(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vm.count("help")) {
|
||||
env::Console console;
|
||||
std::cout << usage() << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
catch(po::error& e)
|
||||
{
|
||||
env::Console console;
|
||||
std::cerr << e.what() << "\n";
|
||||
std::cerr << usage() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLine::createOptions()
|
||||
{
|
||||
m_visibleOptions.add_options()
|
||||
("help", "shows this message");
|
||||
|
||||
po::options_description options;
|
||||
options.add_options()
|
||||
("command", po::value<std::string>(), "command to execute");
|
||||
|
||||
m_positional
|
||||
.add("command", 1)
|
||||
.add("subargs", -1);
|
||||
|
||||
m_allOptions.add(m_visibleOptions);
|
||||
m_allOptions.add(options);
|
||||
}
|
||||
|
||||
std::string CommandLine::usage(const Command* c) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
oss
|
||||
<< "\n"
|
||||
<< "Usage:\n";
|
||||
|
||||
if (c) {
|
||||
oss
|
||||
<< " ModOrganizer.exe [options] " << c->name() << " [command-options]\n"
|
||||
<< "\n"
|
||||
<< "Command options:\n"
|
||||
<< c->options() << "\n";
|
||||
} else {
|
||||
oss
|
||||
<< " ModOrganizer.exe [options] [[command] [command-options]]\n"
|
||||
<< "\n"
|
||||
<< "Commands:\n";
|
||||
|
||||
for (auto&& c : m_commands) {
|
||||
oss << " " << c->name() << " " << c->description() << "\n";
|
||||
}
|
||||
|
||||
oss << "\n";
|
||||
}
|
||||
|
||||
oss
|
||||
<< "Global options:\n"
|
||||
<< m_visibleOptions << "\n";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
std::string Command::name() const
|
||||
{
|
||||
return meta().name;
|
||||
}
|
||||
|
||||
std::string Command::description() const
|
||||
{
|
||||
return meta().description;
|
||||
}
|
||||
|
||||
po::options_description Command::options() const
|
||||
{
|
||||
return doOptions();
|
||||
}
|
||||
|
||||
po::options_description Command::doOptions() const
|
||||
{
|
||||
// no-op
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Command::usage() const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
oss
|
||||
<< "\n"
|
||||
<< "Usage:\n"
|
||||
<< " ModOrganizer.exe [options] [[command] [command-options]]\n"
|
||||
<< "\n"
|
||||
<< "Options:\n"
|
||||
<< options() << "\n";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
int Command::run(po::variables_map& vm)
|
||||
{
|
||||
return doRun(vm);
|
||||
}
|
||||
|
||||
|
||||
|
||||
po::options_description CrashDumpCommand::doOptions() const
|
||||
{
|
||||
po::options_description d;
|
||||
|
||||
d.add_options()
|
||||
("type", po::value<std::string>()->default_value("mini"), "mini|data|full");
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
Command::Meta CrashDumpCommand::meta() const
|
||||
{
|
||||
return {"crashdump", "writes a crashdump for a running process of MO"};
|
||||
}
|
||||
|
||||
int CrashDumpCommand::doRun(po::variables_map& vm)
|
||||
{
|
||||
env::Console console;
|
||||
|
||||
const auto typeString = vm["type"].as<std::string>();
|
||||
const auto type = env::coreDumpTypeFromString(typeString);
|
||||
|
||||
// dump
|
||||
const auto b = env::coredumpOther(type);
|
||||
if (!b) {
|
||||
std::wcerr << L"\n>>>> a minidump file was not written\n\n";
|
||||
}
|
||||
|
||||
std::wcerr << L"Press enter to continue...";
|
||||
std::wcin.get();
|
||||
|
||||
return (b ? 0 : 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace cl
|
||||
{
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
virtual ~Command() = default;
|
||||
|
||||
std::string name() const;
|
||||
std::string description() const;
|
||||
|
||||
po::options_description options() const;
|
||||
std::string usage() const;
|
||||
|
||||
int run(po::variables_map& vm);
|
||||
|
||||
protected:
|
||||
struct Meta
|
||||
{
|
||||
std::string name, description;
|
||||
};
|
||||
|
||||
virtual po::options_description doOptions() const;
|
||||
virtual Meta meta() const = 0;
|
||||
virtual int doRun(po::variables_map& vm) = 0;
|
||||
};
|
||||
|
||||
|
||||
class CrashDumpCommand : public Command
|
||||
{
|
||||
protected:
|
||||
po::options_description doOptions() const;
|
||||
Meta meta() const override;
|
||||
int doRun(po::variables_map& vm) override;
|
||||
};
|
||||
|
||||
|
||||
class CommandLine
|
||||
{
|
||||
public:
|
||||
CommandLine();
|
||||
|
||||
int run(int argc, char** argv);
|
||||
std::string usage(const Command* c=nullptr) const;
|
||||
|
||||
private:
|
||||
po::options_description m_visibleOptions, m_allOptions;
|
||||
po::positional_options_description m_positional;
|
||||
std::vector<std::unique_ptr<Command>> m_commands;
|
||||
|
||||
void createOptions();
|
||||
};
|
||||
|
||||
} // namespace
|
||||
+39
-3
@@ -16,9 +16,15 @@ using namespace MOBase;
|
||||
Console::Console()
|
||||
: m_hasConsole(false), m_in(nullptr), m_out(nullptr), m_err(nullptr)
|
||||
{
|
||||
// open a console
|
||||
if (!AllocConsole()) {
|
||||
// failed, ignore
|
||||
// try to attach to parent
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
if (GetLastError() != ERROR_ACCESS_DENIED) {
|
||||
// parent has no console, create one
|
||||
if (!AllocConsole()) {
|
||||
// failed, ignore
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_hasConsole = true;
|
||||
@@ -1032,6 +1038,36 @@ HandlePtr dumpFile()
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
CoreDumpTypes coreDumpTypeFromString(const std::string& s)
|
||||
{
|
||||
if (s == "data")
|
||||
return env::CoreDumpTypes::Data;
|
||||
else if (s == "full")
|
||||
return env::CoreDumpTypes::Full;
|
||||
else
|
||||
return env::CoreDumpTypes::Mini;
|
||||
}
|
||||
|
||||
std::string toString(CoreDumpTypes type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CoreDumpTypes::Mini:
|
||||
return "mini";
|
||||
|
||||
case CoreDumpTypes::Data:
|
||||
return "data";
|
||||
|
||||
case CoreDumpTypes::Full:
|
||||
return "full";
|
||||
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool createMiniDump(HANDLE process, CoreDumpTypes type)
|
||||
{
|
||||
const DWORD pid = GetProcessId(process);
|
||||
|
||||
@@ -307,6 +307,10 @@ enum class CoreDumpTypes
|
||||
Full
|
||||
};
|
||||
|
||||
|
||||
CoreDumpTypes coreDumpTypeFromString(const std::string& s);
|
||||
std::string toString(CoreDumpTypes type);
|
||||
|
||||
// creates a minidump file for this process
|
||||
//
|
||||
bool coredump(CoreDumpTypes type);
|
||||
|
||||
+5
-26
@@ -49,6 +49,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "env.h"
|
||||
#include "envmodule.h"
|
||||
#include "shared/util.h"
|
||||
#include "commandline.h"
|
||||
|
||||
#include <eh.h>
|
||||
#include "shared/windows_error.h"
|
||||
@@ -811,22 +812,6 @@ int runApplication(
|
||||
return 1;
|
||||
}
|
||||
|
||||
int doCoreDump(env::CoreDumpTypes type)
|
||||
{
|
||||
env::Console c;
|
||||
|
||||
// dump
|
||||
const auto b = env::coredumpOther(type);
|
||||
if (!b) {
|
||||
std::wcerr << L"\n>>>> a minidump file was not written\n\n";
|
||||
}
|
||||
|
||||
std::wcerr << L"Press enter to continue...";
|
||||
std::wcin.get();
|
||||
|
||||
return (b ? 0 : 1);
|
||||
}
|
||||
|
||||
log::Levels convertQtLevel(QtMsgType t)
|
||||
{
|
||||
switch (t)
|
||||
@@ -895,17 +880,11 @@ void initLogging()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TimeThis tt("main to runApplication()");
|
||||
cl::CommandLine cl;
|
||||
|
||||
// handle --crashdump first
|
||||
for (int i=1; i<argc; ++i) {
|
||||
if (std::strcmp(argv[i], "--crashdump") == 0) {
|
||||
return doCoreDump(env::CoreDumpTypes::Mini);
|
||||
} else if (std::strcmp(argv[i], "--crashdump-data") == 0) {
|
||||
return doCoreDump(env::CoreDumpTypes::Data);
|
||||
} else if (std::strcmp(argv[i], "--crashdump-full") == 0) {
|
||||
return doCoreDump(env::CoreDumpTypes::Full);
|
||||
}
|
||||
}
|
||||
const auto r = cl.run(argc, argv);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
initLogging();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user