Skip to content

Commit 005c5c9

Browse files
committed
Removing use of Windows hooks for taking screenshots in IE.
This will fix the issue of taking full-page screenshots when using the 32-bit IE driver on 64-bit Windows installations.
1 parent 8f310fc commit 005c5c9

File tree

7 files changed

+7351
-7610
lines changed

7 files changed

+7351
-7610
lines changed

cpp/iedriver/CommandHandlers/ScreenshotCommandHandler.h

Lines changed: 38 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#define WEBDRIVER_IE_SCREENSHOTCOMMANDHANDLER_H_
1919

2020
#include "../Browser.h"
21-
#include "../HookProcessor.h"
2221
#include "../IECommandHandler.h"
2322
#include "../IECommandExecutor.h"
2423
#include "logging.h"
@@ -129,76 +128,56 @@ class ScreenshotCommandHandler : public IECommandHandler {
129128
LOG(DEBUG) << "Initial chrome sizes are (w, h): "
130129
<< chrome_width << ", " << chrome_height;
131130

132-
// Technically, we could use a custom structure here, and save a
133-
// few bytes, but RECT is already a well-known structure, and
134-
// one that is included as part of the Windows SDK, so we'll
135-
// leverage it.
136-
RECT max_image_dimensions;
137-
max_image_dimensions.left = 0;
138-
max_image_dimensions.top = 0;
139-
max_image_dimensions.right = document_info.width + chrome_width;
140-
max_image_dimensions.bottom = document_info.height + chrome_height;
131+
int target_window_width = document_info.width + chrome_width;
132+
int target_window_height = document_info.height + chrome_height;
141133

142134
// For some reason, this technique does not allow the user to resize
143135
// the browser window to greater than SIZE_LIMIT x SIZE_LIMIT. This is pretty
144136
// big, so we'll cap the allowable screenshot size to that.
145137
//
146138
// GDI+ limit after which it may report Generic error for some image types
147139
int SIZE_LIMIT = 65534;
148-
if (max_image_dimensions.bottom > SIZE_LIMIT) {
140+
if (target_window_height > SIZE_LIMIT) {
149141
LOG(WARN) << "Required height is greater than limit. Truncating screenshot height.";
150-
max_image_dimensions.bottom = SIZE_LIMIT;
151-
document_info.height = max_image_dimensions.bottom - chrome_height;
142+
target_window_height = SIZE_LIMIT;
143+
document_info.height = target_window_height - chrome_height;
152144
}
153-
if (max_image_dimensions.right > SIZE_LIMIT) {
145+
if (target_window_width > SIZE_LIMIT) {
154146
LOG(WARN) << "Required width is greater than limit. Truncating screenshot width.";
155-
max_image_dimensions.right = SIZE_LIMIT;
156-
document_info.width = max_image_dimensions.right - chrome_width;
147+
target_window_width = SIZE_LIMIT;
148+
document_info.width = target_window_width - chrome_width;
157149
}
158150

159151
long original_width = browser->GetWidth();
160152
long original_height = browser->GetHeight();
161153
LOG(DEBUG) << "Initial browser window sizes are (w, h): "
162154
<< original_width << ", " << original_height;
163155

164-
bool requires_rezise = original_width <= max_image_dimensions.right ||
165-
original_height <= max_image_dimensions.bottom;
166-
167-
// The resize message is being ignored if the window appears to be
168-
// maximized. There's likely a way to bypass that. The kludgy way
169-
// is to unmaximize the window, then move on with setting the window
170-
// to the dimensions we really want. This is okay because we revert
171-
// back to the original dimensions afterward.
172156
BOOL is_maximized = ::IsZoomed(ie_window_handle);
173-
if (is_maximized) {
174-
LOG(DEBUG) << "Window is maximized currently. Demaximizing.";
175-
::ShowWindow(ie_window_handle, SW_SHOWNORMAL);
176-
}
157+
bool requires_resize = original_width < target_window_width ||
158+
original_height < target_window_height;
159+
160+
if (requires_resize) {
161+
// The resize message is being ignored if the window appears to be
162+
// maximized. There's likely a way to bypass that. The kludgy way
163+
// is to unmaximize the window, then move on with setting the window
164+
// to the dimensions we really want. This is okay because we revert
165+
// back to the original dimensions afterward.
166+
if (is_maximized) {
167+
LOG(DEBUG) << "Window is maximized currently. Demaximizing.";
168+
::ShowWindow(ie_window_handle, SW_SHOWNORMAL);
169+
}
177170

178-
HookSettings hook_settings;
179-
hook_settings.window_handle = ie_window_handle;
180-
hook_settings.hook_procedure_name = "ScreenshotWndProc";
181-
hook_settings.hook_procedure_type = WH_CALLWNDPROC;
182-
hook_settings.communication_type = OneWay;
183-
184-
HookProcessor hook;
185-
if (!hook.CanSetWindowsHook(ie_window_handle)) {
186-
LOG(WARN) << "Screenshot will be truncated! There is a mismatch "
187-
<< "in the bitness between the driver and browser. In "
188-
<< "particular, you are likely using a 32-bit "
189-
<< "IEDriverServer.exe and a 64-bit version of IE.";
171+
RECT ie_window_rect;
172+
::GetWindowRect(ie_window_handle, &ie_window_rect);
173+
::SetWindowPos(ie_window_handle,
174+
NULL,
175+
ie_window_rect.left,
176+
ie_window_rect.top,
177+
target_window_width,
178+
target_window_height,
179+
SWP_NOSENDCHANGING);
190180
}
191-
hook.Initialize(hook_settings);
192-
193-
hook.PushData(sizeof(max_image_dimensions), &max_image_dimensions);
194-
browser->SetWidth(max_image_dimensions.right);
195-
196-
// Must re-push data because the resize causes a message to the
197-
// IE window, and reading the data clears the buffer.
198-
hook.PushData(sizeof(max_image_dimensions), &max_image_dimensions);
199-
browser->SetHeight(max_image_dimensions.bottom);
200-
201-
hook.Dispose();
202181

203182
// Capture the window's canvas to a DIB.
204183
BOOL created = this->image_->Create(document_info.width,
@@ -216,12 +195,14 @@ class ScreenshotCommandHandler : public IECommandHandler {
216195
LOG(WARN) << "PrintWindow API is not able to get content window screenshot";
217196
}
218197

219-
// Restore the browser to the original dimensions.
220-
if (is_maximized) {
221-
::ShowWindow(ie_window_handle, SW_MAXIMIZE);
222-
} else {
223-
browser->SetHeight(original_height);
224-
browser->SetWidth(original_width);
198+
if (requires_resize) {
199+
// Restore the browser to the original dimensions.
200+
if (is_maximized) {
201+
::ShowWindow(ie_window_handle, SW_MAXIMIZE);
202+
} else {
203+
browser->SetHeight(original_height);
204+
browser->SetWidth(original_width);
205+
}
225206
}
226207

227208
this->image_->ReleaseDC();
@@ -352,84 +333,5 @@ class ScreenshotCommandHandler : public IECommandHandler {
352333

353334
} // namespace webdriver
354335

355-
#ifdef __cplusplus
356-
extern "C" {
357-
#endif
358-
359-
// This function is our message processor that we inject into the IEFrame
360-
// process. Its sole purpose is to process WM_GETMINMAXINFO messages and
361-
// modify the max tracking size so that we can resize the IEFrame window to
362-
// greater than the virtual screen resolution. All other messages are
363-
// delegated to the original IEFrame message processor. This function
364-
// uninjects itself immediately upon execution.
365-
LRESULT CALLBACK MinMaxInfoHandler(HWND hwnd,
366-
UINT message,
367-
WPARAM wParam,
368-
LPARAM lParam) {
369-
// Grab a reference to the original message processor.
370-
HANDLE original_message_proc = ::GetProp(hwnd,
371-
L"__original_message_processor__");
372-
::RemoveProp(hwnd, L"__original_message_processor__");
373-
374-
// Uninject this method.
375-
::SetWindowLongPtr(hwnd,
376-
GWLP_WNDPROC,
377-
reinterpret_cast<LONG_PTR>(original_message_proc));
378-
379-
if (WM_GETMINMAXINFO == message) {
380-
MINMAXINFO* minMaxInfo = reinterpret_cast<MINMAXINFO*>(lParam);
381-
RECT max_size;
382-
webdriver::HookProcessor::CopyDataFromBuffer(sizeof(RECT), reinterpret_cast<void*>(&max_size));
383-
minMaxInfo->ptMaxTrackSize.x = max_size.right;
384-
minMaxInfo->ptMaxTrackSize.y = max_size.bottom;
385-
386-
// We're not going to pass this message onto the original message
387-
// processor, so we should return 0, per the documentation for
388-
// the WM_GETMINMAXINFO message.
389-
return 0;
390-
}
391-
392-
// All other messages should be handled by the original message processor.
393-
return ::CallWindowProc(reinterpret_cast<WNDPROC>(original_message_proc),
394-
hwnd,
395-
message,
396-
wParam,
397-
lParam);
398-
}
399-
400-
// Many thanks to sunnyandy for helping out with this approach. What we're
401-
// doing here is setting up a Windows hook to see incoming messages to the
402-
// IEFrame's message processor. Once we find one that's WM_GETMINMAXINFO,
403-
// we inject our own message processor into the IEFrame process to handle
404-
// that one message. WM_GETMINMAXINFO is sent on a resize event so the process
405-
// can see how large a window can be. By modifying the max values, we can allow
406-
// a window to be sized greater than the (virtual) screen resolution would
407-
// otherwise allow.
408-
//
409-
// See the discussion here: http://www.codeguru.com/forum/showthread.php?p=1889928
410-
LRESULT CALLBACK ScreenshotWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
411-
CWPSTRUCT* call_window_proc_struct = reinterpret_cast<CWPSTRUCT*>(lParam);
412-
if (WM_COPYDATA == call_window_proc_struct->message) {
413-
COPYDATASTRUCT* data = reinterpret_cast<COPYDATASTRUCT*>(call_window_proc_struct->lParam);
414-
webdriver::HookProcessor::CopyDataToBuffer(data->cbData, data->lpData);
415-
} else if (WM_GETMINMAXINFO == call_window_proc_struct->message) {
416-
// Inject our own message processor into the process so we can modify
417-
// the WM_GETMINMAXINFO message. It is not possible to modify the
418-
// message from this hook, so the best we can do is inject a function
419-
// that can.
420-
LONG_PTR proc = ::SetWindowLongPtr(call_window_proc_struct->hwnd,
421-
GWLP_WNDPROC,
422-
reinterpret_cast<LONG_PTR>(MinMaxInfoHandler));
423-
::SetProp(call_window_proc_struct->hwnd,
424-
L"__original_message_processor__",
425-
reinterpret_cast<HANDLE>(proc));
426-
}
427-
428-
return ::CallNextHookEx(NULL, nCode, wParam, lParam);
429-
}
430-
431-
#ifdef __cplusplus
432-
}
433-
#endif
434336

435337
#endif // WEBDRIVER_IE_SCREENSHOTCOMMANDHANDLER_H_

0 commit comments

Comments
 (0)