6

I am trying to use the GetConsoleScreenBufferInfo(HANDLE, PCONSOLE_SCREEN_BUFFER_INFO) function from the Windows API using Perl 6 and (of course) NativeCall.

I think I have set up the CONSOLE_SCREEN_BUFFER_INFO struct the function needs correctly, but the code crashes after the call when I try to dump its content.

This is the shortest (not quite but close) way to demonstrate the problem:

use NativeCall;

constant \HANDLE            := Pointer[void];
constant \SHORT             := int16;
constant \USHORT            := uint16;
constant \WORD              := uint16;
constant \DWORD             := uint32;
constant \BOOL              := int32;
constant \STD_OUTPUT_HANDLE := -11;
constant \STD_INPUT_HANDLE  := -10;

class COORD is repr('CStruct')            {
  has SHORT $.X;
  has SHORT $.Y;
}

class SMALL_RECT is repr("CStruct")            {
  has SHORT $.Left;
  has SHORT $.Top;
  has SHORT $.Right;
  has SHORT $.Bottom;
};

class CONSOLE_SCREEN_BUFFER_INFO is repr("CStruct")            {
  has COORD $.dwSize;
  has COORD $.dwCursorPosition;
  has WORD $.wAttributes;
  has SMALL_RECT $.srWindow;
  has COORD $.dwMaximumWindowSize;

  submethod TWEAK {
    $!dwSize := COORD.new;
    $!dwCursorPosition := COORD.new;
    $!srWindow := SMALL_RECT.new;
    $!dwMaximumWindowSize := COORD.new;
  }
}

# C: BOOL WINAPI GetConsoleScreenBufferInfo(_In_  HANDLE hConsoleOutput, _Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
sub GetConsoleScreenBufferInfo(HANDLE, CONSOLE_SCREEN_BUFFER_INFO is rw) is native("Kernel32.dll") returns BOOL { * };
sub GetStdHandle(DWORD) is native('Kernel32') returns Pointer[void]  { * };

my CONSOLE_SCREEN_BUFFER_INFO
  $info = CONSOLE_SCREEN_BUFFER_INFO.new;

my HANDLE
  $handle-o = GetStdHandle( STD_OUTPUT_HANDLE );

dd $info;
say "GetConsoleScreenBufferInfo ", GetConsoleScreenBufferInfo( $handle-o, $info );
say "Will I live?";
dd $info; #crashes without notice

Any hints as to why the crash occurs and how to fix it are very welcome.

Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
Holli
  • 5,072
  • 10
  • 27
  • It crashes as soon as I try to access anything. dd was just exemplary. – Holli Aug 21 '19 at 23:56
  • `constant \STD_INPUT_HANDLE := -10;` presumably binds an `Int`, with `is repr('P6Int')`, to `STD_INPUT_HANDLE`. And in `GetStdHandle( STD_OUTPUT_HANDLE )` that gets used to bind to a 32 bit `DWORD`. Does Rakudo automatically coerce or whatever has to be done to make that work? – raiph Aug 22 '19 at 00:29
  • Yes, that works transparently. I use another handle that I obtain in the same way for input and that works fine. The problem must be in GetConsoleScreenBufferInfo – Holli Aug 22 '19 at 01:14
  • I probably need to use "Pointer" somehow. I will look into it again tomorrow. – Holli Aug 22 '19 at 01:16
  • Do you really need `TWEAK` method? Whole structure is overwritten by `GetConsoleScreenBufferInfo`. I don't know how Perl works internally, maybe it adds vtable which is overwriten by API call. In C++ for example you can't add virtual methods because they could need additional storage for vtable pointer. – Daniel Sęk Aug 22 '19 at 06:25
  • The attribute declaration probably should read `HAS SMALL_RECT $.srWindow;` – Christoph Aug 22 '19 at 09:53

1 Answers1

9

You need to use HAS instead of has for the members of CONSOLE_SCREEN_BUFFER_INFO that are structures as these are embedded instead of referenced by pointer (which is the Perl6 default).

Once you do that, you can drop the TWEAK as well so the code will read

class CONSOLE_SCREEN_BUFFER_INFO is repr("CStruct") {
  HAS COORD $.dwSize;
  HAS COORD $.dwCursorPosition;
  has WORD $.wAttributes;
  HAS SMALL_RECT $.srWindow;
  HAS COORD $.dwMaximumWindowSize;
}
Christoph
  • 164,997
  • 36
  • 182
  • 240