Created
June 9, 2019 22:37
-
-
Save jephthai/070873cf8542d13186ed673cd325db19 to your computer and use it in GitHub Desktop.
wincon - win32 constant resolver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# | |
# Often, when writing code that interfaces with the Win32 API, I need | |
# to know the numerical value of constants defined in various headers. | |
# Microsoft documents some, but others are conspicuously absent. It's | |
# no fun to grep through *.h files, trying to find it. And keeping | |
# Visual Studio around and open solely to hover over constant names | |
# to see their value is a pain in a Linux dev environment. | |
# | |
# So this program uses the MinGW cross compiler to look up constant | |
# values for you. In some cases, they may be numeric #defines, | |
# whereas other times they may be an index in an enum or an expression | |
# that resolves to a constant at compile time. | |
# | |
# This program shows you the expression that gets macro-expanded, but | |
# also tries to compile a program, storing the constant in a uint64_t. | |
# In most cases, from these two sources you can find the actual value | |
# for the constant. | |
# | |
# E.g., if you want to use the "PROCESS_ALL_ACCESS" constant when you | |
# call OpenProcess() (it's not shown in the MSDN docs!), you can do | |
# this: | |
# | |
# $ wincon PROCESS_ALL_ACCESS | |
# | |
# Consulting the MinGW oracle... | |
# | |
# Constant: PROCESS_ALL_ACCESS | |
# Lexical value: ((0x000F0000l) | (0x00100000l) | 0xfff) | |
# uint64_t value: dec: 2035711 hex: 0x1f0fff bin: 0b111110000111111111111 | |
# | |
# Forth def: $1f0fff value PROCESS_ALL_ACCESS | |
# | |
# Add other include files if you need more than just "windows.h". | |
# Here is one I ran into once, from iphlpapi.dll: | |
# | |
# $ wincon iphlpapi.h TCP_TABLE_BASIC_CONNECTIONS | |
# | |
# Consulting the MinGW oracle... | |
# | |
# Constant: TCP_TABLE_BASIC_CONNECTIONS | |
# Lexical value: TCP_TABLE_BASIC_CONNECTIONS | |
# uint64_t value: dec: 1 hex: 0x1 bin: 0b1 | |
# | |
# Forth def: $1 value TCP_TABLE_BASIC_CONNECTIONS# | |
# | |
# | |
require 'tempfile' | |
$compiler = "x86_64-w64-mingw32-gcc" | |
# | |
# Use the SHIFT-IN and SHIFT-OUT characters to add some color for | |
# fields when printing data out to stdout. | |
# | |
def pputs(msg = "", color=32) | |
msg.gsub!("", "\x1b[#{color};1m") | |
msg.gsub!("", "\x1b[0m") | |
puts(msg) | |
end | |
# | |
# We need at least the constant to be evaluated, but we can also | |
# take any number of additional #include<...> files before that | |
# name too. | |
# | |
if ARGV.length == 0 | |
pputs | |
pputs " usage: wincon [ <include> ] <constant>" | |
pputs | |
pputs " Print value of <constant> after including the indicated" | |
pputs " header files. Uses the MinGW cross compiler, and assumes" | |
pputs " 64-bit compilation." | |
pputs | |
pputs " Note: windows.h will be included by default, so adding" | |
pputs " on the command line would be silly." | |
pputs | |
pputs " Example:" | |
pputs | |
pputs " $ wincon SC_MANAGER_ALL_ACCESS" | |
pputs | |
exit 1 | |
end | |
# | |
# Make sure the MinGW cross compiler is in the path. If not, we | |
# can't very well decode Win32 header constants. | |
# | |
`which #{$compiler} 2>&1 >/dev/null` | |
unless $?.success? | |
pputs | |
pputs(" ERROR: need to have #{$compiler} in $PATH") | |
pputs | |
exit 2 | |
end | |
# | |
# Take a list of include files and make them valid preprocessor | |
# macros for the compiler to consume. | |
# | |
def include_syntax(includes) | |
code = "" | |
includes.each { |include| code += "#include <#{include}>\n" } | |
return code | |
end | |
# | |
# Compile a program with MinGW. 'mode' decides whether we just | |
# send it through the preprocessor, or continue on to assembly | |
# code generation. | |
# | |
def program(code, mode = :preprocess) | |
cflags = { :preprocess => "-E", :compile => "-S" } | |
source = Tempfile.new("source") | |
begin | |
source.write(code) | |
source.close | |
result = `#{$compiler} -O0 #{cflags[mode]} -o - -xc #{source.path}`.chomp | |
return result | |
ensure | |
source.unlink | |
end | |
end | |
# | |
# Construct a program whose last line will be the macro-expanded | |
# value of our target constant. | |
# | |
def lexical_program(includes, value) | |
begin | |
code = include_syntax(includes) | |
code += "#{value}\n" | |
output = program(code, :preprocess) | |
return output.split(/\n/)[-1] | |
rescue | |
return "Unknown" | |
end | |
end | |
# | |
# Construct a program that stores the value of our constant in an | |
# integer field so we can extract it. | |
# | |
def int_value_program(includes, value) | |
begin | |
code = include_syntax(includes) | |
code += "static uint64_t value = (uint64_t)(#{value});\n" | |
code += "int main() { return (int)value; }\n" | |
output = program(code, :compile).split(/\n/) | |
lineno = output.find_index { |i| i =~ /^value:/ } + 1 | |
return output[lineno].split()[1].to_i | |
rescue Exception => e | |
return "Unknown #{e.message}" | |
end | |
end | |
# | |
# Program logic begins here | |
# | |
includes = ["windows.h", "stdint.h", *ARGV[0...-1]] | |
constant = ARGV[-1] | |
pputs | |
pputs(" Consulting the MinGW oracle...") | |
pputs | |
lexical = lexical_program(includes, constant) | |
intval = int_value_program(includes, constant) | |
if intval.is_a?(Fixnum) | |
bases = sprintf("dec: %d hex: 0x%x bin: 0b%b", intval, intval, intval) | |
forth = sprintf("$%x value %s", intval, constant) | |
color = 32 | |
else | |
bases = "ERROR" | |
forth = "ERROR" | |
color = 31 | |
end | |
pputs(" Constant: #{constant}", 34) | |
pputs(" Lexical value: #{lexical}", 34) | |
pputs(" uint64_t value: #{bases}", color) | |
pputs | |
pputs(" Forth def: #{forth}", color) | |
pputs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment