1

I have this: "ctypes.UInt64("7")"

It is returned by this:

var chars = SendMessage(hToolbar, TB_GETBUTTONTEXTW, local_tbb.idCommand, ctypes.voidptr_t(0));

so

console.log('chars=', chars, chars.toString(), uneval(chars));

gives

'chars=' 'UInt64 {  }' "7" 'ctypes.UInt64("7")'

So I can get the value by going chars.toString(), but I have to run a parseInt on that, is there anyway to read it like a property? Like chars.UInt64?

nmaier
  • 32,336
  • 5
  • 63
  • 78
Noitidart
  • 35,443
  • 37
  • 154
  • 323

1 Answers1

5

The problem with 64-bit integers in js-ctypes is that Javascript lacks a compatible type. All Javascript numbers are IEEE double precision floating point numbers (double), and those can represent 53-bit integers at most. So you shouldn't even be trying to parse the int yourself, unless you know for a fact that the result would fit into a double. E.g. You cannot know this for pointers.

E.g. consider the following:

// 6 * 8-bit = 48 bit; 48 < 53, so this is OK
((parseInt("0xffffffffffff", 16) + 2) == parseInt("0xffffffffffff", 16)) == false
// However, 7 * 8-bit = 56 bit; 56 < 53, so this is not OK
((parseInt("0xffffffffffffff", 16) + 2) == parseInt("0xffffffffffffff", 16)) == true
// Oops, that compared equal, because a double precision floating point
// cannot actual hold the parseInt result, which is still well below 64-bit!

Lets deal with 64-bit integers in JS properly...

If you just want to comparisons, use UInt64.compare()/Int64.compare(), e.g.

// number == another number
ctypes.UInt64.compare(ctypes.UInt64("7"), ctypes.UInt64("7")) == 0
// number != another number
ctypes.UInt64.compare(ctypes.UInt64("7"), ctypes.UInt64("6")) != 0
// number > another number
ctypes.UInt64.compare(ctypes.UInt64("7"), ctypes.UInt64("6")) > 0
// number < another number
ctypes.UInt64.compare(ctypes.UInt64("7"), ctypes.UInt64("8")) < 0

If you need the result, but are not sure it is a 32-bit unsigned integer, you can detect if you're dealing with 32 bit unsigned integers that are just packed into Uint64:

ctypes.UInt64.compare(ctypes.UInt64("7"), ctypes.UInt64("0xffffffff")) < 0

And the analog for 32-bit signed integers in Int64, but you need to compare minimum and maximum:

ctypes.Int64.compare(ctypes.Int64("7"), ctypes.Int64("2147483647")) < 0 &&
ctypes.Int64.compare(ctypes.Int64("7"), ctypes.Int64("-2147483648")) > 0

So, once you know or detected that something will fit into a JS double, it is safe to call parseInt on it.

var number = ...;
if (ctypes.UInt64.compare(number, ctypes.UInt64("0xffffffff")) > 0) {
  throw Error("Whoops, unexpectedly large value that our code would not handle correctly");
}
chars = parseInt(chars.toString(), 10); 

(For the sake of completeness, there is also UInt64.hi()/Int64.hi() and UInt64.lo()/Int64.lo() to get the high and low 32-bits for real 64-bit integers and do 64-bit integer math yourself (e.g.), but beware of endianess).

PS: The return value of SendMessage is intptr_t not uintptr_t, which is important here because SendMessage(hwnd, TB_GETBUTTONTEXT, ...) may return -1 on failure!

So putting all this together (untested):

var SendMessage = user32.declare(
    'SendMessageW',
    ctypes.winapi_abi,
    ctypes.intptr_t,
    ctypes.voidptr_t, // HWND
    ctypes.uint32_t, // MSG
    ctypes.uintptr_t, // WPARAM
    ctypes.intptr_t // LPARAM
);
// ...

var chars = SendMessage(hToolbar, TB_GETBUTTONTEXTW, local_tbb.idCommand, ctypes.voidptr_t(0));
if (ctypes.Int64.compare(chars, ctypes.Int64("0")) < 0) {
  throw new Error("TB_GETBUTTONTEXT returned a failure (negative value)");
}
if (ctypes.Int64.comare(chars, ctypes.Int64("32768")) > 0) {
  throw new Error("TB_GETBUTTONTEXT returned unreasonably large number > 32KiB");
}
chars = parseInt(chars.toString());
Community
  • 1
  • 1
nmaier
  • 32,336
  • 5
  • 63
  • 78
  • Wow wow thank you so much. I promise all your effort is not going to waste I'm using it heavily and hope to release to my addon Profilist so other people can be using it too. Thank you very much! – Noitidart Jun 22 '14 at 19:03
  • Oh wow I made a huge fix on my `SendMessage`. I was using `uintptr_t` I fixed it now to s `intptr_t` – Noitidart Jun 22 '14 at 20:13
  • Actually for the return of `SendMessage` would this be more appropriate: `ctypes.voidptr_t.size == 8 ? ctypes.int64_t : ctypes.long`? – Noitidart Jun 22 '14 at 20:35
  • 1
    That certainly would be possible. If it would be better is up for discussion... It would make writing win32-only code easier, but make it harder to write code that works on 32-bit **and** 64-bit Firefox. And there are 64-bit Firefox on Windows: There are 64-bit Nightlies and also some third party builds, such as PaleMoon x64. I wouldn't want to write code that disregards these and that would break if there ever are official Firefox 64 on Windows release (future-proof), so I wouldn't use the pointer size trick. – nmaier Jun 22 '14 at 22:19
  • Forigve me man I don't understand. So which way is future safe? – Noitidart Jun 22 '14 at 23:14
  • It is future proof to write code that will work on win32 and win64, so using `intptr_t` is future proof, while `ctypes.voidptr_t.size == 8 ? ctypes.int64_t : ctypes.long` is not (unless you write the additional code required to work on win64). – nmaier Jun 22 '14 at 23:20
  • Oh man this must be annoying: I still don't get it :(. I thought that doing `ctypes.voidptr_t.size == 8 ? ctypes.int64_t : ctypes.long` was future safe because if the `size` is `8` than it is `64bit` and uses `int64_t`, and if it is not then it uses `long`, how is this way not safe? – Noitidart Jun 22 '14 at 23:50
  • 1
    On Win32 with `ctypes.long`, you get a number, and on Win64 with `ctypes.int64_t` you'll get a `ctypes.Int64` **wrapped** number. So, you'd still need to handle those in64 numbers like described in my answer, but not the win32 numbers, which would require writing two versions of the same code essentially, one which works with plain `long` numbers and one with wrapped `int64` numbers, test both versions to work correctly. So you gain nothing except more work. Unless you skipped the 64-bit part, in which case your stuff won't be future-proof any longer. – nmaier Jun 23 '14 at 00:24