1package FFI::Platypus::Lang::Win32;
2
3use strict;
4use warnings;
5use 5.008004;
6use Config;
7
8# ABSTRACT: Documentation and tools for using Platypus with the Windows API
9our $VERSION = '1.56'; # VERSION
10
11
12sub abi
13{
14  $^O =~ /^(cygwin|MSWin32|msys)$/ && $Config{ptrsize} == 4
15  ? 'stdcall'
16  : 'default_abi';
17}
18
19
20my %map;
21
22sub native_type_map
23{
24  unless(%map)
25  {
26    require FFI::Platypus::ShareConfig;
27    %map = %{ FFI::Platypus::ShareConfig->get('type_map') };
28
29    my %win32_map = qw(
30      BOOL                      int
31      BOOLEAN                   BYTE
32      BYTE                      uchar
33      CCHAR                     char
34      CHAR                      char
35      COLORREF                  DWORD
36      DWORD                     uint
37      DWORDLONG                 uint64
38      DWORD_PTR                 ULONG_PTR
39      DWORD32                   uint32
40      DWORD64                   uint64
41      FLOAT                     float
42      HACCEL                    HANDLE
43      HANDLE                    PVOID
44      HBITMAP                   HANDLE
45      HBRUSH                    HANDLE
46      HCOLORSPACE               HANDLE
47      HCONV                     HANDLE
48      HCONVLIST                 HANDLE
49      HCURSOR                   HICON
50      HDC                       HANDLE
51      HDDEDATA                  HANDLE
52      HDESK                     HANDLE
53      HDROP                     HANDLE
54      HDWP                      HANDLE
55      HENHMETAFILE              HANDLE
56      HFILE                     int
57      HFONT                     HANDLE
58      HGDIOBJ                   HANDLE
59      HGLOBAL                   HANDLE
60      HHOOK                     HANDLE
61      HICON                     HANDLE
62      HINSTANCE                 HANDLE
63      HKEY                      HANDLE
64      HKL                       HANDLE
65      HLOCAL                    HANDLE
66      HMENU                     HANDLE
67      HMETAFILE                 HANDLE
68      HMODULE                   HINSTANCE
69      HMONITOR                  HANDLE
70      HPALETTE                  HANDLE
71      HPEN                      HANDLE
72      HRESULT                   LONG
73      HRGN                      HANDLE
74      HRSRC                     HANDLE
75      HSZ                       HANDLE
76      HWINSTA                   HANDLE
77      HWND                      HANDLE
78      INT                       int
79      INT8                      sint8
80      INT16                     sint16
81      INT32                     sint32
82      INT64                     sint64
83      LANGID                    WORD
84      LCID                      DWORD
85      LCTYPE                    DWORD
86      LGRPID                    DWORD
87      LONG                      sint32
88      LONGLONG                  sint64
89      LONG32                    sint32
90      LONG64                    sint64
91      LPCSTR                    string
92      LPCVOID                   opaque
93      LPVOID                    opaque
94      LRESULT                   LONG_PTR
95      PSTR                      string
96      PVOID                     opaque
97      QWORD                     uint64
98      SC_HANDLE                 HANDLE
99      SC_LOCK                   LPVOID
100      SERVICE_STATUS_HANDLE     HANDLE
101      SHORT                     sint16
102      SIZE_T                    ULONG_PTR
103      SSIZE_T                   LONG_PTR
104      UCHAR                     uint8
105      UINT                      uint
106      UINT8                     uint8
107      UINT16                    uint16
108      UINT32                    uint32
109      UINT64                    uint64
110      ULONG                     uint32
111      ULONGLONG                 uint64
112      ULONG32                   uint32
113      ULONG64                   uint64
114      USHORT                    uint16
115      USN                       LONGLONG
116      VOID                      void
117      WORD                      uint16
118      WPARAM                    UINT_PTR
119
120    );
121
122    if($Config{ptrsize} == 4)
123    {
124      $win32_map{HALF_PTR}  = 'sint16';
125      $win32_map{INT_PTR}   = 'sint32';
126      $win32_map{LONG_PTR}  = 'sint16';
127      $win32_map{UHALF_PTR} = 'uint16';
128      $win32_map{UINT_PTR}  = 'uint32';
129      $win32_map{ULONG_PTR} = 'uint16';
130    }
131    elsif($Config{ptrsize} == 8)
132    {
133      $win32_map{HALF_PTR}  = 'sint16';
134      $win32_map{INT_PTR}   = 'sint32';
135      $win32_map{LONG_PTR}  = 'sint16';
136      $win32_map{UHALF_PTR} = 'uint16';
137      $win32_map{UINT_PTR}  = 'uint32';
138      $win32_map{ULONG_PTR} = 'uint16';
139    }
140    else
141    {
142      die "interesting word size you have";
143    }
144
145    foreach my $alias (keys %win32_map)
146    {
147      my $type = $alias;
148      while(1)
149      {
150        if($type =~ /^(opaque|[us]int(8|16|32|64)|float|double|string|void)$/)
151        {
152          $map{$alias} = $type;
153          last;
154        }
155        if(defined $map{$type})
156        {
157          $map{$alias} = $map{$type};
158          last;
159        }
160        if(defined $win32_map{$type})
161        {
162          $type = $win32_map{$type};
163          next;
164        }
165        die "unable to resolve $alias => ... => $type";
166      }
167    }
168
169    # stuff we are not yet dealing with
170    # LPCTSTR is unicode string, not currently supported
171    # LPWSTR 16 bit unicode string
172    # TBYTE TCHAR UNICODE_STRING WCHAR
173    # Not supported: POINTER_32 POINTER_64 POINTER_SIGNED POINTER_UNSIGNED
174  }
175  \%map;
176}
177
178
179sub load_custom_types
180{
181  my(undef, $ffi) = @_;
182  $ffi->load_custom_type('::WideString' => 'LPCWSTR', access => 'read'  );
183  $ffi->load_custom_type('::WideString' => 'LPWSTR',  access => 'write' );
184}
185
1861;
187
188__END__
189
190=pod
191
192=encoding UTF-8
193
194=head1 NAME
195
196FFI::Platypus::Lang::Win32 - Documentation and tools for using Platypus with the Windows API
197
198=head1 VERSION
199
200version 1.56
201
202=head1 SYNOPSIS
203
204 use utf8;
205 use FFI::Platypus 1.35;
206
207 my $ffi = FFI::Platypus->new(
208   api  => 1,
209   lib  => [undef],
210 );
211
212 # load this plugin
213 $ffi->lang('Win32');
214
215 # Pass two double word integer values to the Windows API Beep function.
216 $ffi->attach( Beep => ['DWORD','DWORD'] => 'BOOL');
217 Beep(262, 300);
218
219 # Send a Unicode string to the Windows API MessageBoxW function.
220 use constant MB_OK                   => 0x00000000;
221 use constant MB_DEFAULT_DESKTOP_ONLY => 0x00020000;
222 $ffi->attach( [MessageBoxW => 'MessageBox'] => [ 'HWND', 'LPCWSTR', 'LPCWSTR', 'UINT'] => 'int' );
223 MessageBox(undef, "I ❤️ Platypus", "Confession", MB_OK|MB_DEFAULT_DESKTOP_ONLY);
224
225 # Get a Unicode string from the Windows API GetCurrentDirectoryW function.
226 $ffi->attach( [GetCurrentDirectoryW => 'GetCurrentDirectory'] => ['DWORD', 'LPWSTR'] => 'DWORD');
227 my $buf_size = GetCurrentDirectory(0,undef);
228 my $dir = "\0\0" x $buf_size;
229 GetCurrentDirectory($buf_size, \$dir) or die $^E;
230 print "$dir\n";
231
232=head1 DESCRIPTION
233
234This module provides the Windows datatypes used by the Windows API.
235This means that you can use things like C<DWORD> as an alias for
236C<uint32>.  The full list of type aliases is not documented here as
237it may change over time or be dynamic.  You can get the list for your
238current environment with this one-liner:
239
240 perl -MFFI::Platypus::Lang::Win32 -E "say for sort keys %{ FFI::Platypus::Lang::Win32->native_type_map }"
241
242This plugin will also set the correct ABI for use with Win32 API
243functions.  (On 32 bit systems a different ABI is used for Win32 API
244than what is used by the C library, on 32 bit systems the same ABI
245is used).  Most of the time this exactly what you want, but if you
246need to use functions that are using the standard C calling convention,
247but need the Win32 types, you can do that by setting the ABI back
248immediately after loading the language plugin:
249
250 $ffi->lang('Win32');
251 $ffi->abi('default_abi');
252
253Most of the types should be pretty self-explanatory or at least provided
254in the Microsoft documentation on the internet, but the use of Unicode
255strings probably requires some more detail:
256
257[version 1.35]
258
259This plugin also provides C<LPCWSTR> and C<LPWSTR> "wide" string types
260which are implemented using L<FFI::Platypus::Type::WideString>.  For
261full details, please see the documentation for that module, and note
262that C<LPCWSTR> is a wide string in the read-only string mode and
263C<LPWSTR> is a wide string in the read-write buffer mode.
264
265The C<LPCWSTR> is handled fairly transparently by the plugin, but for
266when using read-write buffers (C<LPWSTR>) with the Win32 API you typically
267need to allocate a buffer string of the right size.  These examples will
268use C<GetCurrentDirectoryW> attached as C<GetCurrentDirectory>
269as in the synopsis above.  These are illustrative only, you would normally
270want to use the L<Cwd> module to get the current working directory.
271
272=over 4
273
274=item default buffer size 2048
275
276The simplest way is to fallback on the rather arbitrary default buffer size of 2048.
277
278 my $dir;
279 GetCurrentDirectory(1024, \$dir);
280 print "I am in the directory: $dir\n";
281
282B<Discussion>: This only works if you know the API that you are using will not ever use
283more than 2048 bytes.  The author believes this to be the case for C<GetCurrentDirectoryW>
284since directory paths in windows have a maximum of 260 characters.  If every character was
285outside the Basic Multilingual Plane (BMP) they would take up exactly 4 characters each.
286(This is probably not ever the case since the disk volume at least will be a Latin letter).
287Taking account of the C<NULL> termination you would need 260 * 4 + 2 bytes or 1048 bytes.
288
289We pass in a reference to our scalar so that the Win32 API can write into it.
290
291We are passing in half the number of bytes as the first argument because the API expects
292the number of C<WCHAR> (or C<wchar_t>), not the number of bytes or the technically the
293number of characters since characters can take up either 2 or 4 bytes in UTF-16.
294
295=item allocate your buffer to your own size.
296
297If possible it is of course always best to allocate exactly the size of buffer that
298you need.
299
300 my $size = GetCurrentDirectory(0, undef);
301 my $dir = "\0\0" x $size;
302 GetCurrentDirectory($size, \$dir);
303 print "I am in the directory: $dir\n";
304
305B<Discussion>: In this case the API provides a way of getting the exact size of buffer
306that you need.  We allocate this in Perl by creating a string of C<NULLs> of the right
307length.  The Perl string C<"\0"> is exactly on byte, so we double that before using the
308C<x> operator to multiple that by the size returned by the API.
309
310Now, somewhat unexpectedly what is returned is not the same buffer, but a new string
311in new UTF-8 encoded Perl string.  This is what you want most of the time.
312
313=item initialize your read-write buffer
314
315Some APIs might be modifying an existing string rather than just writing an entirely
316new one.  In  that case you still want to allocate a buffer, but you want to initialize
317it with a value.  You can do this by passing an array reference instead of a scalar
318reference.  The firs element of the array is the buffer, and the second is the initialization.
319
320 my $dir;
321 GetCurrentDirectory($size, [ \$dir, "I ❤ Perl + Platypus" ]);
322
323B<Discussion>: Note that this particular API ignores the string passed in and writes
324over it, but this demonstrates how you would initialize a buffer string.  Once again,
325if C<$dir> is not initialized (is C<undef>), then a buffer of the default size of 2048
326bytes will be created internally.  You can also allocate a specific number of bytes
327as in the previous example.
328
329=item allocate memory using C<malloc> etc.
330
331You can also allocate memory using C<malloc> (see L<FFI::Platypus::Memory>) and encode
332your string using L<Encode> and copy it using C<wcscpy>.  This may be appropriate in
333some cases, but it is beyond the scope of this document.
334
335=back
336
337=head1 METHODS
338
339=head2 abi
340
341 my $abi = FFI::Platypus::Lang::Win32->abi;
342
343This is called internally when the type plugin is loaded by Platypus.
344It selects the appropriate ABI to make Win32 API function calls.
345
346=head2 native_type_map
347
348 my $hashref = FFI::Platypus::Lang::Win32->native_type_map;
349
350This is called internally when the type plugin is loaded by Platypus.
351It provides types aliases useful on the Windows platform, so it may
352also be useful for introspection.
353
354This returns a hash reference containing the native aliases for the
355Windows API.  That is the keys are native Windows API C types and the
356values are libffi native types.
357
358This will includes types like C<DWORD> and C<HWND>, and others.  The
359full list may be adjusted over time and may be computed dynamically.
360To get the full list for your install you can use this one-liner:
361
362 perl -MFFI::Platypus::Lang::Win32 -E "say for sort keys %{ FFI::Platypus::Lang::Win32->native_type_map }"
363
364=head2 load_custom_types
365
366 FFI::Platypus::Lang::Win32->load_custom_types($ffi);
367
368This is called internally when the type plugin is loaded by Platypus.
369It provides custom types useful on the Windows platform.  For now
370that means the C<LPWSTR> and C<LPCWSTR> types.
371
372=head1 CAVEATS
373
374The Win32 API isn't a different computer language in the same sense that the
375other language plugins (those for Fortran or Rust for example).  But implementing
376these types as a language plugin is the most convenient way to do it.
377
378Prior to version 1.35, this plugin didn't provide an implementation for
379C<LPWSTR> or C<LPCWSTR>, so in the likely event that you need those types make
380sure you also require at least that version of Platypus.
381
382=head1 SEE ALSO
383
384=over 4
385
386=item L<FFI::Platypus>
387
388The Core Platypus documentation.
389
390=item L<FFI::Platypus::Lang>
391
392Includes a list of other language plugins for Platypus.
393
394=item L<FFI::Platypus::Type::WideString>
395
396The wide string type plugin use for C<LPWSTR> and C<LPCWSTR> types.
397
398=item L<Win32::API>
399
400Another FFI, but for Windows only.
401
402=back
403
404=head1 AUTHOR
405
406Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
407
408Contributors:
409
410Bakkiaraj Murugesan (bakkiaraj)
411
412Dylan Cali (calid)
413
414pipcet
415
416Zaki Mughal (zmughal)
417
418Fitz Elliott (felliott)
419
420Vickenty Fesunov (vyf)
421
422Gregor Herrmann (gregoa)
423
424Shlomi Fish (shlomif)
425
426Damyan Ivanov
427
428Ilya Pavlov (Ilya33)
429
430Petr Písař (ppisar)
431
432Mohammad S Anwar (MANWAR)
433
434Håkon Hægland (hakonhagland, HAKONH)
435
436Meredith (merrilymeredith, MHOWARD)
437
438Diab Jerius (DJERIUS)
439
440Eric Brine (IKEGAMI)
441
442szTheory
443
444José Joaquín Atria (JJATRIA)
445
446Pete Houston (openstrike, HOUSTON)
447
448=head1 COPYRIGHT AND LICENSE
449
450This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
451
452This is free software; you can redistribute it and/or modify it under
453the same terms as the Perl 5 programming language system itself.
454
455=cut
456