kernel32: GlobalMemoryStatusEx: memory usage reported only reflects physical memory.
[wine] / dlls / kernel32 / virtual.c
1 /*
2  * Win32 virtual memory functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "ntstatus.h"
36 #define WIN32_NO_STATUS
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winternl.h"
41 #include "winerror.h"
42 #include "wine/exception.h"
43 #include "excpt.h"
44 #include "wine/debug.h"
45
46 #include "kernel_private.h"
47
48 WINE_DECLARE_DEBUG_CHANNEL(seh);
49
50 static unsigned int page_size;
51
52
53 /***********************************************************************
54  *             VirtualAlloc   (KERNEL32.@)
55  *
56  * Reserves or commits a region of pages in virtual address space.
57  *
58  * PARAMS
59  *  addr    [I] Address of region to reserve or commit.
60  *  size    [I] Size of region.
61  *  type    [I] Type of allocation.
62  *  protect [I] Type of access protection.
63  *
64  * RETURNS
65  *      Success: Base address of allocated region of pages.
66  *      Failure: NULL.
67  */
68 LPVOID WINAPI VirtualAlloc( LPVOID addr, SIZE_T size, DWORD type, DWORD protect )
69 {
70     return VirtualAllocEx( GetCurrentProcess(), addr, size, type, protect );
71 }
72
73
74 /***********************************************************************
75  *             VirtualAllocEx   (KERNEL32.@)
76  *
77  * Seems to be just as VirtualAlloc, but with process handle.
78  *
79  * PARAMS
80  *  hProcess [I] Handle to process to do mem operation.
81  *  addr     [I] Address of region to reserve or commit.
82  *  size     [I] Size of region.
83  *  type     [I] Type of allocation.
84  *  protect  [I] Type of access protection.
85  *
86  *
87  * RETURNS
88  *      Success: Base address of allocated region of pages.
89  *      Failure: NULL.
90  */
91 LPVOID WINAPI VirtualAllocEx( HANDLE hProcess, LPVOID addr, SIZE_T size,
92     DWORD type, DWORD protect )
93 {
94     LPVOID ret = addr;
95     NTSTATUS status;
96
97     if ((status = NtAllocateVirtualMemory( hProcess, &ret, 0, &size, type, protect )))
98     {
99         SetLastError( RtlNtStatusToDosError(status) );
100         ret = NULL;
101     }
102     return ret;
103 }
104
105
106 /***********************************************************************
107  *             VirtualFree   (KERNEL32.@)
108  *
109  * Releases or decommits a region of pages in virtual address space.
110  *
111  * PARAMS
112  *  addr [I] Address of region of committed pages.
113  *  size [I] Size of region.
114  *  type [I] Type of operation.
115  *
116  * RETURNS
117  *      Success: TRUE.
118  *      Failure: FALSE.
119  */
120 BOOL WINAPI VirtualFree( LPVOID addr, SIZE_T size, DWORD type )
121 {
122     return VirtualFreeEx( GetCurrentProcess(), addr, size, type );
123 }
124
125
126 /***********************************************************************
127  *             VirtualFreeEx   (KERNEL32.@)
128  *
129  * Releases or decommits a region of pages in virtual address space.
130  *
131  * PARAMS
132  *  process [I] Handle to process.
133  *  addr    [I] Address of region to free.
134  *  size    [I] Size of region.
135  *  type    [I] Type of operation.
136  *
137  * RETURNS
138  *      Success: TRUE.
139  *      Failure: FALSE.
140  */
141 BOOL WINAPI VirtualFreeEx( HANDLE process, LPVOID addr, SIZE_T size, DWORD type )
142 {
143     NTSTATUS status = NtFreeVirtualMemory( process, &addr, &size, type );
144     if (status) SetLastError( RtlNtStatusToDosError(status) );
145     return !status;
146 }
147
148
149 /***********************************************************************
150  *             VirtualLock   (KERNEL32.@)
151  *
152  * Locks the specified region of virtual address space.
153  *
154  * PARAMS
155  *  addr [I] Address of first byte of range to lock.
156  *  size [I] Number of bytes in range to lock.
157  *
158  * RETURNS
159  *      Success: TRUE.
160  *      Failure: FALSE.
161  *
162  * NOTES
163  *      Always returns TRUE.
164  *
165  */
166 BOOL WINAPI VirtualLock( LPVOID addr, SIZE_T size )
167 {
168     NTSTATUS status = NtLockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 );
169     if (status) SetLastError( RtlNtStatusToDosError(status) );
170     return !status;
171 }
172
173
174 /***********************************************************************
175  *             VirtualUnlock   (KERNEL32.@)
176  *
177  * Unlocks a range of pages in the virtual address space.
178  *
179  * PARAMS
180  *  addr [I] Address of first byte of range.
181  *  size [I] Number of bytes in range.
182  *
183  * RETURNS
184  *      Success: TRUE.
185  *      Failure: FALSE.
186  *
187  * NOTES
188  *      Always returns TRUE.
189  *
190  */
191 BOOL WINAPI VirtualUnlock( LPVOID addr, SIZE_T size )
192 {
193     NTSTATUS status = NtUnlockVirtualMemory( GetCurrentProcess(), &addr, &size, 1 );
194     if (status) SetLastError( RtlNtStatusToDosError(status) );
195     return !status;
196 }
197
198
199 /***********************************************************************
200  *             VirtualProtect   (KERNEL32.@)
201  *
202  * Changes the access protection on a region of committed pages.
203  *
204  * PARAMS
205  *  addr     [I] Address of region of committed pages.
206  *  size     [I] Size of region.
207  *  new_prot [I] Desired access protection.
208  *  old_prot [O] Address of variable to get old protection.
209  *
210  * RETURNS
211  *      Success: TRUE.
212  *      Failure: FALSE.
213  */
214 BOOL WINAPI VirtualProtect( LPVOID addr, SIZE_T size, DWORD new_prot, LPDWORD old_prot)
215 {
216     return VirtualProtectEx( GetCurrentProcess(), addr, size, new_prot, old_prot );
217 }
218
219
220 /***********************************************************************
221  *             VirtualProtectEx   (KERNEL32.@)
222  *
223  * Changes the access protection on a region of committed pages in the
224  * virtual address space of a specified process.
225  *
226  * PARAMS
227  *  process  [I] Handle of process.
228  *  addr     [I] Address of region of committed pages.
229  *  size     [I] Size of region.
230  *  new_prot [I] Desired access protection.
231  *  old_prot [O] Address of variable to get old protection.
232  *
233  * RETURNS
234  *      Success: TRUE.
235  *      Failure: FALSE.
236  */
237 BOOL WINAPI VirtualProtectEx( HANDLE process, LPVOID addr, SIZE_T size,
238     DWORD new_prot, LPDWORD old_prot )
239 {
240     NTSTATUS status = NtProtectVirtualMemory( process, &addr, &size, new_prot, old_prot );
241     if (status) SetLastError( RtlNtStatusToDosError(status) );
242     return !status;
243 }
244
245
246 /***********************************************************************
247  *             VirtualQuery   (KERNEL32.@)
248  *
249  * Provides info about a range of pages in virtual address space.
250  *
251  * PARAMS
252  *  addr [I] Address of region.
253  *  info [O] Address of info buffer.
254  *  len  [I] Size of buffer.
255  *
256  * RETURNS
257  *      Number of bytes returned in information buffer or 0 if
258  *      addr >= 0xc0000000 (kernel space).
259  */
260 SIZE_T WINAPI VirtualQuery( LPCVOID addr, PMEMORY_BASIC_INFORMATION info,
261     SIZE_T len )
262 {
263     return VirtualQueryEx( GetCurrentProcess(), addr, info, len );
264 }
265
266
267 /***********************************************************************
268  *             VirtualQueryEx   (KERNEL32.@)
269  *
270  * Provides info about a range of pages in virtual address space of a
271  * specified process.
272  *
273  * PARAMS
274  *  process [I] Handle to process.
275  *  addr    [I] Address of region.
276  *  info    [O] Address of info buffer.
277  *  len     [I] Size of buffer.
278  *
279  * RETURNS
280  *      Number of bytes returned in information buffer.
281  */
282 SIZE_T WINAPI VirtualQueryEx( HANDLE process, LPCVOID addr,
283     PMEMORY_BASIC_INFORMATION info, SIZE_T len )
284 {
285     SIZE_T ret;
286     NTSTATUS status;
287
288     if ((status = NtQueryVirtualMemory( process, addr, MemoryBasicInformation, info, len, &ret )))
289     {
290         SetLastError( RtlNtStatusToDosError(status) );
291         ret = 0;
292     }
293     return ret;
294 }
295
296
297 /***********************************************************************
298  *             CreateFileMappingA   (KERNEL32.@)
299  *
300  * Creates a named or unnamed file-mapping object for the specified file.
301  *
302  * PARAMS
303  *  hFile     [I] Handle to the file to map.
304  *  sa        [I] Optional security attributes.
305  *  protect   [I] Protection for mapping object.
306  *  size_high [I] High-order 32 bits of object size.
307  *  size_low  [I] Low-order 32 bits of object size.
308  *  name      [I] Name of file-mapping object.
309  *
310  * RETURNS
311  *      Success: Handle.
312  *      Failure: NULL. Mapping object does not exist.
313  */
314 HANDLE WINAPI CreateFileMappingA( HANDLE hFile, SECURITY_ATTRIBUTES *sa,
315     DWORD protect, DWORD size_high, DWORD size_low, LPCSTR name )
316 {
317     WCHAR buffer[MAX_PATH];
318
319     if (!name) return CreateFileMappingW( hFile, sa, protect, size_high, size_low, NULL );
320
321     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
322     {
323         SetLastError( ERROR_FILENAME_EXCED_RANGE );
324         return 0;
325     }
326     return CreateFileMappingW( hFile, sa, protect, size_high, size_low, buffer );
327 }
328
329
330 /***********************************************************************
331  *             CreateFileMappingW   (KERNEL32.@)
332  *
333  * See CreateFileMappingA.
334  */
335 HANDLE WINAPI CreateFileMappingW( HANDLE hFile, LPSECURITY_ATTRIBUTES sa,
336                                   DWORD protect, DWORD size_high,
337                                   DWORD size_low, LPCWSTR name )
338 {
339     static const int sec_flags = SEC_FILE | SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_NOCACHE;
340
341     HANDLE ret;
342     OBJECT_ATTRIBUTES attr;
343     UNICODE_STRING nameW;
344     NTSTATUS status;
345     DWORD access, sec_type;
346     LARGE_INTEGER size;
347
348     attr.Length                   = sizeof(attr);
349     attr.RootDirectory            = 0;
350     attr.ObjectName               = NULL;
351     attr.Attributes               = OBJ_CASE_INSENSITIVE | OBJ_OPENIF |
352                                     ((sa && sa->bInheritHandle) ? OBJ_INHERIT : 0);
353     attr.SecurityDescriptor       = sa ? sa->lpSecurityDescriptor : NULL;
354     attr.SecurityQualityOfService = NULL;
355
356     if (name)
357     {
358         RtlInitUnicodeString( &nameW, name );
359         attr.ObjectName = &nameW;
360         attr.RootDirectory = get_BaseNamedObjects_handle();
361     }
362
363     sec_type = protect & sec_flags;
364     protect &= ~sec_flags;
365     if (!sec_type) sec_type = SEC_COMMIT;
366
367     switch(protect)
368     {
369     case 0:
370         protect = PAGE_READONLY;  /* Win9x compatibility */
371         /* fall through */
372     case PAGE_READONLY:
373     case PAGE_WRITECOPY:
374         access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ;
375         break;
376     case PAGE_READWRITE:
377         access = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE;
378         break;
379     default:
380         SetLastError( ERROR_INVALID_PARAMETER );
381         return 0;
382     }
383
384     if (hFile == INVALID_HANDLE_VALUE)
385     {
386         hFile = 0;
387         if (!size_low && !size_high)
388         {
389             SetLastError( ERROR_INVALID_PARAMETER );
390             return 0;
391         }
392     }
393
394     size.u.LowPart  = size_low;
395     size.u.HighPart = size_high;
396
397     status = NtCreateSection( &ret, access, &attr, &size, protect, sec_type, hFile );
398     if (status == STATUS_OBJECT_NAME_EXISTS)
399         SetLastError( ERROR_ALREADY_EXISTS );
400     else
401         SetLastError( RtlNtStatusToDosError(status) );
402     return ret;
403 }
404
405
406 /***********************************************************************
407  *             OpenFileMappingA   (KERNEL32.@)
408  *
409  * Opens a named file-mapping object.
410  *
411  * PARAMS
412  *  access  [I] Access mode.
413  *  inherit [I] Inherit flag.
414  *  name    [I] Name of file-mapping object.
415  *
416  * RETURNS
417  *      Success: Handle.
418  *      Failure: NULL.
419  */
420 HANDLE WINAPI OpenFileMappingA( DWORD access, BOOL inherit, LPCSTR name )
421 {
422     WCHAR buffer[MAX_PATH];
423
424     if (!name) return OpenFileMappingW( access, inherit, NULL );
425
426     if (!MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, MAX_PATH ))
427     {
428         SetLastError( ERROR_FILENAME_EXCED_RANGE );
429         return 0;
430     }
431     return OpenFileMappingW( access, inherit, buffer );
432 }
433
434
435 /***********************************************************************
436  *             OpenFileMappingW   (KERNEL32.@)
437  *
438  * See OpenFileMappingA.
439  */
440 HANDLE WINAPI OpenFileMappingW( DWORD access, BOOL inherit, LPCWSTR name)
441 {
442     OBJECT_ATTRIBUTES attr;
443     UNICODE_STRING nameW;
444     HANDLE ret;
445     NTSTATUS status;
446
447     if (!name)
448     {
449         SetLastError( ERROR_INVALID_PARAMETER );
450         return 0;
451     }
452     attr.Length = sizeof(attr);
453     attr.RootDirectory = get_BaseNamedObjects_handle();
454     attr.ObjectName = &nameW;
455     attr.Attributes = OBJ_CASE_INSENSITIVE | (inherit ? OBJ_INHERIT : 0);
456     attr.SecurityDescriptor = NULL;
457     attr.SecurityQualityOfService = NULL;
458     RtlInitUnicodeString( &nameW, name );
459
460     if (access == FILE_MAP_COPY) access = FILE_MAP_READ;
461
462     if ((status = NtOpenSection( &ret, access, &attr )))
463     {
464         SetLastError( RtlNtStatusToDosError(status) );
465         ret = 0;
466     }
467     return ret;
468 }
469
470
471 /***********************************************************************
472  *             MapViewOfFile   (KERNEL32.@)
473  *
474  * Maps a view of a file into the address space.
475  *
476  * PARAMS
477  *  mapping     [I] File-mapping object to map.
478  *  access      [I] Access mode.
479  *  offset_high [I] High-order 32 bits of file offset.
480  *  offset_low  [I] Low-order 32 bits of file offset.
481  *  count       [I] Number of bytes to map.
482  *
483  * RETURNS
484  *      Success: Starting address of mapped view.
485  *      Failure: NULL.
486  */
487 LPVOID WINAPI MapViewOfFile( HANDLE mapping, DWORD access,
488     DWORD offset_high, DWORD offset_low, SIZE_T count )
489 {
490     return MapViewOfFileEx( mapping, access, offset_high,
491                             offset_low, count, NULL );
492 }
493
494
495 /***********************************************************************
496  *             MapViewOfFileEx   (KERNEL32.@)
497  *
498  * Maps a view of a file into the address space.
499  *
500  * PARAMS
501  *  handle      [I] File-mapping object to map.
502  *  access      [I] Access mode.
503  *  offset_high [I] High-order 32 bits of file offset.
504  *  offset_low  [I] Low-order 32 bits of file offset.
505  *  count       [I] Number of bytes to map.
506  *  addr        [I] Suggested starting address for mapped view.
507  *
508  * RETURNS
509  *      Success: Starting address of mapped view.
510  *      Failure: NULL.
511  */
512 LPVOID WINAPI MapViewOfFileEx( HANDLE handle, DWORD access,
513     DWORD offset_high, DWORD offset_low, SIZE_T count, LPVOID addr )
514 {
515     NTSTATUS status;
516     LARGE_INTEGER offset;
517     ULONG protect;
518
519     offset.u.LowPart  = offset_low;
520     offset.u.HighPart = offset_high;
521
522     if (access & FILE_MAP_WRITE) protect = PAGE_READWRITE;
523     else if (access & FILE_MAP_READ) protect = PAGE_READONLY;
524     else if (access & FILE_MAP_COPY) protect = PAGE_WRITECOPY;
525     else protect = PAGE_NOACCESS;
526
527     if ((status = NtMapViewOfSection( handle, GetCurrentProcess(), &addr, 0, 0, &offset,
528                                       &count, ViewShare, 0, protect )))
529     {
530         SetLastError( RtlNtStatusToDosError(status) );
531         addr = NULL;
532     }
533     return addr;
534 }
535
536
537 /***********************************************************************
538  *             UnmapViewOfFile   (KERNEL32.@)
539  *
540  * Unmaps a mapped view of a file.
541  *
542  * PARAMS
543  *  addr [I] Address where mapped view begins.
544  *
545  * RETURNS
546  *      Success: TRUE.
547  *      Failure: FALSE.
548  *
549  */
550 BOOL WINAPI UnmapViewOfFile( LPCVOID addr )
551 {
552     NTSTATUS status = NtUnmapViewOfSection( GetCurrentProcess(), (void *)addr );
553     if (status) SetLastError( RtlNtStatusToDosError(status) );
554     return !status;
555 }
556
557
558 /***********************************************************************
559  *             FlushViewOfFile   (KERNEL32.@)
560  *
561  * Writes to the disk a byte range within a mapped view of a file.
562  *
563  * PARAMS
564  *  base [I] Start address of byte range to flush.
565  *  size [I] Number of bytes in range.
566  *
567  * RETURNS
568  *      Success: TRUE.
569  *      Failure: FALSE.
570  */
571 BOOL WINAPI FlushViewOfFile( LPCVOID base, SIZE_T size )
572 {
573     NTSTATUS status = NtFlushVirtualMemory( GetCurrentProcess(), &base, &size, 0 );
574     if (status)
575     {
576         if (status == STATUS_NOT_MAPPED_DATA) status = STATUS_SUCCESS;
577         else SetLastError( RtlNtStatusToDosError(status) );
578     }
579     return !status;
580 }
581
582
583 /***********************************************************************
584  *             IsBadReadPtr   (KERNEL32.@)
585  *
586  * Check for read access on a memory block.
587  *
588  * ptr  [I] Address of memory block.
589  * size [I] Size of block.
590  *
591  * RETURNS
592  *  Success: TRUE.
593  *      Failure: FALSE. Process has read access to entire block.
594  */
595 BOOL WINAPI IsBadReadPtr( LPCVOID ptr, UINT size )
596 {
597     if (!size) return FALSE;  /* handle 0 size case w/o reference */
598     if (!ptr) return TRUE;
599     
600     if (!page_size) page_size = getpagesize();
601     __TRY
602     {
603         volatile const char *p = ptr;
604         char dummy;
605         UINT count = size;
606
607         while (count > page_size)
608         {
609             dummy = *p;
610             p += page_size;
611             count -= page_size;
612         }
613         dummy = p[0];
614         dummy = p[count - 1];
615     }
616     __EXCEPT_PAGE_FAULT
617     {
618         TRACE_(seh)("%p caused page fault during read\n", ptr);
619         return TRUE;
620     }
621     __ENDTRY
622     return FALSE;
623 }
624
625
626 /***********************************************************************
627  *             IsBadWritePtr   (KERNEL32.@)
628  *
629  * Check for write access on a memory block.
630  *
631  * PARAMS
632  *  ptr  [I] Address of memory block.
633  *  size [I] Size of block in bytes.
634  *
635  * RETURNS
636  *  Success: TRUE.
637  *      Failure: FALSE. Process has write access to entire block.
638  */
639 BOOL WINAPI IsBadWritePtr( LPVOID ptr, UINT size )
640 {
641     if (!size) return FALSE;  /* handle 0 size case w/o reference */
642     if (!ptr) return TRUE;
643     
644     if (!page_size) page_size = getpagesize();
645     __TRY
646     {
647         volatile char *p = ptr;
648         UINT count = size;
649
650         while (count > page_size)
651         {
652             *p |= 0;
653             p += page_size;
654             count -= page_size;
655         }
656         p[0] |= 0;
657         p[count - 1] |= 0;
658     }
659     __EXCEPT_PAGE_FAULT
660     {
661         TRACE_(seh)("%p caused page fault during write\n", ptr);
662         return TRUE;
663     }
664     __ENDTRY
665     return FALSE;
666 }
667
668
669 /***********************************************************************
670  *             IsBadHugeReadPtr   (KERNEL32.@)
671  *
672  * Check for read access on a memory block.
673  *
674  * PARAMS
675  *  ptr  [I] Address of memory block.
676  *  size [I] Size of block.
677  *
678  * RETURNS
679  *  Success: TRUE.
680  *      Failure: FALSE. Process has read access to entire block.
681  */
682 BOOL WINAPI IsBadHugeReadPtr( LPCVOID ptr, UINT size )
683 {
684     return IsBadReadPtr( ptr, size );
685 }
686
687
688 /***********************************************************************
689  *             IsBadHugeWritePtr   (KERNEL32.@)
690  *
691  * Check for write access on a memory block.
692  *
693  * PARAMS
694  *  ptr  [I] Address of memory block.
695  *  size [I] Size of block.
696  *
697  * RETURNS
698  *  Success: TRUE.
699  *      Failure: FALSE. Process has write access to entire block.
700  */
701 BOOL WINAPI IsBadHugeWritePtr( LPVOID ptr, UINT size )
702 {
703     return IsBadWritePtr( ptr, size );
704 }
705
706
707 /***********************************************************************
708  *             IsBadCodePtr   (KERNEL32.@)
709  *
710  * Check for read access on a memory address.
711  *
712  * PARAMS
713  *  ptr [I] Address of function.
714  *
715  * RETURNS
716  *      Success: TRUE.
717  *      Failure: FALSE. Process has read access to specified memory.
718  */
719 BOOL WINAPI IsBadCodePtr( FARPROC ptr )
720 {
721     return IsBadReadPtr( ptr, 1 );
722 }
723
724
725 /***********************************************************************
726  *             IsBadStringPtrA   (KERNEL32.@)
727  *
728  * Check for read access on a range of memory pointed to by a string pointer.
729  *
730  * PARAMS
731  *  str [I] Address of string.
732  *  max [I] Maximum size of string.
733  *
734  * RETURNS
735  *      Success: TRUE.
736  *      Failure: FALSE. Read access to all bytes in string.
737  */
738 BOOL WINAPI IsBadStringPtrA( LPCSTR str, UINT max )
739 {
740     if (!str) return TRUE;
741     
742     __TRY
743     {
744         volatile const char *p = str;
745         while (p != str + max) if (!*p++) break;
746     }
747     __EXCEPT_PAGE_FAULT
748     {
749         TRACE_(seh)("%p caused page fault during read\n", str);
750         return TRUE;
751     }
752     __ENDTRY
753     return FALSE;
754 }
755
756
757 /***********************************************************************
758  *             IsBadStringPtrW   (KERNEL32.@)
759  *
760  * See IsBadStringPtrA.
761  */
762 BOOL WINAPI IsBadStringPtrW( LPCWSTR str, UINT max )
763 {
764     if (!str) return TRUE;
765     
766     __TRY
767     {
768         volatile const WCHAR *p = str;
769         while (p != str + max) if (!*p++) break;
770     }
771     __EXCEPT_PAGE_FAULT
772     {
773         TRACE_(seh)("%p caused page fault during read\n", str);
774         return TRUE;
775     }
776     __ENDTRY
777     return FALSE;
778 }