1 /*
2  * Copyright (c) 2009, The Mozilla Foundation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the Mozilla Foundation nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY The Mozilla Foundation ''AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL The Mozilla Foundation BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * Contributors:
28  *   Ted Mielczarek <ted.mielczarek@gmail.com>
29  */
30 /*
31  * win32-screenshot.cpp: Save a screenshot of the Windows desktop in .png
32  * format. If a filename is specified as the first argument on the commandline,
33  *  then the image will be saved to that filename. Otherwise, the image will
34  *  be saved as "screenshot.png" in the current working directory.
35  */
36 
37 // VS2015: Platform SDK 8.1's GdiplusTypes.h uses the min macro
38 #undef NOMINMAX
39 #undef WIN32_LEAN_AND_MEAN
40 #include <windows.h>
41 #include <gdiplus.h>
42 
43 // Link w/ subsystem windows so we don't get a console when executing
44 // this binary.
45 #ifndef __MINGW32__
46 #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:wmainCRTStartup")
47 #endif
48 
49 using namespace Gdiplus;
50 
51 // From http://msdn.microsoft.com/en-us/library/ms533843%28VS.85%29.aspx
GetEncoderClsid(const WCHAR * format,CLSID * pClsid)52 static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
53   UINT num = 0;   // number of image encoders
54   UINT size = 0;  // size of the image encoder array in bytes
55 
56   ImageCodecInfo* pImageCodecInfo = nullptr;
57 
58   GetImageEncodersSize(&num, &size);
59   if (size == 0) return -1;  // Failure
60 
61   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
62   if (pImageCodecInfo == nullptr) return -1;  // Failure
63 
64   GetImageEncoders(num, size, pImageCodecInfo);
65 
66   for (UINT j = 0; j < num; ++j) {
67     if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
68       *pClsid = pImageCodecInfo[j].Clsid;
69       free(pImageCodecInfo);
70       return j;  // Success
71     }
72   }
73 
74   free(pImageCodecInfo);
75   return -1;  // Failure
76 }
77 
78 #ifdef __MINGW32__
79 extern "C"
80 #endif
81     int
wmain(int argc,wchar_t ** argv)82     wmain(int argc, wchar_t** argv) {
83   GdiplusStartupInput gdiplusStartupInput;
84   ULONG_PTR gdiplusToken;
85   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
86 
87   HWND desktop = GetDesktopWindow();
88   HDC desktopdc = GetDC(desktop);
89   HDC mydc = CreateCompatibleDC(desktopdc);
90   int width = GetSystemMetrics(SM_CXSCREEN);
91   int height = GetSystemMetrics(SM_CYSCREEN);
92   HBITMAP mybmp = CreateCompatibleBitmap(desktopdc, width, height);
93   HBITMAP oldbmp = (HBITMAP)SelectObject(mydc, mybmp);
94   BitBlt(mydc, 0, 0, width, height, desktopdc, 0, 0, SRCCOPY | CAPTUREBLT);
95   SelectObject(mydc, oldbmp);
96 
97   const wchar_t* filename = (argc > 1) ? argv[1] : L"screenshot.png";
98   Bitmap* b = Bitmap::FromHBITMAP(mybmp, nullptr);
99   CLSID encoderClsid;
100   Status stat = GenericError;
101   if (b && GetEncoderClsid(L"image/png", &encoderClsid) != -1) {
102     stat = b->Save(filename, &encoderClsid, nullptr);
103   }
104   if (b) delete b;
105 
106   // cleanup
107   GdiplusShutdown(gdiplusToken);
108   ReleaseDC(desktop, desktopdc);
109   DeleteObject(mybmp);
110   DeleteDC(mydc);
111   return stat == Ok ? 0 : 1;
112 }
113