88#include < gdiplus.h>
99#include < thumbcache.h>
1010#include < memory>
11+ #include < fstream>
1112
1213#pragma comment(lib, "Ole32.lib")
1314#pragma comment(lib, "Shlwapi.lib")
@@ -98,9 +99,52 @@ void SaveBitmapAsPNG(HBITMAP hBitmap, const std::wstring& outputPath) {
9899 GdiplusShutdown (gdiplusToken);
99100}
100101
102+ void SaveBitmapAsBMP (HBITMAP hBitmap, const std::wstring& outputPath) {
103+ BITMAP bmp;
104+ if (!GetObject (hBitmap, sizeof (BITMAP), &bmp)) return ;
105+
106+ BITMAPFILEHEADER bmfHeader = {};
107+ BITMAPINFOHEADER bi = {};
108+
109+ bi.biSize = sizeof (BITMAPINFOHEADER);
110+ bi.biWidth = bmp.bmWidth ;
111+ bi.biHeight = bmp.bmHeight ;
112+ bi.biPlanes = 1 ;
113+ bi.biBitCount = 32 ;
114+ bi.biCompression = BI_RGB;
115+
116+ int bmpSize = ((bmp.bmWidth * bi.biBitCount + 31 ) / 32 ) * 4 * bmp.bmHeight ;
117+
118+ std::unique_ptr<BYTE[]> pixels (new BYTE[bmpSize]);
119+
120+ HDC hdc = GetDC (nullptr );
121+ if (!GetDIBits (hdc, hBitmap, 0 , bmp.bmHeight , pixels.get (), (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
122+ ReleaseDC (nullptr , hdc);
123+ return ;
124+ }
125+ ReleaseDC (nullptr , hdc);
126+
127+ bmfHeader.bfType = 0x4D42 ;
128+ bmfHeader.bfOffBits = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER);
129+ bmfHeader.bfSize = bmfHeader.bfOffBits + bmpSize;
130+
131+ std::ofstream file (outputPath, std::ios::out | std::ios::binary);
132+ if (!file) return ;
133+ file.write (reinterpret_cast <const char *>(&bmfHeader), sizeof (bmfHeader));
134+ file.write (reinterpret_cast <const char *>(&bi), sizeof (bi));
135+ file.write (reinterpret_cast <const char *>(pixels.get ()), bmpSize);
136+ file.close ();
137+ }
138+
139+ bool ShouldSaveAsPng (const std::wstring& outputPath) {
140+ std::wstring ext = outputPath.substr (outputPath.find_last_of (L" ." ) + 1 );
141+ std::transform (ext.begin (), ext.end (), ext.begin (), ::towlower);
142+ return (ext == L" png" );
143+ }
144+
101145void getImage (const Napi::CallbackInfo& info, bool useThumbnail) {
102146 Napi::Env env = info.Env ();
103- if (info.Length () != 3 || !info[0 ].IsString () || !info[1 ].IsString () || !info[2 ].IsNumber ()) {
147+ if (info.Length () < 3 || !info[0 ].IsString () || !info[1 ].IsString () || !info[2 ].IsNumber ()) {
104148 Napi::TypeError::New (env, " Expected (inputPath: string, outputPath: string, size: number)" ).ThrowAsJavaScriptException ();
105149 return ;
106150 }
@@ -109,33 +153,35 @@ void getImage(const Napi::CallbackInfo& info, bool useThumbnail) {
109153 std::wstring outputPath = std::wstring (info[1 ].As <Napi::String>().Utf8Value ().begin (), info[1 ].As <Napi::String>().Utf8Value ().end ());
110154 int size = info[2 ].As <Napi::Number>().Int32Value ();
111155
112- GdiplusStartupInput gdiplusStartupInput;
113- ULONG_PTR gdiplusToken;
114- if (GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, nullptr ) != Ok) {
115- Napi::Error::New (env, " Failed to initialize GDI+" ).ThrowAsJavaScriptException ();
116- return ;
117- }
156+ bool saveAsPng = ShouldSaveAsPng (outputPath);
118157
119158 if (FAILED (CoInitializeEx (nullptr , COINIT_APARTMENTTHREADED))) {
120- GdiplusShutdown (gdiplusToken);
121159 Napi::Error::New (env, " Failed to initialize COM" ).ThrowAsJavaScriptException ();
122160 return ;
123161 }
124162
125163 HBITMAP hBitmap = nullptr ;
126- HRESULT hr = useThumbnail
127- ? GetThumbnailImage (inputPath, size, hBitmap)
128- : GetIconImage (inputPath, size, hBitmap);
129-
130- if (SUCCEEDED (hr) && hBitmap) {
131- SaveBitmapAsPNG (hBitmap, outputPath);
132- DeleteObject (hBitmap);
133- } else {
134- Napi::Error::New (env, " Failed to retrieve image" ).ThrowAsJavaScriptException ();
164+ try {
165+ HRESULT hr = useThumbnail
166+ ? GetThumbnailImage (inputPath, size, hBitmap)
167+ : GetIconImage (inputPath, size, hBitmap);
168+
169+ if (SUCCEEDED (hr) && hBitmap) {
170+ if (saveAsPng) {
171+ SaveBitmapAsPNG (hBitmap, outputPath);
172+ } else {
173+ SaveBitmapAsBMP (hBitmap, outputPath);
174+ }
175+ DeleteObject (hBitmap);
176+ } else {
177+ Napi::Error::New (env, " Failed to retrieve image" ).ThrowAsJavaScriptException ();
178+ }
179+ } catch (...) {
180+ if (hBitmap) DeleteObject (hBitmap);
181+ Napi::Error::New (env, " Native exception in image processing" ).ThrowAsJavaScriptException ();
135182 }
136183
137184 CoUninitialize ();
138- GdiplusShutdown (gdiplusToken);
139185}
140186
141187Napi::Value getIcon (const Napi::CallbackInfo& info) {
0 commit comments