On 12/20/23 11:12, Marcel Timmerman wrote:
Hi,

I would like to initialize a Pointer with some value but get an error. For example;

my $a = Pointer[Str].new('text');

The error thrown:

Default constructor for 'NativeCall::Types::Pointer[Str]' only takes named arguments

The way I can do it now is by creating a CArray and then do a nativecast which is a bit cumbersome. The reason I would like to use Pointers is when there is only one object to point to, not an array. Secondly, for the use of the deref() function to get the value it points to.

Is there another way to solve this or is it a bug in the NativeCall::Types module. I have noticed that thereĀ  are 3 new() methods defined accepting positional arguments.

Regards,
Marcel

Hi Marcel,

I always initialize a pointer with a binary zero.  This is
a null pointer in C.  A "DWORD" is a 32 bit unsigned integer.

Here is some code of mine to work with Windows error messages.
Maybe there is something in it that will give you some examples.
Unfortunately, I did this years ago and have forgotten most of
how I did it.

Note.  Anything that starts with "LP" is a (LONG or 32 bit
not 64 bit -- it'sa Windows thing) pointer.  For example
"LPCWSTR lpSubKey".

Also note that C string are terminated with a nul.  Or
in the case of UTF-16, two nuls.  This is not the case in
Raku as a Raku string comes with a hidden structure
that tells you the length of the string, meaning a
Raku string can contain nuls.

You will see me doing

   my BYTES  $lpBuffer = CArray[BYTE].new( 0xFF xx $nSize );

in places.  This is to help me troubleshoot.  You will
get back "blah blah blah null 0xFF 0xFF ..."

HTH,
-T


constant NULL     = 0x0000;
sub WinFormatMessage( uint32 $ErrorNumber, Bool $Debug = False ) returns Str is export( :WinFormatMessage ) {


#`{

Return a string with the error message corresponding to the error number


https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew


     DWORD FormatMessageW(
DWORD dwFlags, # bitwise OR FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS LPCVOID lpSource, # NULL. The location of the message definition. The type of this parameter depends upon the settings in the dwFlags parameter.
         DWORD   dwMessageId,  # the error message number ($ErrorNumber)
         DWORD   dwLanguageId, # 0 for system's language
         LPTSTR  lpBuffer,     # the return string, give it 1024
         DWORD   nSize,        # 0  nubmer of bytes in the return
         va_list *Arguments    # NULL
     );

https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f0eed1f-a180-4a08-bda9-3dc61e4fdd02/what-is-the-function-to-turn-an-error-number-into-an-error-message?forum=vcgeneral
RLWA comments: Don't worry about the va_list* parameter in the FormatMessageA or FormatMessageW functions. Pass NULL and make sure to specify FORMAT_MESSAGE_IGNORE_INSERTS in the dwFlags parameter

        Note: "LPTSTR is a [long] pointer to a (non-const) TCHAR string"
               LPTSTR: null-terminated string of TCHAR (Long Pointer)

     R. Wieser's declaration:
DWORD dwFlags, // FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS
         LPCVOID lpSource,              // 0
         DWORD   dwMessageId,      // The error code
         DWORD   dwLanguageId,     // 0
         LPWSTR  lpBuffer,                // RW ptr to the string buffer
DWORD nSize, // RW ptr to variable holding available string buffer size
         va_list *Arguments                 // null

}

   my Str $SubName = &?ROUTINE.name;
   my Str $OS      = $*KERNEL.name;
   if not $OS eq "win32" {
      say "Sorry, $SubName only work in Windows.";
      exit; }

my DWORD $dwFlags = FORMAT_MESSAGE_FROM_SYSTEM +| FORMAT_MESSAGE_IGNORE_INSERTS;
   my DWORD  $lpSource     = NULL;
my DWORD $dwMessageId = $ErrorNumber; # error number from the calling function my DWORD $dwLanguageId = LANG_USER_DEFAULT; # 0 is the delault system language

   my DWORD  $nSize        = 1024;  # number of bytes in $lpBuffer, maybe
   # my BYTES  $lpBuffer     = CArray[BYTE].new( 0 xx $nSize );
   my BYTES  $lpBuffer     = CArray[BYTE].new( 0xFF xx $nSize );
   my DWORD  $va_list      = NULL;
   my Str    $ErrorString  = "";
   my DWORD  $RtnCode      = 0;
   my DWORD  $LastError    = 0;


sub FormatMessageW( DWORD, DWORD, DWORD, DWORD, CArray[BYTE] is rw, DWORD is rw, DWORD )
      is native("Kernel32")
      is symbol("FormatMessageW")
      returns DWORD
   { * };

$RtnCode = FormatMessageW( $dwFlags, $lpSource, $dwMessageId, $dwLanguageId, $lpBuffer, $nSize, $va_list );

   # say "$lpBuffer";
   # say $lpBuffer[ 0 ];
   # say $lpBuffer[ 0 ] +| 0x00;
if not $lpBuffer[ 0 ] == -1 { # -1 = 0xFF (it gets coersed) means nothing was returned
      loop (my $Index=0; $Index < $nSize  - 2 ; $Index += 2) {
         my byte $i = $lpBuffer[ $Index ] +| 0x00;
         if $i eq 0x00  { last; }
         $ErrorString ~= chr ( $i );
      }
   }

   $ErrorString ~~ s/ "{chr(13)}{chr(10)}" //;
   if $ErrorString.chars == 0 {
      $LastError   = WinGetLastError();
      $ErrorString = WinFormatMessage( $LastError );
WinMsg( "$SubName Error:", "FormatMessageW returned a failure error code of\n" ~
              "$LastError\n\n" ~ Q[(] ~ $ErrorString ~ Q[)] ~ "\n\n" ~
              "for error request of $ErrorNumber" );
      $ErrorString = "Bad Error Code";
   }

   if $Debug eq True  {
      say( "$SubName" ~ Q[:] ~ " Debug" ~ Q[:] ~ "\n" ~
            "   WinGetLastError          $LastError\n" ~
            "   Error Number             $dwMessageId\n" ~
            "   nSize                    $nSize\n" ~
            "   RtnCode                  $RtnCode\n" ~
            "   Error String Characters  " ~ $ErrorString.chars ~ "\n" ~
" ErrorString " ~ Q[<] ~ $ErrorString ~ Q[>] ~ "\n" ~
            # "   lpBuffer\n\n$lpBuffer" ~
            "\n" );
   }

   return $ErrorString
}

Reply via email to