I am porting from Centos to Cygwin and find that my application is exiting with no error message and exit status zero mid execution during the constructor for Botan::InitializationVector.
If I try to attach with gdb proactively in main() where it is waiting on a spin variable, I don't get a normal stack trace:
(gdb) where
#0 0x7c90120f in ntdll!DbgUiConnectToDbg ()
from /cygdrive/c/WINDOWS/system32/ntdll.dll
#1 0x7c952119 in ntdll!KiIntSystemCall ()
from /cygdrive/c/WINDOWS/system32/ntdll.dll
#2 0x00000005 in ?? ()
#3 0x00000000 in ?? ()
So with no gdb, it is hard to figure out what is going wrong.
Why would I get no error message on Cygwin yet the application would exit mid execution?
I deduce it is inside the constructor due to clog only showing for line before and not after constructor:
clog << " About to create iv for Botan.\n";
Botan::InitializationVector iv(_rng, size);
clog << " About to copy iv for Botan.\n";
Botan is open source: http://botan.randombit.net/ Here are some code snippets from src/sym_algo/symkey.{h,cpp}:
typedef OctetString InitializationVector;
class BOTAN_DLL OctetString
{
public:
u32bit length() const { return bits.size(); }
SecureVector<byte> bits_of() const { return bits; }
const byte* begin() const { return bits.begin(); }
const byte* end() const { return bits.end(); }
std::string as_string() const;
OctetString& operator^=(const OctetString&);
void set_odd_parity();
void change(const std::string&);
void change(const byte[], u32bit);
void change(const MemoryRegion<byte>& in) { bits = in; }
OctetString(class RandomNumberGenerator&, u32bit len);
OctetString(const std::string& str = "") { change(str); }
OctetString(const byte in[], u32bit len) { change(in, len); }
OctetString(const MemoryRegion<byte>& in) { change(in); }
private:
SecureVector<byte> bits;
};
OctetString::OctetString(RandomNumberGenerator& rng,
u32bit length)
{
bits.create(length);
rng.randomize(bits, length);
}
I moved the failing code into main() and it works fine. I also put a try catch ... around the code and no exceptions are being thrown. Something goes wrong between main() and the point of failure later in the application. I can do a divide and conquer to narrow down the exact point where it no longer works. One of the Botan developers gave me this stripped down code to use instead that also fails:
Botan::AutoSeeded_RNG _rng;
unsigned int size = 1; // or 16, or 1452 all fail.
Botan::SecureVector<Botan::byte> iv_val(size);
cerr << "We get to here." << endl;
_rng.randomize(&iv_val[0], size);
cerr << "But not here." << endl;
Now that I have the debugger working I see segv:
(gdb) s
Botan::AutoSeeded_RNG::randomize (this=0x1270380, out=0x5841420 "", len=1)
at ../../src/Botan-1.8.11/build/include/botan/auto_rng.h:23
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x005d79ee in Botan::AutoSeeded_RNG::randomize (this=0x1270380,
out=0x5841420 "", len=1)
at ../../src/Botan-1.8.11/build/include/botan/auto_rng.h:23
(gdb) p rng
$7 = (class Botan::RandomNumberGenerator *) 0x5841324
(gdb) p *this
$8 = {<Botan::RandomNumberGenerator> = {
_vptr$RandomNumberGenerator = 0x11efc14}, rng = 0x5841324}
(gdb) p *rng
$9 = {_vptr$RandomNumberGenerator = 0x656e6f4e}
Here is auto_rng.h code:
class BOTAN_DLL AutoSeeded_RNG : public RandomNumberGenerator
{
public:
void randomize(byte out[], u32bit len)
{ rng->randomize(out, len); } // SEGV on this line.
bool is_seeded() const
{ return rng->is_seeded(); }
void clear() throw() { rng->clear(); }
std::string name() const
{ return "AutoSeeded(" + rng->name() + ")"; }
void reseed(u32bit poll_bits = 256) { rng->reseed(poll_bits); }
void add_entropy_source(EntropySource* es)
{ rng->add_entropy_source(es); }
void add_entropy(const byte in[], u32bit len)
{ rng->add_entropy(in, len); }
AutoSeeded_RNG(u32bit poll_bits = 256);
~AutoSeeded_RNG() { delete rng; }
private:
RandomNumberGenerator* rng;
};
Cygwin apps are multithreaded (e.g., one thread is the signal listener thread). Use info threads in gdb to find the thread that really faulted.
You could attach gdb proactively and put a breakpoint right before the constructor call that is failing, then single step through.
Based on the new code, you're violating the Rule of Three and this may be causing your problem.
By defining a class with raw pointers and not providing a correct copy constructor (or making it inaccessible) you open yourself up to double-free.
Add the copy constructor. Or open an issue on the project's bug tracker. You are using the latest version, right?
Related
This is the class in question (only functions that pertain to this question) and everything it depends on (all written myself). It provides an interface to a DLL.
struct MemRegion {
const uint64_t address;
const uint64_t size;
};
enum Version {
VERSION_US,
VERSION_JP
};
const struct MemRegion SEGMENTS[2][2] = {
{{1302528, 2836576},
{14045184, 4897408}},
{{1294336, 2406112},
{13594624, 4897632}},
};
using Slot = array<vector<uint8_t>, 2>;
class Game {
private:
Version m_version;
HMODULE m_dll;
const MemRegion* m_regions;
public:
Game(Version version, cstr dll_path) {
m_version = version;
m_dll = LoadLibraryA(dll_path);
if (m_dll == NULL) {
unsigned int lastError = GetLastError();
cerr << "Last error is " << lastError << endl;
exit(-2);
}
// this is a custom macro which calls a function in the dll
call_void_fn(m_dll, "sm64_init");
m_regions = SEGMENTS[version];
}
~Game() {
FreeLibrary(m_dll);
}
void advance() {
call_void_fn(m_dll, "sm64_update");
}
Slot alloc_slot() {
Slot buffers = {
vector<uint8_t>(m_regions[0].size),
vector<uint8_t>(m_regions[1].size)
};
return buffers;
}
void save_slot(Slot& slot) {
for (int i = 0; i < 2; i++) {
const MemRegion& region = m_regions[i];
vector<uint8_t>& buffer = slot[i];
cerr << "before memmove for savestate" << endl;
memmove(buffer.data(), reinterpret_cast<void* const>(m_dll + region.address), region.size);
cerr << "after memmove for savestate" << endl;
}
}
};
When I call save_slot(), it should copy two blocks of memory to a couple of vector<uint8_t>s. This does not seem to be the case, though. The function finishes the first copy, but throws a segmentation fault at the second memcpy. Why does it only happen at the second copy, and how can I get around this sort of issue?
Edit 1: This is what GDB gives me when the program terminates:
Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffac2164452 in msvcrt!memmove () from C:\Windows\System32\msvcrt.dll
Edit 2: I tried accessing the segments individually. It works, but for some reason, I can't access both segments in the same program.
I found out that HMODULE is equivalent to void*. Since you can't really use pointer arithmetic on void*s, you have to cast it to a uint8_t* or equivalent to properly get an offset.
Here's what that looks like in practice:
void save_state(Slot& slot) {
uint8_t* const _dll = (uint8_t*)((void*)m_dll);
for (int i = 0; i < 2; i++) {
MemRegion segment = m_regions[i];
std::vector<uint8_t>& buffer = slot[i];
memmove(&buffer[0], _dll + segment.address, segment.size);
}
}
I seem to be getting a segmentation fault when I call std::make_unique<uint8_t []>. I'm pretty sure I know where the segmentation fault is occurring because I used gdb; however, I can't figure out how to fix it. Here is the source code with irrelevant parts filtered out.
loader.cc:
#include "loader.h"
... // Other code that would make this post unnecessarily long
Section::Section(Binary& bin, bfd *bfd_h, asection *as) : binary(bin) {
int bfd_flags = bfd_section_flags(as);
const char *secname;
Section::SectionType sectype;
sectype = Section::SectionType::NONE;
if (bfd_flags & SEC_CODE) {
sectype = Section::SectionType::CODE;
} else if (bfd_flags & SEC_DATA) {
sectype = Section::SectionType::DATA;
}
this->type = sectype;
this->vma = bfd_section_vma(as);
this->size = bfd_section_size(as);
secname = bfd_section_name(as);
this->name = std::string { secname != nullptr ? secname : "<unnamed>" };
this->bytes = std::make_unique<uint8_t []>(this->size); // SEGFAULT RIGHT HERE
if (!bfd_get_section_contents(bfd_h, as, this->bytes.get(), 0, this->size)) {
throw std::runtime_error("Failed to read section");
}
}
int Binary::load_binary(std::string& fname) {
int ret;
const bfd_arch_info_type *bfd_info;
std::unique_ptr<bfd, bfd_deleter> bfd_h;
try {
bfd_h = open_bfd(fname);
} catch (bfd_exception& be) {
std::cerr << be.fname() << ": " << be.what() << " (" << be.errormsg() << ")" << std::endl;
goto fail;
}
this->filename = std::string (fname);
this->entry = bfd_get_start_address(bfd_h.get());
this->type_str = std::string (bfd_h->xvec->name);
switch (bfd_h->xvec->flavour) {
case bfd_target_elf_flavour:
this->type = Binary::BinaryType::ELF;
break;
case bfd_target_coff_flavour:
this->type = Binary::BinaryType::PE;
break;
case bfd_target_unknown_flavour:
default:
std::cerr << "unsupported binary type ("
<< bfd_h->xvec->name << ")" << std::endl;
goto fail;
}
bfd_info = bfd_get_arch_info(bfd_h.get());
this->arch_str = std::string{bfd_info->printable_name};
switch (bfd_info->mach) {
case bfd_mach_i386_i386:
this->arch = Binary::BinaryArch::X86;
this->bits = 32;
break;
case bfd_mach_x86_64:
this->arch = Binary::BinaryArch::X86;
this->bits = 64;
break;
default:
std::cerr << "unsupported architecture (" << bfd_info->printable_nameĀ·
<< bfd_info->printable_name << ")" << std::endl;
goto fail;
}
this->load_symbols(bfd_h.get());
this->load_dynsym(bfd_h.get());
if (this->load_sections(bfd_h.get()) < 0) goto fail;
ret = 0;
goto success;
fail:
ret = -1;
success:
return ret;
}
... // Other code that would make this post unnecessarily long
loader.h:
#include <memory>
#include <vector>
#include <bfd.h>
#include <inttype.h>
... // Other code that would make this post unnecessarily long
class Section {
public:
enum class SectionType { NONE, CODE, DATA };
Section(Binary& bin);
Section(Binary& bin, bfd *bfd_h, asection *as);
bool contains(uint64_t addr);
void info();
private:
friend class Binary;
Binary& binary;
std::string name;
SectionType type;
uint64_t vma;
uint64_t size;
std::unique_ptr<uint8_t []> bytes;
};
class Binary {
public:
enum class BinaryType { AUTO, ELF, PE };
enum class BinaryArch { NONE, X86 };
Binary();
Section* get_section_text();
int load_binary(std::string &fname);
void info();
private:
std::string filename;
BinaryType type;
std::string type_str;
BinaryArch arch;
std::string arch_str;
unsigned bits;
uint64_t entry;
std::vector<Section> sections;
std::vector<Symbol> symbols;
int load_symbols(bfd *bfd_h);
int load_dynsym(bfd *bfd_h);
int load_sections(bfd *bfd_h);
... // Other code that would make this post unnecessarily long
};
Here is the backtrace that gdb returned:
(gdb) bt
#0 0x00007fffff335b9f in unlink_chunk (p=p#entry=0x802b7d0, av=0x7fffff46cb80 <main_arena>) at malloc.c:1453
#1 0x00007fffff338881 in _int_malloc (av=av#entry=0x7fffff46cb80 <main_arena>, bytes=bytes#entry=36) at malloc.c:4041 #2 0x00007fffff339a84 in __GI___libc_malloc (bytes=36) at malloc.c:3058
#3 0x00007fffff546045 in operator new(unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x0000000008003b94 in std::make_unique<unsigned char []> (__num=36) at /usr/include/c++/10/bits/unique_ptr.h:966
#5 Section::Section (this=0x7ffffffedd80, bin=..., bfd_h=0x801b730, as=0x801dbf8) at loader.cc:66
#6 0x0000000008003d53 in Binary::load_sections (this=this#entry=0x7ffffffedec0, bfd_h=bfd_h#entry=0x801b730)
at loader.cc:122
#7 0x000000000800487e in Binary::load_binary (this=0x7ffffffedec0, fname=...) at loader.cc:185
#8 0x0000000008002451 in main (argc=<optimized out>, argv=<optimized out>) at loader_demo.cc:13
This code is based on code from "Practical Binary Analysis", so pardon the goto's. I'm also using the bfd library.
TLDR: The book I was using had a function A and another function B that was similar to it. I entered the code into my text editor and wrote the code for function A in my editor. Then I copied and pasted A and modified it into function B. Except that I had forgotten to make one change and that was what was causing the segfault.
I finally found out why my program was segfaulting... kind of. It turns out there was nothing wrong with any code even near make_unique; however, I thought that was the case since the stack trace that gdb output indicated that the error was at make_unique.
The error was actually from copying code. There was a code snippet in the book of a function called load_symbols_bfd. I typed the code into my text editor and modified the code into more modern C++. There was a similar function called load_dynsym_bfd. I figured that I could copy the modified version of load_symbols_bfd and change it into my own version of load_dynsym_bfd. However, I had forgotten to change one of the function names in my modified version of load_dynsym_bfd that had been in load_symbols_bfd; I had forgotten to change bfd_canonicalize_symtab into bfd_canonicalize_dynamic_symtab. When the line containing bfd_canonicalize_symtab was commented out in my modified version of load_dynsym_bfd the segfault disappeared.
I'm building a library consisting of a class (MyTotp) that has an object as property (Profile).
In the main test function I built a vector of Profile objects and a for loop which iterates through vector assigning the object to the MyTotp property and calculating the result.
The program compiles correctly. When it is run the first loop is executed correctly but the second one raise a segmentation fault (core dump).
Debugging the program I noticed that the error appears after the second re-allocation of memory (required_size may change (and it does into the test) in each loop) for array uint8_t * secret as pointed into the code. I thought the re-allocation was the cause for the memory failure, but I can't exclude anything
Below the significant code...
MyTotp.h
class buzztotp {
public:
[...]
Profile profile;
[...]
};
}
MyTotp.cpp
void MyTotp::getPaddedSecret(uint8_t * out) {
uint8_t* secret = this->profile.getSecret();
uint32_t secret_len = this->profile.getSecretLen();
for (int i = 0; i < this->required_size; i++) {
int index = i % secret_len;
out[i] = secret[index];
}
delete[] secret; secret = NULL;
}
uint32_t MyTotp::generateTOTP() {
this->preprocess(); //It calculates, according to profile properties, this->required_size
uint8_t* secret = new uint8_t[this->required_size]; //Here the memory error while debugging
this->getPaddedSecret(secret);
[...]
delete[] secret; secret = NULL;
return (...);
}
Profile.h
uint8_t* Profile::getSecret() const {
uint8_t* out;
out = new uint8_t[this->secret_len]; //#Edit1 - Error now here
//memcpy(out, this->secret, this->secret_len*sizeof(uint8_t));
std::copy(this->secret_vector.begin(), this->secret_vector.end(), out);
return out;
}
main.cpp
int main(void) {
currentDir();
XmlResourceManager xmlresman;
xmlresman.setFilename(std::string("...myfile.xml"));
/*
* Here the vector<Profile> xmlresman::profiles is built as:
*
* Profile __profile;
* [...]
* this->profiles.push_back(__profile);
*/
xmlresman.parseXml();
for (unsigned int i = 0; i < xmlresman.profiles.size(); i++) {
MyTotp mytotp;
buzztotp.profile = xmlresman.profiles[i];
try {
std::cout << "TOTP: " << mytotp.generateTOTP() << std::endl;
} catch (std::string str) {
std::cerr << str << std::endl;
}
//mytotp.~mytotp();
}
return 0;
}
Any suggestions?
Edit1
I used two strategies (I can't figure the best out) for elaborating a uint8_t* by a function: generating/instantiating it into the parent block and passing it to the function as an argument OR returning a generated/instantiated pointer into the function itself. Running the debug again it seems the error is at the re-allocation of out into Profile::getSecret(). I'm starting to get confused about the behaviour of the program run.
The explicit destructor (mytotp) into the main loop was only a past try: it is a misprint, completely useless by now.
Edit2
Cancelling the explicit constructor seems to solve the problem.
I want to print the backtrace of an exception (the state at the point is where was thrown), but in a catch block.
I am also just targetting gcc, but would like to keep unportable code to a minimum (I know the stack printing code I show in my examples is non-portable).
From what I understood so far, in the catch block the stack was already partially unwond, so it is no longer there to be read.
To be fair, it should still be there (in memory), if no local variables are created and no method calls are done, but I'm not sure how safe I would be trying to read it this way.
I also noticed that if I register a terminate method (via std::set_terminate) and have no catch block, then the full stack at the point where the unhandled exception was thrown is available to the handler. I'm guessing this is because it was unwound totally, but the original values in the stack was not overwritten (and most likely the terminate handler somehow has an indepentent call stack).
I tested this under gcc:
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <execinfo.h>
using namespace std;
void handler()
{
void *trace_elems[20];
int trace_elem_count(backtrace(trace_elems, 20));
char **stack_syms(backtrace_symbols(trace_elems, trace_elem_count));
for(int i=0 ; i != trace_elem_count; ++i)
{
cout << stack_syms[i] << "\n";
}
free(stack_syms);
exit(1);
}
void level3() { throw std::runtime_error("oops"); }
void level2() { level3(); }
void level1() { level2(); }
If I use it like this, the exception backtrace is lost (only main and handler are in the call stack):
void main()
{
try { level1(); }
catch(...) { handler();}
}
But if I call it like this, the backtrace at the point the exception was thrown is printed:
void main()
{
std::set_terminate(handler);
level1();
}
Some context on my use case: I have two running processes, one produces requests and the other will process them. The execution of these requests can sometimes result in exceptions. At that point the backtrace is usefull to figure out why it failed (what() very often does not have enough information), but I cannot let the exception reach main, since both processes need to continue working. I just need to print the backtrace and return a message signalling failure to execute.
EDIT: A common solution, that is being suggested to this problem is having a base exception class that captures the call stack on construction, and has it available to print out later. This is certainly a possible solution (and one I might have to resort to, if no better solution is found).
I am avoiding it at the moment because:
It adds some (considerable) overhead to capture the call stack at the moment I throw the exception
I have a relatively high percentage of cases where I can actually handle the exception before having to give up and print the stack trace.
You could create a custom exception class, that would in its constructor, call backtrace and store the buffer within itself.
When caught, such exception would have the required data to print the trace. In fact, the handler could well be a member function of the exception. Note that the trace would include the extra call to the constructor of the exception.
This works as long as you only throw the custom exception or exceptions derived from it.
Best I can suggest is to collect the stack trace at the point of the throw.
This code may need a little refining, but it should give you an idea:
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <vector>
#include <execinfo.h>
using namespace std;
inline
auto make_stack_trace(int depth = 20) {
auto trace_elems = std::vector<void *>(depth);
auto trace_elem_count = backtrace(trace_elems.data(), depth);
char **stack_syms = backtrace_symbols(trace_elems.data(), trace_elem_count);
std::vector<std::string> symbols;
symbols.reserve(trace_elem_count);
for (int i = 0; i < trace_elem_count; ++i) {
symbols.emplace_back(stack_syms[i]);
}
free(stack_syms);
return symbols;
}
struct tracer {
tracer(const std::vector<std::string> &trace)
: trace_(trace) {}
friend std::ostream &operator<<(std::ostream &os, const tracer &t) {
const char *sep = "";
for (const auto &line : t.trace_) {
os << sep << line;
sep = "\n";
}
return os;
}
const std::vector<std::string> &trace_;
};
struct has_stack_trace {
has_stack_trace(std::vector<std::string> trace) : trace_(std::move(trace)) {}
auto trace() const {
return tracer(trace_);
}
virtual const char* what() const noexcept = 0;
std::vector<std::string> trace_;
};
template<class Exception>
struct with_stack_trace : has_stack_trace, Exception {
template<class...Args>
with_stack_trace(Args &&...args)
: has_stack_trace(make_stack_trace()), Exception(std::forward<Args>(args)...) {
}
virtual const char* what() const noexcept override
{
return Exception::what();
}
};
void level3() { throw with_stack_trace<std::runtime_error>("oops"); }
void level2() { level3(); }
void level1() { level2(); }
int main() {
try {
level1();
}
catch (has_stack_trace const &e) {
std::cout << e.what() << std::endl;
std::cout << e.trace() << std::endl;
}
catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
}
Sample output:
oops
0 parallel-find 0x000000010bad09e9 _Z16make_stack_tracei + 105
1 parallel-find 0x000000010bad08ec _ZN16with_stack_traceISt13runtime_errorEC2IJRA5_KcEEEDpOT_ + 44
2 parallel-find 0x000000010bacf46d _ZN16with_stack_traceISt13runtime_errorEC1IJRA5_KcEEEDpOT_ + 29
3 parallel-find 0x000000010bacf40a _Z6level3v + 42
4 parallel-find 0x000000010bacf4a9 _Z6level2v + 9
5 parallel-find 0x000000010bacf4b9 _Z6level1v + 9
6 parallel-find 0x000000010bacf4d7 main + 23
7 libdyld.dylib 0x00007fffa6346255 start + 1
8 ??? 0x0000000000000001 0x0 + 1
Process finished with exit code 0
EDIT: After even more code modification, the error is still there, modified code shown:
KeyDown():
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
PollKeyboard():
LPDIRECTINPUTDEVICE8 di_keyboard;
void PollKeyboard()
{
long result = di_keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);
char para[16];
itoa(result, para, 17);
if(result != DI_OK) MessageBox(NULL, para, "ERROR", MB_OK);
}
When I try to put MessageBox within a KeyDown() if statement (as seen below in the game loop), the MessageBox simply coninues to appear even if I stop pressing the key, ie: I press , the "Would you like to quit?" message box appears, I say no, It disappears and then instantly reappears, as if I were still holding the key.
This is my loop:
void GameRun(HWND hWnd) //called once every frame
{
PollKeyboard();
if(GetTickCount - start >= 30)
{
if(KeyDown(DIK_LEFT))
MoveLeft();
if(KeyDown(DIK_RIGHT))
MoveRight();
}
if(d3ddev->BeginScene())
{
//rendering
}
if(KeyDown(DIK_ESCAPE))
{
//any MessageBox()
int result = MessageBox(hWnd, "I'm causing so much trouble!", "IMMORTAL", MB_YESNOCANCEL);
if(result == IDYES)
//end
}
}
EDIT: The catch in PollKeyboard() displays the sequence 53gd6bcc, I could not, however, find the error code it corresponds to.
EDIT: After another test, I saw that even if the MessageBox is not within a KeyDown() if statement, the glitch still occurs.
EDIT: After a bit more testing, it appears that MessageBox itself is causing the glitch.
Because the sample code works, something else in your program is causing the bug. Try moving bits of the code below into your own until it works, then you will know which section of code was culprit.
Sample Code
Alright, huge code block coming up. This code works correctly for me. (Escape, along with all other keys successfully activate and deactivate). It's large, commented, and explains things fairly well. Try this, if it works, we'll examine other parts of your program, if not, I can only leave you with "Good luck", and take what you want:
// DirectInput
#define DIRECTINPUT_VERSION 0x0800
#include<dinput.h>
// Standard stuff
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
// Link from code, MSVC specific, could be done in project settings
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
// Utility lexical_cast, use Boost if possible.
// Simple replacement, converts a stream-able `T`
// to a string
template <typename T>
const std::string lexical_cast(const T& pValue)
{
std::stringstream ss;
ss << pValue;
return ss.str();
}
// Utility function + macro to execute DirectX code with exceptions.
// Kinda ugly, but helpful for us.
void check_error(HRESULT pResult, const std::string& pFuncName)
{
// DI_OK == S_OK, but S_OK is more general, so we'll use that
if (pResult != S_OK)
{
throw std::runtime_error("Error executing: " + pFuncName +
"! Returned: " + lexical_cast(pResult));
}
}
// Macro, makes calling the function easier. It is wrapped in
// an `if` statement for reasons outlined in:
// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.5
#define CHECK_ERROR(x) if (true) { check_error(x, #x); } else (void)0
// The above gives the warning:
// "warning C4127: conditional expression is constant", disable below:
#pragma warning(disable: 4127)
// Manages input
class input_manager
{
public:
// Constants
static const int NumberKeys = 256;
// Creation
input_manager(void)
{
// Create input and keyboard (like I said, ugly macro, but helpful :] )
CHECK_ERROR(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, reinterpret_cast<void**>(&_input), 0));
CHECK_ERROR(_input->CreateDevice(GUID_SysKeyboard, &_keyboard, 0));
CHECK_ERROR(_keyboard->SetDataFormat(&c_dfDIKeyboard));
CHECK_ERROR(_keyboard->Acquire());
}
~input_manager(void)
{
// Free resources. Note: Many programmers
// unnecessarily wrap this stuff in
// `if (_keyboard !=0)`, and then
// `_keyboard = 0`. This is completely unnecessary,
// because destructors are only run one time.
// Also, I can assume they are valid, because if they
// weren't, we wouldn't be here (an exception would have
// been thrown)
_keyboard->Unacquire();
_keyboard->Release();
_input->Release();
// Also, if we wrapped this into a nice RAII class, we wouldn't
// be forced to write a destructor, but this is outside the scope.
// Feel free to ask how; additionally, since we're on the topic, if you'd
// like more tips handling input (I've written PLENTY of input managers)
// I'm free for asking about things like testing for triggers rather than pressed
// ("was it pressed, regardless if it's being held now" versus
// "is it being pressed"), etc.
}
// Operations
void update(void)
{
CHECK_ERROR(_keyboard->GetDeviceState(NumberKeys, reinterpret_cast<void*>(&_keys)));
}
// Query
bool key_pressed(int pKey) const
{
return test_key(pKey);
}
// Might wrap into an operator[] for convenience.
private:
// Constants
static const int PressMask = 0x80;
// Sorry for the confusion, but indeed, with
// `char`s the mask is simply 0x80.
// Utility
bool test_key(int pKey) const
{
return (_keys[pKey] & PressMask) != 0;
}
// Members
LPDIRECTINPUT8 _input;
LPDIRECTINPUTDEVICE8 _keyboard;
char _keys[NumberKeys];
};
void test_keys(const input_manager& input)
{
bool anyPressed = false;
for (unsigned i = 0; i < input_manager::NumberKeys; ++i)
{
if (input.key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
anyPressed = true;
}
}
if (!anyPressed)
{
std::cout << "No keys pressed." << std::endl;
}
}
void execute(void)
{
input_manager input;
std::cout << "Press Q to quit." << std::endl;
bool running = true;
while (running)
{
input.update();
if (input.key_pressed(DIK_Q))
{
running = false;
}
test_keys(input);
Sleep(0); // give some processor time
}
}
int main(void)
{
// Place real code in an execute function, so main
// is clean and ready to catch exceptions:
try
{
execute();
}
catch (const std::exception& e)
{
// Error!
std::cerr << "Unhandled exception:" << e.what() << std::endl;
}
}
Old suggestion:
Try catching the return value from GetDeviceState:
HRESULT result = // v Prefer C++-style casts
di_keyboard->GetDeviceState(sizeof(keys), reinterpret_cast<void*>(&keys);
if (result != DI_OK)
{
// uh-oh
std::cout << result << std::endl;
}
Compare it against the table here.
Old Semi-Answer:
Shortly after editing in the code in the Extra Stuff section, I realized the error, sorry I didn't catch it earlier. You're testing the wrong bit :)
Observe:
// v HERE! Should be 0x8000, not 0x80.
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
Try that:
int KeyDown(int key)
{
return (keys[key] & 0x8000);
}
Also, this should be moved into a constant to avoid magic numbers:
// somewhere, probably in the private section of the class or in a detail namespace:
static const int PushedMask = 0x8000;
// code reads better:
int KeyDown(int key)
{
return (keys[key] & PushedMask);
}
Lastly, in C++ you have a bool type, so take advantage of it!
// v here
bool KeyDown(int key)
{
return (keys[key] & PushedMask);
}
I know Visual Studio will warn about this conversion from int to bool, so you can get rid of it while also making your intents clearer:
bool KeyDown(int key)
{
return (keys[key] & PushedMask) != 0; // or == 1, your choice
}
Extra Stuff:
Try the following code:
#include <iostream>
#include <windows.h>
bool key_pressed(int pKeyCode)
{
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
}
void test_keys(void)
{
for (unsigned i = 0; i < 255; ++i)
{
if (key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
}
}
}
int main(void)
{
bool running = true;
while (running)
{
if (key_pressed(VK_ESCAPE))
{
running = false;
}
test_keys();
Sleep(0);
}
}
This works for me (responds to all keys, quits on escape). Minimal test case for GetAsyncKeyState. If this does not work, please add OS, Keyboard, etc, in your comment.
If you create a MessageBox(Null,...) you won't have any control over the window after it's creation. IE, the window won't disappear when you depress the key.
As for why it keeps on appearing, seems to have something to do with this:
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
keys consits of 1 byte long characters, while input_bit_num is a 2 byte value. While I don't honestly know which bit it is that you're looking for (0xff - 0x00 is the domain of 1 byte).
Honestly, I'm surprised that your code runs, unless the & operation is carrying over into keys[key-1] in which case any KeyDown is undefined, and KeyDown(...) when key is 0 is particularly dangerous.