1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for SetConsoleWindowInfo
5  * PROGRAMMER:      Hermes Belusca-Maito
6  */
7 
8 #include <apitest.h>
9 #include <wincon.h>
10 
11 static VOID
12 ResizeTextConsole(
13     IN HANDLE hConOut,
14     IN OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi,
15     IN COORD Resolution,
16     IN PSMALL_RECT WindowSize OPTIONAL)
17 {
18     BOOL Success;
19     SMALL_RECT ConRect;
20 
21     if (Resolution.X != pcsbi->dwSize.X || Resolution.Y != pcsbi->dwSize.Y)
22     {
23         SHORT oldWidth, oldHeight;
24 
25         oldWidth  = pcsbi->srWindow.Right  - pcsbi->srWindow.Left + 1;
26         oldHeight = pcsbi->srWindow.Bottom - pcsbi->srWindow.Top  + 1;
27 
28         /*
29          * If the current console window is too large for
30          * the new screen buffer, resize it first.
31          */
32         if (oldWidth > Resolution.X || oldHeight > Resolution.Y)
33         {
34             ConRect.Left   = ConRect.Top = 0;
35             ConRect.Right  = ConRect.Left + min(oldWidth , Resolution.X) - 1;
36             ConRect.Bottom = ConRect.Top  + min(oldHeight, Resolution.Y) - 1;
37             Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
38             ok(Success, "Setting console wnd info failed with last error error %lu\n", GetLastError());
39         }
40 
41         /* Now resize the screen buffer */
42         Success = SetConsoleScreenBufferSize(hConOut, Resolution);
43         ok(Success, "Setting console SB size failed with last error error %lu\n", GetLastError());
44 
45         /*
46          * Setting a new screen buffer size can change other information,
47          * so update the saved console information.
48          */
49         Success = GetConsoleScreenBufferInfo(hConOut, pcsbi);
50         ok(Success, "Getting SB info\n");
51     }
52 
53     if (!WindowSize)
54     {
55         /* Always resize the console window within the permitted maximum size */
56         ConRect.Left   = 0;
57         ConRect.Right  = ConRect.Left + min(Resolution.X, pcsbi->dwMaximumWindowSize.X) - 1;
58         ConRect.Bottom = min(pcsbi->dwCursorPosition.Y, Resolution.Y - 1);
59         ConRect.Top    = ConRect.Bottom - min(Resolution.Y, pcsbi->dwMaximumWindowSize.Y) + 1;
60     }
61     else
62     {
63         /* Resize the console window according to user's wishes */
64         ConRect.Left   = ConRect.Top = 0;
65         ConRect.Right  = ConRect.Left + WindowSize->Right  - WindowSize->Left;
66         ConRect.Bottom = ConRect.Top  + WindowSize->Bottom - WindowSize->Top ;
67     }
68 
69     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
70     ok(Success, "Setting console wnd info failed with last error error %lu\n", GetLastError());
71 
72     /* Update console screen buffer info */
73     Success = GetConsoleScreenBufferInfo(hConOut, pcsbi);
74     ok(Success, "Getting SB info\n");
75 }
76 
77 START_TEST(SetConsoleWindowInfo)
78 {
79     /*
80      * The aim of this test is to show that what MSDN says about the validity
81      * checks performed on the window size rect given to SetConsoleWindowInfo
82      * is partially wrong.
83      *
84      * Indeed, while it is claimed that:
85      *   "The function fails if the specified window rectangle extends beyond
86      *    the boundaries of the console screen buffer. This means that the Top
87      *    and Left members of the lpConsoleWindow rectangle (or the calculated
88      *    top and left coordinates, if bAbsolute is FALSE) cannot be less than
89      *    zero. Similarly, the Bottom and Right members (or the calculated
90      *    bottom and right coordinates) cannot be greater than (screen buffer
91      *    height – 1) and (screen buffer width – 1), respectively. The function
92      *    also fails if the Right member (or calculated right coordinate) is
93      *    less than or equal to the Left member (or calculated left coordinate)
94      *    or if the Bottom member (or calculated bottom coordinate) is less than
95      *    or equal to the Top member (or calculated top coordinate)."
96      *
97      * the really performed tests are fewer, and it appears that the console
98      * subsystem knows how to take proper actions when the window size rect
99      * has e.g. negative left/top coordinates...
100      *
101      * NOTE that we all perform those tests in "absolute mode" (second parameter
102      * of SetConsoleWindowInfo being TRUE), so that the specified window size rect
103      * is in absolute coordinates (i.e. relative to the console screen buffer),
104      * and not in coordinates relative to the current window-corner coordinates.
105      */
106 
107     BOOL Success;
108     DWORD dwLastError;
109     HANDLE hConOut;
110     COORD Resolution;
111     CONSOLE_SCREEN_BUFFER_INFO org_csbi, csbi, csbi2;
112     SMALL_RECT ConRect;
113 
114     /* First, retrieve a handle to the real console output, even if we are redirected */
115     hConOut = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
116     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
117     if (hConOut == INVALID_HANDLE_VALUE)
118         return; // We cannot run this test if we failed...
119 
120     /*
121      * Retrieve the original console screen buffer info and save it
122      * for restoration at the end of the test. Use a copy after then.
123      */
124     Success = GetConsoleScreenBufferInfo(hConOut, &org_csbi);
125     ok(Success, "Getting SB info\n");
126     if (!Success)
127         goto Cleanup; // We cannot as well run this test if we failed...
128     csbi = org_csbi;
129 
130     /*
131      * Set the console screen buffer to a correct size that should not
132      * completely fill the computer screen. 'csbi' is correctly updated.
133      */
134     Resolution.X = 80;
135     Resolution.Y = 25;
136     ResizeTextConsole(hConOut, &csbi, Resolution, NULL);
137 
138     /* Test 1: Resize the console window to its possible maximum size (succeeds) */
139     ConRect.Left   = ConRect.Top = 0;
140     ConRect.Right  = ConRect.Left + min(csbi.dwSize.X, csbi.dwMaximumWindowSize.X) - 1;
141     ConRect.Bottom = ConRect.Top  + min(csbi.dwSize.Y, csbi.dwMaximumWindowSize.Y) - 1;
142     SetLastError(0xdeadbeef);
143     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
144     dwLastError = GetLastError();
145     ok(Success, "Setting console wnd info\n");
146     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
147 
148     /* Test 2: Set negative Left/Top members, but correct Right/Bottom ones.
149      * The Left/Top members are shifted to zero while the Right/Bottom ones
150      * are shifted too in accordance.
151      * Situation where the Right/Bottom members will be ok after the shift
152      * (succeeds, disagrees with MSDN) */
153     ConRect.Left   = ConRect.Top = -5;
154     ConRect.Right  = csbi.dwSize.X - 7;
155     ConRect.Bottom = csbi.dwSize.Y - 7;
156     // Expected result: ConRect.Left == ConRect.Top == 0 and
157     // ConRect.Right == csbi.dwSize.X - 2, ConRect.Bottom == csbi.dwSize.Y - 2;
158     SetLastError(0xdeadbeef);
159     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
160     dwLastError = GetLastError();
161     ok(Success, "Setting console wnd info should have succeeded!\n");
162     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
163 
164     /* Check the new reported window size rect */
165     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
166     ok(Success, "Getting SB info\n");
167     if (Success)
168     {
169         ConRect.Right -= ConRect.Left;
170         ConRect.Left = 0;
171         ConRect.Bottom -= ConRect.Top;
172         ConRect.Top = 0;
173 
174         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
175            csbi2.srWindow.Left, ConRect.Left);
176         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
177            csbi2.srWindow.Top, ConRect.Top);
178         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
179            csbi2.srWindow.Right, ConRect.Right);
180         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
181            csbi2.srWindow.Bottom, ConRect.Bottom);
182     }
183 
184     /* Test 3: Similar to Test 2, but set the Right/Bottom members too large
185      * with respect to the screen buffer size, so that after their shift, they
186      * are still too large (fails, agrees with MSDN) */
187     ConRect.Left   = ConRect.Top = -5;
188     ConRect.Right  = csbi.dwSize.X + 2; // Bigger than SB size
189     ConRect.Bottom = csbi.dwSize.Y + 2; // Bigger than SB size
190     SetLastError(0xdeadbeef);
191     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
192     dwLastError = GetLastError();
193     ok(!Success, "Setting console wnd info should have failed!\n");
194     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
195        ERROR_INVALID_PARAMETER, dwLastError);
196 
197     /* Check the new reported window size rect */
198     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
199     ok(Success, "Getting SB info\n");
200     if (Success)
201     {
202         /* NOTE that here we compare against the old csbi data! */
203         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
204            csbi2.srWindow.Left, 0);
205         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
206            csbi2.srWindow.Top, 0);
207         ok(csbi2.srWindow.Right == csbi.dwSize.X - 2, "srWindow.Right = %d, expected %d\n",
208            csbi2.srWindow.Right, csbi.dwSize.X - 2);
209         ok(csbi2.srWindow.Bottom == csbi.dwSize.Y - 2, "srWindow.Bottom = %d, expected %d\n",
210            csbi2.srWindow.Bottom, csbi.dwSize.Y - 2);
211     }
212 
213     /* Test 4: Similar to Tests 2 and 3, but we here just check what happens for
214      * the Right/Bottom members when they are too large, without caring about the
215      * Left/Top members (the latter being set to valid values this time)
216      * (fails, agrees with MSDN) */
217     ConRect.Left   = ConRect.Top = 2; // OK
218     ConRect.Right  = csbi.dwSize.X + 7; // Bigger than SB size
219     ConRect.Bottom = csbi.dwSize.Y + 7; // Bigger than SB size
220     SetLastError(0xdeadbeef);
221     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
222     dwLastError = GetLastError();
223     ok(!Success, "Setting console wnd info should have failed!\n");
224     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
225        ERROR_INVALID_PARAMETER, dwLastError);
226 
227     /* Check the new reported window size rect */
228     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
229     ok(Success, "Getting SB info\n");
230     if (Success)
231     {
232         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
233            csbi2.srWindow.Left, 0);
234         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
235            csbi2.srWindow.Top, 0);
236 
237         /* NOTE that here we compare against the old csbi data! */
238         ok(csbi2.srWindow.Right == csbi.dwSize.X - 2, "srWindow.Right = %d, expected %d\n",
239            csbi2.srWindow.Right, csbi.dwSize.X - 2);
240         ok(csbi2.srWindow.Bottom == csbi.dwSize.Y - 2, "srWindow.Bottom = %d, expected %d\n",
241            csbi2.srWindow.Bottom, csbi.dwSize.Y - 2);
242     }
243 
244     /* Test 5: Set Right/Bottom members strictly smaller than Left/Top members
245      * (fails, agrees with MSDN) */
246     ConRect.Left   = csbi.dwSize.X - 5;
247     ConRect.Right  = 0;
248     ConRect.Top    = csbi.dwSize.Y - 5;
249     ConRect.Bottom = 0;
250     SetLastError(0xdeadbeef);
251     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
252     dwLastError = GetLastError();
253     ok(!Success, "Setting console wnd info should have failed!\n");
254     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
255        ERROR_INVALID_PARAMETER, dwLastError);
256 
257     /* Test 6: Set Left/Top members equal to the Right/Bottom members respectively
258      * (succeeds, disagrees with MSDN) */
259     ConRect.Left = ConRect.Right  = 2;
260     ConRect.Top  = ConRect.Bottom = 5;
261     SetLastError(0xdeadbeef);
262     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
263     dwLastError = GetLastError();
264     ok(Success, "Setting console wnd info should have succeeded!\n");
265     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
266 
267     /* Check the new reported window size rect */
268     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
269     ok(Success, "Getting SB info\n");
270     if (Success)
271     {
272         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
273            csbi2.srWindow.Left, ConRect.Left);
274         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
275            csbi2.srWindow.Top, ConRect.Top);
276         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
277            csbi2.srWindow.Right, ConRect.Right);
278         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
279            csbi2.srWindow.Bottom, ConRect.Bottom);
280     }
281 
282     /*
283      * Test 7: Test how large can the console window be, for a given
284      * screen buffer size. For that we set the console screen buffer
285      * to a really large size, hoping that its corresponding window size
286      * is larger than the computer screen. The permitted maximum window
287      * size specified in csbi.dwMaximumWindowSize should be a boundary.
288      */
289     Resolution.X = 500;
290     Resolution.Y = 500;
291     ResizeTextConsole(hConOut, &csbi, Resolution, NULL);
292     /* Be sure that csbi.dwMaximumWindowSize is strictly smaller
293      * than the console screen buffer size, for our matters... */
294     ok((csbi.dwMaximumWindowSize.X < Resolution.X) && (csbi.dwMaximumWindowSize.Y < Resolution.Y),
295        "dwMaximumWindowSize = {%d, %d} was expected to be smaller than Resolution = {%d, %d}\n",
296        csbi.dwMaximumWindowSize.X, csbi.dwMaximumWindowSize.Y, Resolution.X, Resolution.Y);
297 
298     /* Now try to set first the console window to a size smaller than the maximum size */
299     ConRect.Left   = ConRect.Top = 0;
300     ConRect.Right  = csbi.dwMaximumWindowSize.X - 1;
301     ConRect.Bottom = csbi.dwMaximumWindowSize.Y - 1;
302     SetLastError(0xdeadbeef);
303     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
304     dwLastError = GetLastError();
305     ok(Success, "Setting console wnd info should have succeeded!\n");
306     ok(dwLastError != ERROR_INVALID_PARAMETER, "GetLastError: %lu\n", dwLastError);
307 
308     /* Check the new reported window size rect */
309     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
310     ok(Success, "Getting SB info\n");
311     if (Success)
312     {
313         ok(csbi2.srWindow.Left == ConRect.Left, "srWindow.Left = %d, expected %d\n",
314            csbi2.srWindow.Left, ConRect.Left);
315         ok(csbi2.srWindow.Top == ConRect.Top, "srWindow.Top = %d, expected %d\n",
316            csbi2.srWindow.Top, ConRect.Top);
317         ok(csbi2.srWindow.Right == ConRect.Right, "srWindow.Right = %d, expected %d\n",
318            csbi2.srWindow.Right, ConRect.Right);
319         ok(csbi2.srWindow.Bottom == ConRect.Bottom, "srWindow.Bottom = %d, expected %d\n",
320            csbi2.srWindow.Bottom, ConRect.Bottom);
321     }
322 
323     /* And now try to set the console window to a size larger than the maximum size.
324      * The SetConsoleWindowInfo call should fail */
325     ConRect.Left   = ConRect.Top = 0;
326     ConRect.Right  = csbi.dwMaximumWindowSize.X + 1;
327     ConRect.Bottom = csbi.dwMaximumWindowSize.Y + 1;
328     SetLastError(0xdeadbeef);
329     Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
330     dwLastError = GetLastError();
331     ok(!Success, "Setting console wnd info should have failed!\n");
332     ok(dwLastError == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
333        ERROR_INVALID_PARAMETER, dwLastError);
334 
335     /* Check the new reported window size rect */
336     Success = GetConsoleScreenBufferInfo(hConOut, &csbi2);
337     ok(Success, "Getting SB info\n");
338     if (Success)
339     {
340         ok(csbi2.srWindow.Left == 0, "srWindow.Left = %d, expected %d\n",
341            csbi2.srWindow.Left, 0);
342         ok(csbi2.srWindow.Top == 0, "srWindow.Top = %d, expected %d\n",
343            csbi2.srWindow.Top, 0);
344         ok(csbi2.srWindow.Right == csbi.dwMaximumWindowSize.X - 1, "srWindow.Right = %d, expected %d\n",
345            csbi2.srWindow.Right, csbi.dwMaximumWindowSize.X - 1);
346         ok(csbi2.srWindow.Bottom == csbi.dwMaximumWindowSize.Y - 1, "srWindow.Bottom = %d, expected %d\n",
347            csbi2.srWindow.Bottom, csbi.dwMaximumWindowSize.Y - 1);
348     }
349 
350 
351     /* Done! Restore the original console screen buffer size and perform cleanup */
352     ResizeTextConsole(hConOut, &csbi, org_csbi.dwSize, &org_csbi.srWindow);
353 
354 Cleanup:
355     CloseHandle(hConOut);
356 }
357