ReactOS 0.4.16-dev-2491-g3dc6630
pager.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Console Utilities Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Console/terminal paging functionality.
5 * COPYRIGHT: Copyright 2017-2021 Hermes Belusca-Maito
6 * Copyright 2021 Katayama Hirofumi MZ
7 */
8
16/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
17#define UNICODE
18#define _UNICODE
19
20#include <windef.h>
21#include <winbase.h>
22#include <wincon.h> // Console APIs (only if kernel32 support included)
23#include <winnls.h> // For WideCharToMultiByte
24#include <strsafe.h>
25
26#include "conutils.h"
27#include "stream.h"
28#include "screen.h"
29#include "pager.h"
30
31// Temporary HACK
32#define CON_STREAM_WRITE ConStreamWrite
33
34#define CP_SHIFTJIS 932 // Japanese Shift-JIS
35#define CP_HANGUL 949 // Korean Hangul/Wansung
36#define CP_JOHAB 1361 // Korean Johab
37#define CP_GB2312 936 // Chinese Simplified (GB2312)
38#define CP_BIG5 950 // Chinese Traditional (Big5)
39
40/* IsFarEastCP(CodePage) */
41#define IsCJKCodePage(CodePage) \
42 ((CodePage) == CP_SHIFTJIS || (CodePage) == CP_HANGUL || \
43 /* (CodePage) == CP_JOHAB || */ \
44 (CodePage) == CP_BIG5 || (CodePage) == CP_GB2312)
45
46static inline INT
48 IN UINT nCodePage,
49 IN WCHAR ch)
50{
51 INT ret = WideCharToMultiByte(nCodePage, 0, &ch, 1, NULL, 0, NULL, NULL);
52 if (ret == 0)
53 ret = 1;
54 else if (ret > 2)
55 ret = 2;
56 return ret;
57}
58
66static BOOL
68 IN OUT PCON_PAGER Pager,
69 IN PCTCH TextBuff,
71{
72 SIZE_T ich = Pager->ich;
73 SIZE_T ichStart;
74 SIZE_T cchLine;
75 BOOL bCacheLine;
76
77 Pager->ichCurr = 0;
78 Pager->iEndLine = 0;
79
80 /*
81 * If we already had an existing line, then we can safely start a new one
82 * and getting rid of any current cached line. Otherwise, we don't have
83 * a current line and we may be caching a new one, in which case, continue
84 * caching it until it becomes complete.
85 */
86 // INVESTIGATE: Do that only if (ichStart >= iEndLine) ??
87 if (Pager->CurrentLine)
88 {
89 // ASSERT(Pager->CurrentLine == Pager->CachedLine);
90 if (Pager->CachedLine)
91 {
92 HeapFree(GetProcessHeap(), 0, (PVOID)Pager->CachedLine);
93 Pager->CachedLine = NULL;
94 Pager->cchCachedLine = 0;
95 }
96
97 Pager->CurrentLine = NULL;
98 }
99
100 /* Nothing else to read if we are past the end of the buffer */
101 if (ich >= cch)
102 {
103 /* If we have a pending cached line, terminate it now */
104 if (Pager->CachedLine)
105 goto TerminateLine;
106
107 /* Otherwise, bail out */
108 return FALSE;
109 }
110
111 /* Start a new line, or continue an existing one */
112 ichStart = ich;
113
114 /* Find where this line ends, looking for a NEWLINE character.
115 * (NOTE: We cannot use strchr because the buffer is not NULL-terminated) */
116 for (; ich < cch; ++ich)
117 {
118 if (TextBuff[ich] == TEXT('\n'))
119 {
120 ++ich;
121 break;
122 }
123 }
124 Pager->ich = ich;
125
126 cchLine = (ich - ichStart);
127
128 //
129 // FIXME: Impose a maximum string limit when the line is cached, in order
130 // not to potentially grow memory indefinitely. When the limit is reached,
131 // terminate the line.
132 //
133
134 /*
135 * If we have stopped because we have exhausted the text buffer
136 * and we have not found an end-of-line character, this may mean
137 * that the text line spans across different text buffers. If we
138 * have been told so, cache this line: we will complete it during
139 * the next call(s) and only then, display it.
140 * Otherwise, consider the line to be terminated now.
141 */
142 bCacheLine = ((Pager->dwFlags & CON_PAGER_CACHE_INCOMPLETE_LINE) &&
143 (ich >= cch) && (TextBuff[ich - 1] != TEXT('\n')));
144
145 /* Allocate, or re-allocate, the cached line buffer */
146 if (bCacheLine && !Pager->CachedLine)
147 {
148 /* We start caching, allocate the cached line buffer */
149 Pager->CachedLine = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
150 cchLine * sizeof(TCHAR));
151 Pager->cchCachedLine = 0;
152
153 if (!Pager->CachedLine)
154 {
156 return FALSE;
157 }
158 }
159 else if (Pager->CachedLine)
160 {
161 /* We continue caching, re-allocate the cached line buffer */
163 (PVOID)Pager->CachedLine,
164 (Pager->cchCachedLine + cchLine) * sizeof(TCHAR));
165 if (!ptr)
166 {
167 HeapFree(GetProcessHeap(), 0, (PVOID)Pager->CachedLine);
168 Pager->CachedLine = NULL;
169 Pager->cchCachedLine = 0;
170
172 return FALSE;
173 }
174 Pager->CachedLine = ptr;
175 }
176 if (Pager->CachedLine)
177 {
178 /* Copy/append the text to the cached line buffer */
179 RtlCopyMemory((PVOID)&Pager->CachedLine[Pager->cchCachedLine],
180 &TextBuff[ichStart],
181 cchLine * sizeof(TCHAR));
182 Pager->cchCachedLine += cchLine;
183 }
184 if (bCacheLine)
185 {
186 /* The line is currently incomplete, don't proceed further for now */
187 return FALSE;
188 }
189
190TerminateLine:
191 /* The line should be complete now. If we have an existing cached line,
192 * it has been completed by appending the remaining text to it. */
193
194 /* We are starting a new line */
195 Pager->ichCurr = 0;
196 if (Pager->CachedLine)
197 {
198 Pager->iEndLine = Pager->cchCachedLine;
199 Pager->CurrentLine = Pager->CachedLine;
200 }
201 else
202 {
203 Pager->iEndLine = cchLine;
204 Pager->CurrentLine = &TextBuff[ichStart];
205 }
206
207 /* Increase only when we have got a NEWLINE */
208 if ((Pager->iEndLine > 0) && (Pager->CurrentLine[Pager->iEndLine - 1] == TEXT('\n')))
209 Pager->lineno++;
210
211 return TRUE;
212}
213
217static BOOL
219 IN PCON_PAGER Pager,
220 IN PCTCH TextBuff,
221 IN SIZE_T cch)
222{
223 const DWORD PageColumns = Pager->PageColumns;
224 const DWORD ScrollRows = Pager->ScrollRows;
225
226 BOOL bFinitePaging = ((PageColumns > 0) && (Pager->PageRows > 0));
227 LONG nTabWidth = Pager->nTabWidth;
228
229 PCTCH Line;
230 SIZE_T ich;
231 SIZE_T ichStart;
232 SIZE_T iEndLine;
233 DWORD iColumn = Pager->iColumn;
234
235 UINT nCodePage = GetConsoleOutputCP();
236 BOOL IsCJK = IsCJKCodePage(nCodePage);
237 UINT nWidthOfChar = 1;
238 BOOL IsDoubleWidthCharTrailing = FALSE;
239
240 /* Normalize the tab width: if negative or too large,
241 * cap it to the number of columns. */
242 if (PageColumns > 0) // if (bFinitePaging)
243 {
244 if (nTabWidth < 0)
245 nTabWidth = PageColumns - 1;
246 else
247 nTabWidth = min(nTabWidth, PageColumns - 1);
248 }
249 else
250 {
251 /* If no column width is known, default to 8 spaces if the
252 * original value is negative; otherwise keep the current one. */
253 if (nTabWidth < 0)
254 nTabWidth = 8;
255 }
256
257
258 /* Continue displaying the previous line, if any, or start a new one */
259 Line = Pager->CurrentLine;
260 ichStart = Pager->ichCurr;
261 iEndLine = Pager->iEndLine;
262
263ProcessLine:
264
265 /* Stop now if we have displayed more page lines than requested */
266 if (bFinitePaging && (Pager->iLine >= ScrollRows))
267 goto End;
268
269 if (!Line || (ichStart >= iEndLine))
270 {
271 /* Start a new line */
272 if (!GetNextLine(Pager, TextBuff, cch))
273 goto End;
274
275 Line = Pager->CurrentLine;
276 ichStart = Pager->ichCurr;
277 iEndLine = Pager->iEndLine;
278 }
279 else
280 {
281 /* Continue displaying the current line */
282 }
283
284 // ASSERT(Line && ((ichStart < iEndLine) || (ichStart == iEndLine && iEndLine == 0)));
285
286 /* Determine whether this line segment (from the current position till the end) should be displayed */
287 Pager->iColumn = iColumn;
288 if (Pager->PagerLine && Pager->PagerLine(Pager, &Line[ichStart], iEndLine - ichStart))
289 {
290 iColumn = Pager->iColumn;
291
292 /* Done with this line; start a new one */
293 Pager->nSpacePending = 0; // And reset any pending space.
294 ichStart = iEndLine;
295 goto ProcessLine;
296 }
297 // else: Continue displaying the line.
298
299
300 /* Print out any pending TAB expansion */
301 if (Pager->nSpacePending > 0)
302 {
304 while (Pager->nSpacePending > 0)
305 {
306 /* Print filling spaces */
307 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
308 --(Pager->nSpacePending);
309 ++iColumn;
310
311 /* Check whether we are going across the column */
312 if ((PageColumns > 0) && (iColumn % PageColumns == 0))
313 {
314 // Pager->nSpacePending = 0; // <-- This is the mode of most text editors...
315
316 /* Reposition the cursor to the next line, first column */
317 if (!bFinitePaging || (PageColumns < Pager->Screen->csbi.dwSize.X))
318 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
319
320 Pager->iLine++;
321
322 /* Restart at the character */
323 // ASSERT(ichStart == ich);
324 goto ProcessLine;
325 }
326 }
327 }
328
329
330 /* Find, within this line segment (starting from its
331 * beginning), until where we can print to the page. */
332 for (ich = ichStart; ich < iEndLine; ++ich)
333 {
334 /* NEWLINE character */
335 if (Line[ich] == TEXT('\n'))
336 {
337 /* We should stop now */
338 // ASSERT(ich == iEndLine - 1);
339 break;
340 }
341
342 /* TAB character */
343 if (Line[ich] == TEXT('\t') &&
344 (Pager->dwFlags & CON_PAGER_EXPAND_TABS))
345 {
346 /* We should stop now */
347 break;
348 }
349
350 /* FORM-FEED character */
351 if (Line[ich] == TEXT('\f') &&
352 (Pager->dwFlags & CON_PAGER_EXPAND_FF))
353 {
354 /* We should stop now */
355 break;
356 }
357
358 /* Other character - Handle double-width for CJK */
359
360 if (IsCJK)
361 nWidthOfChar = GetWidthOfCharCJK(nCodePage, Line[ich]);
362
363 /* Care about CJK character presentation only when outputting
364 * to a device where the number of columns is known. */
365 if ((PageColumns > 0) && IsCJK)
366 {
367 IsDoubleWidthCharTrailing = (nWidthOfChar == 2) &&
368 ((iColumn + 1) % PageColumns == 0);
369 if (IsDoubleWidthCharTrailing)
370 {
371 /* Reserve this character for the next line */
372 ++iColumn; // Count a blank instead.
373 /* We should stop now */
374 break;
375 }
376 }
377
378 iColumn += nWidthOfChar;
379
380 /* Check whether we are going across the column */
381 if ((PageColumns > 0) && (iColumn % PageColumns == 0))
382 {
383 ++ich;
384 break;
385 }
386 }
387
388 /* Output the pending line segment */
389 if (ich - ichStart > 0)
390 CON_STREAM_WRITE(Pager->Screen->Stream, &Line[ichStart], ich - ichStart);
391
392 /* Have we finished the line segment? */
393 if (ich >= iEndLine)
394 {
395 /* Restart at the character */
396 ichStart = ich;
397 goto ProcessLine;
398 }
399
400 /* Handle special characters */
401
402 /* NEWLINE character */
403 if (Line[ich] == TEXT('\n'))
404 {
405 // ASSERT(ich == iEndLine - 1);
406
407 /* Reposition the cursor to the next line, first column */
408 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
409
410 Pager->iLine++;
411 iColumn = 0;
412
413 /* Done with this line; start a new one */
414 Pager->nSpacePending = 0; // And reset any pending space.
415 ichStart = iEndLine;
416 goto ProcessLine;
417 }
418
419 /* TAB character */
420 if (Line[ich] == TEXT('\t') &&
421 (Pager->dwFlags & CON_PAGER_EXPAND_TABS))
422 {
423 /* Perform TAB expansion, unless the tab width is zero */
424 if (nTabWidth == 0)
425 {
426 ichStart = ++ich;
427 goto ProcessLine;
428 }
429
430 ichStart = ++ich;
431 /* Reset the number of spaces needed to develop this TAB character */
432 Pager->nSpacePending = nTabWidth - (iColumn % nTabWidth);
433 goto ExpandTab;
434 }
435
436 /* FORM-FEED character */
437 if (Line[ich] == TEXT('\f') &&
438 (Pager->dwFlags & CON_PAGER_EXPAND_FF))
439 {
440 if (bFinitePaging)
441 {
442 /* Clear until the end of the page */
443 while (Pager->iLine < ScrollRows)
444 {
445 /* Call the user paging function in order to know
446 * whether we need to output the blank lines. */
447 Pager->iColumn = iColumn;
448 if (Pager->PagerLine && Pager->PagerLine(Pager, TEXT("\n"), 1))
449 {
450 /* Only one blank line displayed, that counts in the line count */
451 Pager->iLine++;
452 break;
453 }
454 else
455 {
456 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
457 Pager->iLine++;
458 }
459 }
460 }
461 else
462 {
463 /* Just output a FORM-FEED and a NEWLINE */
464 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\f\n"), 2);
465 Pager->iLine++;
466 }
467
468 iColumn = 0;
469 Pager->nSpacePending = 0; // And reset any pending space.
470
471 /* Skip and restart past the character */
472 ichStart = ++ich;
473 goto ProcessLine;
474 }
475
476 /* If we output a double-width character that goes across the column,
477 * fill with blank and display the character on the next line. */
478 if (IsDoubleWidthCharTrailing)
479 {
480 IsDoubleWidthCharTrailing = FALSE; // Reset the flag.
481 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT(" "), 1);
482 /* Fall back below */
483 }
484
485 /* Are we wrapping the line? */
486 if ((PageColumns > 0) && (iColumn % PageColumns == 0))
487 {
488 /* Reposition the cursor to the next line, first column */
489 if (!bFinitePaging || (PageColumns < Pager->Screen->csbi.dwSize.X))
490 CON_STREAM_WRITE(Pager->Screen->Stream, TEXT("\n"), 1);
491
492 Pager->iLine++;
493 }
494
495 /* Restart at the character */
496 ichStart = ich;
497 goto ProcessLine;
498
499
500End:
501 /*
502 * We are exiting, either because we displayed all the required lines
503 * (iLine >= ScrollRows), or, because we don't have more data to display.
504 */
505
506 Pager->ichCurr = ichStart;
507 Pager->iColumn = iColumn;
508 // INVESTIGATE: Can we get rid of CurrentLine here? // if (ichStart >= iEndLine) ...
509
510 /* Return TRUE if we displayed all the required lines; FALSE otherwise */
511 if (bFinitePaging && (Pager->iLine >= ScrollRows))
512 {
513 Pager->iLine = 0; /* Reset the count of lines being printed */
514 return TRUE;
515 }
516 else
517 {
518 return FALSE;
519 }
520}
521
522
547BOOL
549 IN PCON_PAGER Pager,
551 IN BOOL StartPaging,
552 IN PCTCH szStr,
553 IN SIZE_T len)
554{
556 BOOL bIsConsole;
557
558 /* Parameters validation */
559 if (!Pager)
560 return FALSE;
561
562 /* Get the size of the visual screen that can be printed to */
563 bIsConsole = ConGetScreenInfo(Pager->Screen, &csbi);
564 if (bIsConsole)
565 {
566 /* Calculate the console screen extent */
567 Pager->PageColumns = csbi.dwSize.X;
568 Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
569 }
570 else
571 {
572 /* We assume it's a file handle */
573 Pager->PageColumns = 0;
574 Pager->PageRows = 0;
575 }
576
577 if (StartPaging)
578 {
579 if (bIsConsole && (Pager->PageRows >= 2))
580 {
581 /* Reset to display one page by default */
582 Pager->ScrollRows = Pager->PageRows - 1;
583 }
584 else
585 {
586 /* File output, or single line: all lines are displayed at once; reset to a default value */
587 Pager->ScrollRows = 0;
588 }
589
590 /* Reset the internal data buffer */
591 Pager->CachedLine = NULL;
592 Pager->cchCachedLine = 0;
593
594 /* Reset the paging state */
595 Pager->CurrentLine = NULL;
596 Pager->ichCurr = 0;
597 Pager->iEndLine = 0;
598 Pager->nSpacePending = 0;
599 Pager->iColumn = 0;
600 Pager->iLine = 0;
601 Pager->lineno = 0;
602 }
603
604 /* Reset the reading index in the user-provided source buffer */
605 Pager->ich = 0;
606
607 /* Run the pager even when the user-provided source buffer is
608 * empty, in case we need to flush any remaining cached line. */
609 if (!Pager->CachedLine)
610 {
611 /* No cached line, bail out now */
612 if (len == 0 || szStr == NULL)
613 return TRUE;
614 }
615
616 while (ConPagerWorker(Pager, szStr, len))
617 {
618 /* Prompt the user only when we display to a console and the screen
619 * is not too small: at least one line for the actual paged text and
620 * one line for the prompt. */
621 if (bIsConsole && (Pager->PageRows >= 2))
622 {
623 /* Reset to display one page by default */
624 Pager->ScrollRows = Pager->PageRows - 1;
625
626 /* Prompt the user; give him some values for statistics */
627 // FIXME: Doesn't reflect what's currently being displayed.
628 if (!PagePrompt(Pager, Pager->ich, len))
629 return FALSE;
630 }
631
632 /* If we display to a console, recalculate its screen extent
633 * in case the user has redimensioned it during the prompt. */
634 if (bIsConsole && ConGetScreenInfo(Pager->Screen, &csbi))
635 {
636 Pager->PageColumns = csbi.dwSize.X;
637 Pager->PageRows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
638 }
639 }
640
641 return TRUE;
642}
643
644BOOL
646 IN PCON_PAGER Pager,
648 IN BOOL StartPaging,
649 IN PCTSTR szStr)
650{
651 SIZE_T len;
652
653 /* Return if no string has been given */
654 if (szStr == NULL)
655 return TRUE;
656
657 len = wcslen(szStr);
658 return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, len);
659}
660
661BOOL
663 IN PCON_PAGER Pager,
665 IN BOOL StartPaging,
667 IN UINT uID)
668{
669 INT Len;
670 PCWSTR szStr = NULL;
671
672 Len = K32LoadStringW(hInstance, uID, (PWSTR)&szStr, 0);
673 if (szStr && Len)
674 return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, Len);
675 return TRUE;
676}
677
678BOOL
680 IN PCON_PAGER Pager,
682 IN BOOL StartPaging,
683 IN UINT uID)
684{
685 return ConResPagingEx(Pager, PagePrompt, StartPaging, NULL, uID);
686}
687
688/* EOF */
static LPTSTR ExpandTab(LPCTSTR line)
Definition: text.h:141
HINSTANCE hInstance
Definition: charmap.c:19
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define Len
Definition: deflate.h:82
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
@ Screen
Definition: console.h:34
#define GetProcessHeap()
Definition: compat.h:736
#define SetLastError(x)
Definition: compat.h:752
#define HeapAlloc
Definition: compat.h:733
#define HeapReAlloc
Definition: compat.h:734
#define HeapFree(x, y, z)
Definition: compat.h:735
#define WideCharToMultiByte
Definition: compat.h:111
#define HEAP_ZERO_MEMORY
Definition: compat.h:134
UINT WINAPI DECLSPEC_HOTPATCH GetConsoleOutputCP(void)
Definition: console.c:957
unsigned char ch[4][2]
Definition: console.c:118
_ACRTIMP size_t __cdecl wcslen(const wchar_t *)
Definition: wcs.c:2983
return ret
Definition: mutex.c:146
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLenum GLsizei len
Definition: glext.h:6722
#define TEXT(s)
Definition: k32.h:28
static PVOID ptr
Definition: dispmode.c:27
#define min(a, b)
Definition: monoChain.cc:55
static BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
Definition: more.c:159
unsigned int UINT
Definition: ndis.h:50
LPCSTR PCTSTR
Definition: ntbasedef.h:500
LPCCH PCTCH
Definition: ntbasedef.h:498
Console/terminal paging functionality.
#define CON_PAGER_EXPAND_TABS
Definition: pager.h:40
BOOL(__stdcall * PAGE_PROMPT)(IN PCON_PAGER Pager, IN DWORD Done, IN DWORD Total)
Definition: pager.h:83
#define CON_PAGER_CACHE_INCOMPLETE_LINE
Definition: pager.h:43
#define CON_PAGER_EXPAND_FF
Definition: pager.h:41
long LONG
Definition: pedump.c:60
_In_ UINT uID
Definition: shlwapi.h:156
Console/terminal screen management.
static BOOL GetNextLine(IN OUT PCON_PAGER Pager, IN PCTCH TextBuff, IN SIZE_T cch)
Retrieves a new text line, or continue fetching the current one.
Definition: pager.c:67
BOOL ConResPagingEx(IN PCON_PAGER Pager, IN PAGE_PROMPT PagePrompt, IN BOOL StartPaging, IN HINSTANCE hInstance OPTIONAL, IN UINT uID)
Definition: pager.c:662
#define IsCJKCodePage(CodePage)
Definition: pager.c:41
static INT GetWidthOfCharCJK(IN UINT nCodePage, IN WCHAR ch)
Definition: pager.c:47
BOOL ConPutsPaging(IN PCON_PAGER Pager, IN PAGE_PROMPT PagePrompt, IN BOOL StartPaging, IN PCTSTR szStr)
Definition: pager.c:645
static BOOL ConPagerWorker(IN PCON_PAGER Pager, IN PCTCH TextBuff, IN SIZE_T cch)
Does the main paging work: fetching text lines and displaying them.
Definition: pager.c:218
#define CON_STREAM_WRITE
Definition: pager.c:32
BOOL ConResPaging(IN PCON_PAGER Pager, IN PAGE_PROMPT PagePrompt, IN BOOL StartPaging, IN UINT uID)
Definition: pager.c:679
BOOL ConWritePaging(IN PCON_PAGER Pager, IN PAGE_PROMPT PagePrompt, IN BOOL StartPaging, IN PCTCH szStr, IN SIZE_T len)
Definition: pager.c:548
BOOL ConGetScreenInfo(IN PCON_SCREEN Screen, OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi)
Definition: screen.c:72
Console I/O streams.
INT WINAPI K32LoadStringW(IN HINSTANCE hInstance OPTIONAL, IN UINT uID, OUT LPWSTR lpBuffer, IN INT nBufferMax)
Definition: utils.c:183
_In_ UINT _In_ UINT cch
Definition: shellapi.h:432
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68
Definition: ncftp.h:79
SHORT X
Definition: blue.h:26
SHORT Top
Definition: blue.h:33
SHORT Bottom
Definition: blue.h:35
uint16_t * PWSTR
Definition: typedefs.h:56
const uint16_t * PCWSTR
Definition: typedefs.h:57
ULONG_PTR SIZE_T
Definition: typedefs.h:80
int32_t INT
Definition: typedefs.h:58
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define IN
Definition: typedefs.h:39
#define OUT
Definition: typedefs.h:40
char TCHAR
Definition: xmlstorage.h:189
__wchar_t WCHAR
Definition: xmlstorage.h:180