1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <string.h>
21 #include <svsys.h>
22 #include <process.h>
23
24 #include <osl/conditn.hxx>
25 #include <osl/file.hxx>
26 #include <sal/log.hxx>
27 #include <tools/debug.hxx>
28 #include <tools/time.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/solarmutex.hxx>
31 #include <comphelper/windowserrorstring.hxx>
32 #include <com/sun/star/uno/Reference.h>
33 #include <o3tl/char16_t2wchar_t.hxx>
34 #include <o3tl/temporary.hxx>
35
36 #include <dndhelper.hxx>
37 #include <vcl/inputtypes.hxx>
38 #include <vcl/opengl/OpenGLContext.hxx>
39 #include <vcl/sysdata.hxx>
40 #include <vcl/timer.hxx>
41 #include <vclpluginapi.h>
42
43 #include <win/dnd_source.hxx>
44 #include <win/dnd_target.hxx>
45 #include <win/wincomp.hxx>
46 #include <win/salids.hrc>
47 #include <win/saldata.hxx>
48 #include <win/salinst.h>
49 #include <win/salframe.h>
50 #include <win/salobj.h>
51 #include <win/saltimer.h>
52 #include <win/salbmp.h>
53 #include <win/winlayout.hxx>
54
55 #include <config_features.h>
56 #include <vcl/skia/SkiaHelper.hxx>
57 #if HAVE_FEATURE_SKIA
58 #include <config_skia.h>
59 #include <skia/salbmp.hxx>
60 #include <skia/win/gdiimpl.hxx>
61 #endif
62
63 #include <salsys.hxx>
64
65 #include <desktop/crashreport.hxx>
66
67 #include <prewin.h>
68
69 #include <gdiplus.h>
70 #include <shlobj.h>
71
72 #include <postwin.h>
73
74 static LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
75
76 class SalYieldMutex : public comphelper::SolarMutex
77 {
78 public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
79 osl::Condition m_condition; /// for MsgWaitForMultipleObjects()
80
81 protected:
82 virtual void doAcquire( sal_uInt32 nLockCount ) override;
83 virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
84
85 static void BeforeReleaseHandler();
86
87 public:
88 explicit SalYieldMutex();
89
90 virtual bool IsCurrentThread() const override;
91 virtual bool tryToAcquire() override;
92 };
93
SalYieldMutex()94 SalYieldMutex::SalYieldMutex()
95 {
96 SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler );
97 }
98
BeforeReleaseHandler()99 void SalYieldMutex::BeforeReleaseHandler()
100 {
101 OpenGLContext::prepareForYield();
102
103 if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
104 {
105 // If we don't call these message, the Output from the
106 // Java clients doesn't come in the right order
107 GdiFlush();
108 }
109 }
110
111 /// note: while VCL is fully up and running (other threads started and
112 /// before shutdown), the main thread must acquire SolarMutex only via
113 /// this function to avoid deadlock
doAcquire(sal_uInt32 nLockCount)114 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
115 {
116 WinSalInstance* pInst = GetSalData()->mpInstance;
117 if ( pInst && pInst->IsMainThread() )
118 {
119 if ( pInst->m_nNoYieldLock )
120 return;
121 // tdf#96887 If this is the main thread, then we must wait for two things:
122 // - the yield mutex being unlocked
123 // - SendMessage() being triggered
124 // This can nicely be done using MsgWaitForMultipleObjects, which is called in
125 // m_condition.wait(). The 2nd one is
126 // needed because if we don't reschedule, then we create deadlocks if a
127 // Window's create/destroy is called via SendMessage() from another thread.
128 // Have a look at the osl_waitCondition implementation for more info.
129 do {
130 // Calling Condition::reset frequently turns out to be a little expensive,
131 // and the vast majority of the time there is no contention, so first
132 // try just acquiring the mutex.
133 if (m_aMutex.tryToAcquire())
134 break;
135 // reset condition *before* acquiring!
136 m_condition.reset();
137 if (m_aMutex.tryToAcquire())
138 break;
139 // wait for SalYieldMutex::release() to set the condition
140 osl::Condition::Result res = m_condition.wait();
141 assert(osl::Condition::Result::result_ok == res);
142 (void) res;
143 }
144 while ( true );
145 }
146 else
147 m_aMutex.acquire();
148 ++m_nCount;
149 --nLockCount;
150
151 comphelper::SolarMutex::doAcquire( nLockCount );
152 }
153
doRelease(const bool bUnlockAll)154 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
155 {
156 WinSalInstance* pInst = GetSalData()->mpInstance;
157 if ( pInst && pInst->m_nNoYieldLock && pInst->IsMainThread() )
158 return 1;
159
160 sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
161 // wake up ImplSalYieldMutexAcquireWithWait() after release
162 if ( 0 == m_nCount )
163 m_condition.set();
164 return nCount;
165 }
166
tryToAcquire()167 bool SalYieldMutex::tryToAcquire()
168 {
169 WinSalInstance* pInst = GetSalData()->mpInstance;
170 if ( pInst )
171 {
172 if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
173 return true;
174 else
175 return comphelper::SolarMutex::tryToAcquire();
176 }
177 else
178 return false;
179 }
180
ImplSalYieldMutexAcquireWithWait(sal_uInt32 nCount)181 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
182 {
183 WinSalInstance* pInst = GetSalData()->mpInstance;
184 if ( pInst )
185 pInst->GetYieldMutex()->acquire( nCount );
186 }
187
ImplSalYieldMutexTryToAcquire()188 bool ImplSalYieldMutexTryToAcquire()
189 {
190 WinSalInstance* pInst = GetSalData()->mpInstance;
191 return pInst && pInst->GetYieldMutex()->tryToAcquire();
192 }
193
ImplSalYieldMutexRelease()194 void ImplSalYieldMutexRelease()
195 {
196 WinSalInstance* pInst = GetSalData()->mpInstance;
197 if ( pInst )
198 {
199 GdiFlush();
200 pInst->GetYieldMutex()->release();
201 }
202 }
203
IsCurrentThread() const204 bool SalYieldMutex::IsCurrentThread() const
205 {
206 if ( !GetSalData()->mpInstance->m_nNoYieldLock )
207 return SolarMutex::IsCurrentThread();
208 else
209 return GetSalData()->mpInstance->IsMainThread();
210 }
211
initKeyCodeMap()212 void SalData::initKeyCodeMap()
213 {
214 auto initKey = [this](wchar_t ch, sal_uInt16 key)
215 {
216 if (UINT vkey = LOWORD(VkKeyScanW(ch)); vkey < 0xffff)
217 maVKMap[vkey] = key;
218 };
219
220 maVKMap.clear();
221
222 initKey( L'+', KEY_ADD );
223 initKey( L'-', KEY_SUBTRACT );
224 initKey( L'*', KEY_MULTIPLY );
225 initKey( L'/', KEY_DIVIDE );
226 initKey( L'.', KEY_POINT );
227 initKey( L',', KEY_COMMA );
228 initKey( L'<', KEY_LESS );
229 initKey( L'>', KEY_GREATER );
230 initKey( L'=', KEY_EQUAL );
231 initKey( L'~', KEY_TILDE );
232 initKey( L'`', KEY_QUOTELEFT );
233 initKey( L'[', KEY_BRACKETLEFT );
234 initKey( L']', KEY_BRACKETRIGHT );
235 initKey( L';', KEY_SEMICOLON );
236 initKey( L'\'', KEY_QUOTERIGHT );
237 initKey( L'}', KEY_RIGHTCURLYBRACKET );
238 initKey( L'#', KEY_NUMBERSIGN);
239 initKey( L':', KEY_COLON );
240 }
241
242 // SalData
243
SalData()244 SalData::SalData()
245 : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false,
246 sal::systools::CoInitializeGuard::WhenFailed::NoThrow)
247 // put main thread in Single Threaded Apartment (STA)
248 {
249 mhInst = nullptr; // default instance handle
250 mnCmdShow = 0; // default frame show style
251 mhDitherPal = nullptr; // dither palette
252 mhDitherDIB = nullptr; // dither memory handle
253 mpDitherDIB = nullptr; // dither memory
254 mpDitherDIBData = nullptr; // beginning of DIB data
255 mpDitherDiff = nullptr; // Dither mapping table
256 mpDitherLow = nullptr; // Dither mapping table
257 mpDitherHigh = nullptr; // Dither mapping table
258 mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
259 mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
260 mpInstance = nullptr; // pointer of first instance
261 mpFirstFrame = nullptr; // pointer of first frame
262 mpFirstObject = nullptr; // pointer of first object window
263 mpFirstVD = nullptr; // first VirDev
264 mpFirstPrinter = nullptr; // first printing printer
265 mh50Bmp = nullptr; // 50% Bitmap
266 mh50Brush = nullptr; // 50% Brush
267 int i;
268 for(i=0; i<MAX_STOCKPEN; i++)
269 {
270 maStockPenColorAry[i] = 0;
271 mhStockPenAry[i] = nullptr;
272 }
273 for(i=0; i<MAX_STOCKBRUSH; i++)
274 {
275 maStockBrushColorAry[i] = 0;
276 mhStockBrushAry[i] = nullptr;
277 }
278 mnStockPenCount = 0; // count of static pens
279 mnStockBrushCount = 0; // count of static brushes
280 mnSalObjWantKeyEvt = 0; // KeyEvent for the SalObj hook
281 mnCacheDCInUse = 0; // count of CacheDC in use
282 mbObjClassInit = false; // is SALOBJECTCLASS initialised
283 mbInPalChange = false; // is in WM_QUERYNEWPALETTE
284 mnAppThreadId = 0; // Id from Application-Thread
285 mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
286 mpSharedTempFontItem = nullptr;
287 mpOtherTempFontItem = nullptr;
288 mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
289 mbThemeMenuSupport = false;
290
291 // init with NULL
292 gdiplusToken = 0;
293
294 initKeyCodeMap();
295
296 SetSalData( this );
297 initNWF();
298
299 static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
300 Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
301 }
302
~SalData()303 SalData::~SalData()
304 {
305 deInitNWF();
306 SetSalData( nullptr );
307
308 if (gdiplusToken)
309 Gdiplus::GdiplusShutdown(gdiplusToken);
310 }
311
OSSupportsDarkMode()312 bool OSSupportsDarkMode()
313 {
314 bool bRet = false;
315 if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
316 {
317 typedef LONG(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
318 if (auto RtlGetVersion
319 = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
320 {
321 RTL_OSVERSIONINFOW vi2{};
322 vi2.dwOSVersionInfoSize = sizeof(vi2);
323 if (RtlGetVersion(&vi2) == 0)
324 {
325 if (vi2.dwMajorVersion > 10)
326 bRet = true;
327 else if (vi2.dwMajorVersion == 10)
328 {
329 if (vi2.dwMinorVersion > 0)
330 bRet = true;
331 else if (vi2.dwBuildNumber >= 18362)
332 bRet = true;
333 }
334 }
335 }
336 }
337 return bRet;
338 }
339
340 namespace {
341
342 enum PreferredAppMode
343 {
344 Default,
345 AllowDark,
346 ForceDark,
347 ForceLight,
348 Max
349 };
350
351 }
352
353 extern "C" {
create_SalInstance()354 VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
355 {
356 SalData* pSalData = new SalData();
357
358 STARTUPINFOW aSI;
359 aSI.cb = sizeof( aSI );
360 GetStartupInfoW( &aSI );
361 pSalData->mhInst = GetModuleHandleW( nullptr );
362 pSalData->mnCmdShow = aSI.wShowWindow;
363
364 pSalData->mnAppThreadId = GetCurrentThreadId();
365
366 static bool bSetAllowDarkMode = OSSupportsDarkMode(); // too early to additionally check LibreOffice's config
367 if (bSetAllowDarkMode)
368 {
369 typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode);
370 if (HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
371 {
372 if (auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135))))
373 SetPreferredAppMode(AllowDark);
374 FreeLibrary(hUxthemeLib);
375 }
376 }
377
378 // register frame class
379 WNDCLASSEXW aWndClassEx;
380 aWndClassEx.cbSize = sizeof( aWndClassEx );
381 aWndClassEx.style = CS_OWNDC;
382 aWndClassEx.lpfnWndProc = SalFrameWndProcW;
383 aWndClassEx.cbClsExtra = 0;
384 aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
385 aWndClassEx.hInstance = pSalData->mhInst;
386 aWndClassEx.hCursor = nullptr;
387 aWndClassEx.hbrBackground = nullptr;
388 aWndClassEx.lpszMenuName = nullptr;
389 aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
390 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
391 if ( !RegisterClassExW( &aWndClassEx ) )
392 return nullptr;
393
394 aWndClassEx.hIcon = nullptr;
395 aWndClassEx.hIconSm = nullptr;
396 aWndClassEx.style |= CS_SAVEBITS;
397 aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
398 if ( !RegisterClassExW( &aWndClassEx ) )
399 return nullptr;
400
401 // shadow effect for popups on XP
402 aWndClassEx.style |= CS_DROPSHADOW;
403 aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
404 if ( !RegisterClassExW( &aWndClassEx ) )
405 return nullptr;
406
407 aWndClassEx.style = 0;
408 aWndClassEx.lpfnWndProc = SalComWndProcW;
409 aWndClassEx.cbWndExtra = 0;
410 aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
411 if ( !RegisterClassExW( &aWndClassEx ) )
412 return nullptr;
413
414 HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
415 L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
416 pSalData->mhInst, nullptr );
417 if ( !hComWnd )
418 return nullptr;
419
420 WinSalInstance* pInst = new WinSalInstance;
421
422 // init instance (only one instance in this version !!!)
423 pSalData->mpInstance = pInst;
424 pInst->mhInst = pSalData->mhInst;
425 pInst->mhComWnd = hComWnd;
426
427 // init static GDI Data
428 ImplInitSalGDI();
429
430 return pInst;
431 }
432 }
433
WinSalInstance()434 WinSalInstance::WinSalInstance()
435 : SalInstance(std::make_unique<SalYieldMutex>())
436 , mhInst( nullptr )
437 , mhComWnd( nullptr )
438 , m_nNoYieldLock( 0 )
439 {
440 ImplSVData* pSVData = ImplGetSVData();
441 pSVData->maAppData.mxToolkitName = OUString("win");
442 m_bSupportsOpenGL = true;
443 #if HAVE_FEATURE_SKIA
444 WinSkiaSalGraphicsImpl::prepareSkia();
445 #if SKIA_USE_BITMAP32
446 if (SkiaHelper::isVCLSkiaEnabled())
447 m_bSupportsBitmap32 = true;
448 #endif
449 #endif
450 }
451
~WinSalInstance()452 WinSalInstance::~WinSalInstance()
453 {
454 ImplFreeSalGDI();
455 DestroyWindow( mhComWnd );
456 #if HAVE_FEATURE_SKIA
457 SkiaHelper::cleanup();
458 #endif
459 }
460
AfterAppInit()461 void WinSalInstance::AfterAppInit()
462 {
463 // (1) Ideally this would be done at the place that creates the thread, but since this thread is normally
464 // just the default/main thread, that is not possible.
465 // (2) Don't do this on unix, where it causes tools like pstree on Linux to
466 // confusingly report soffice.bin as VCL Main instead.
467 osl_setThreadName("VCL Main");
468 }
469
ImplSalDispatchMessage(const MSG * pMsg)470 static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
471 {
472 SalData* pSalData = GetSalData();
473 if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
474 return 0;
475 LRESULT lResult = DispatchMessageW( pMsg );
476 if ( pSalData->mpFirstObject )
477 ImplSalPostDispatchMsg( pMsg );
478 return lResult;
479 }
480
481 // probably can't be static, because of SalTimer friend? (static gives C4211)
ImplSalYield(const bool bWait,const bool bHandleAllCurrentEvents)482 bool ImplSalYield(const bool bWait, const bool bHandleAllCurrentEvents)
483 {
484 // used to abort further message processing on tick count wraps
485 static sal_uInt32 nLastTicks = 0;
486
487 // we should never yield in m_nNoYieldLock mode!
488 const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
489 assert(!bNoYieldLock);
490 if (bNoYieldLock)
491 return false;
492
493 MSG aMsg;
494 bool bWasMsg = false, bWasTimeoutMsg = false;
495 WinSalTimer* pTimer = static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer);
496
497 sal_uInt32 nCurTicks = GetTickCount();
498
499 do
500 {
501 if (!PeekMessageW(&aMsg, nullptr, 0, 0, PM_REMOVE))
502 break;
503
504 bWasMsg = true;
505 TranslateMessage(&aMsg);
506 LRESULT nRet = ImplSalDispatchMessage(&aMsg);
507
508 bWasTimeoutMsg |= (SAL_MSG_TIMER_CALLBACK == aMsg.message) && static_cast<bool>(nRet);
509
510 if (!bHandleAllCurrentEvents)
511 break;
512
513 if ((aMsg.time > nCurTicks) && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks))
514 break;
515 }
516 while( true );
517
518 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
519 // event loop with timeout messages.
520 // We ensure we never handle more than one timeout per call.
521 // This way we'll always process a normal system message.
522 if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
523 {
524 pTimer->ImplHandleElapsedTimer();
525 bWasMsg = true;
526 }
527
528 nLastTicks = nCurTicks;
529
530 if ( bWait && !bWasMsg )
531 {
532 switch (GetMessageW(&aMsg, nullptr, 0, 0))
533 {
534 case -1:
535 SAL_WARN("vcl.schedule", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
536 // should we std::abort() / SalAbort here?
537 break;
538 case 0:
539 SAL_INFO("vcl.schedule", "GetMessageW received WM_QUIT while waiting");
540 break;
541 default:
542 bWasMsg = true;
543 TranslateMessage(&aMsg);
544 ImplSalDispatchMessage(&aMsg);
545 break;
546 }
547 }
548
549 // If we enabled ForceRealTimer mode skipping our direct timeout processing,
550 // mainly because some Windows API call spawns its own nested message loop,
551 // switch back to our own processing (like after window resize or move)
552 if ( pTimer )
553 pTimer->SetForceRealTimer( false );
554
555 return bWasMsg;
556 }
557
IsMainThread() const558 bool WinSalInstance::IsMainThread() const
559 {
560 const SalData* pSalData = GetSalData();
561 return pSalData->mnAppThreadId == GetCurrentThreadId();
562 }
563
DoYield(bool bWait,bool bHandleAllCurrentEvents)564 bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
565 {
566 bool bDidWork = false;
567 SolarMutexReleaser aReleaser;
568 if ( !IsMainThread() )
569 {
570 bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
571 WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
572 if ( !bDidWork && bWait )
573 {
574 maWaitingYieldCond.reset();
575 maWaitingYieldCond.wait();
576 bDidWork = true;
577 }
578 }
579 else
580 {
581 bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
582 if ( bDidWork )
583 maWaitingYieldCond.set();
584 }
585
586 return bDidWork;
587 }
588
589 namespace
590 {
591 struct NoYieldLockGuard
592 {
NoYieldLockGuard__anonc363b8950311::NoYieldLockGuard593 NoYieldLockGuard()
594 : counter(InSendMessage() ? GetSalData()->mpInstance->m_nNoYieldLock : dummy())
595 {
596 ++counter;
597 }
~NoYieldLockGuard__anonc363b8950311::NoYieldLockGuard598 ~NoYieldLockGuard() { --counter; }
dummy__anonc363b8950311::NoYieldLockGuard599 static decltype(WinSalInstance::m_nNoYieldLock)& dummy()
600 {
601 DBG_TESTSOLARMUTEX(); // called when !InSendMessage()
602 static decltype(WinSalInstance::m_nNoYieldLock) n = 0;
603 return n;
604 }
605 decltype(WinSalInstance::m_nNoYieldLock)& counter;
606 };
607 }
608
SalComWndProc(HWND,UINT nMsg,WPARAM wParam,LPARAM lParam,bool & rDef)609 LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
610 {
611 LRESULT nRet = 0;
612 WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
613
614 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
615 << ", lParam=" << lParam << "); inSendMsg: " << InSendMessage());
616
617 if (ImplGetSVData()->mbDeInit)
618 {
619 SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
620 return 0;
621 }
622
623 switch ( nMsg )
624 {
625 case SAL_MSG_THREADYIELD:
626 assert( !static_cast<bool>(wParam) );
627 nRet = static_cast<LRESULT>(ImplSalYield(
628 false, static_cast<bool>( lParam ) ));
629 break;
630
631 case SAL_MSG_STARTTIMER:
632 {
633 auto const nParam = static_cast<sal_uInt64>( lParam );
634 sal_uInt64 nTime = tools::Time::GetSystemTicks();
635 if ( nTime < nParam )
636 nTime = nParam - nTime;
637 else
638 nTime = 0;
639 assert( pTimer != nullptr );
640 pTimer->ImplStart( nTime );
641 break;
642 }
643
644 case SAL_MSG_STOPTIMER:
645 assert( pTimer != nullptr );
646 pTimer->ImplStop();
647 break;
648
649 case (SAL_MSG_CREATEFRAME):
650 {
651 NoYieldLockGuard g;
652 nRet = reinterpret_cast<LRESULT>(
653 ImplSalCreateFrame(GetSalData()->mpInstance, reinterpret_cast<HWND>(lParam),
654 static_cast<SalFrameStyleFlags>(wParam)));
655 }
656 break;
657 case (SAL_MSG_RECREATEHWND):
658 {
659 NoYieldLockGuard g;
660 nRet = reinterpret_cast<LRESULT>(ImplSalReCreateHWND(
661 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false));
662 }
663 break;
664 case (SAL_MSG_RECREATECHILDHWND):
665 {
666 NoYieldLockGuard g;
667 nRet = reinterpret_cast<LRESULT>(ImplSalReCreateHWND(
668 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true));
669 }
670 break;
671 case (SAL_MSG_DESTROYFRAME):
672 {
673 NoYieldLockGuard g;
674 delete reinterpret_cast<SalFrame*>(lParam);
675 }
676 break;
677
678 case SAL_MSG_DESTROYHWND:
679 // We only destroy the native window here. We do NOT destroy the SalFrame contained
680 // in the structure (GetWindowPtr()).
681 if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
682 {
683 OSL_FAIL("DestroyWindow failed!");
684 // Failure: We remove the SalFrame from the window structure. So we avoid that
685 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
686 SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
687 }
688 break;
689
690 case (SAL_MSG_CREATEOBJECT):
691 {
692 NoYieldLockGuard g;
693 nRet = reinterpret_cast<LRESULT>(ImplSalCreateObject(
694 GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)));
695 }
696 break;
697 case (SAL_MSG_DESTROYOBJECT):
698 {
699 NoYieldLockGuard g;
700 delete reinterpret_cast<SalObject*>(lParam);
701 }
702 break;
703 case (SAL_MSG_GETCACHEDDC):
704 {
705 NoYieldLockGuard g;
706 nRet = reinterpret_cast<LRESULT>(
707 GetDCEx(reinterpret_cast<HWND>(wParam), nullptr, 0x00000002L));
708 }
709 break;
710 case (SAL_MSG_RELEASEDC):
711 {
712 NoYieldLockGuard g;
713 ReleaseDC(reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam));
714 }
715 break;
716
717 case SAL_MSG_TIMER_CALLBACK:
718 assert( pTimer != nullptr );
719 pTimer->ImplHandleTimerEvent( wParam );
720 break;
721
722 case WM_TIMER:
723 assert( pTimer != nullptr );
724 pTimer->ImplHandle_WM_TIMER( wParam );
725 break;
726
727 case SAL_MSG_FORCE_REAL_TIMER:
728 assert(pTimer != nullptr);
729 pTimer->SetForceRealTimer(true);
730 break;
731
732 case SAL_MSG_DUMMY:
733 break;
734
735 default:
736 rDef = true;
737 break;
738 }
739
740 return nRet;
741 }
742
SalComWndProcW(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)743 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
744 {
745 bool bDef = false;
746 LRESULT nRet = 0;
747 __try
748 {
749 nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
750 }
751 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
752 {
753 }
754 if ( bDef )
755 {
756 if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
757 nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
758 }
759 return nRet;
760 }
761
AnyInput(VclInputFlags nType)762 bool WinSalInstance::AnyInput( VclInputFlags nType )
763 {
764 if ( nType & VclInputFlags::TIMER )
765 {
766 const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
767 if ( pTimer && pTimer->HasTimerElapsed() )
768 return true;
769 }
770
771 // Note: Do not use PeekMessage(), despite the name it may dispatch events,
772 // even with PM_NOREMOVE specified, which may lead to unwanted recursion.
773
774 if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
775 {
776 // revert bugfix for #108919# which never reported timeouts when called from the timer handler
777 // which made the application completely unresponsive during background formatting
778 if ( GetQueueStatus( QS_ALLEVENTS ))
779 return true;
780 }
781 else
782 {
783 UINT flags = 0;
784
785 // This code previously considered modifier keys as OTHER,
786 // but that makes this hard to do without PeekMessage,
787 // is inconsistent with the X11 backend, and I see no good reason.
788 if ( nType & VclInputFlags::KEYBOARD )
789 flags |= QS_KEY;
790
791 if ( nType & VclInputFlags::MOUSE )
792 flags |= QS_MOUSE;
793
794 if ( nType & VclInputFlags::PAINT )
795 flags |= QS_PAINT;
796
797 if ( nType & VclInputFlags::TIMER )
798 flags |= QS_TIMER;
799
800 if( nType & VclInputFlags::OTHER )
801 flags |= QS_ALLEVENTS & ~QS_KEY & ~QS_MOUSE & ~QS_PAINT & ~QS_TIMER;
802
803 if( GetQueueStatus( flags ))
804 return true;
805 }
806
807 return false;
808 }
809
CreateChildFrame(SystemParentData * pSystemParentData,SalFrameStyleFlags nSalFrameStyle)810 SalFrame* WinSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, SalFrameStyleFlags nSalFrameStyle )
811 {
812 // to switch to Main-Thread
813 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(pSystemParentData->hWnd) )));
814 }
815
CreateFrame(SalFrame * pParent,SalFrameStyleFlags nSalFrameStyle)816 SalFrame* WinSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
817 {
818 // to switch to Main-Thread
819 HWND hWndParent;
820 if ( pParent )
821 hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
822 else
823 hWndParent = nullptr;
824 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(hWndParent) )));
825 }
826
DestroyFrame(SalFrame * pFrame)827 void WinSalInstance::DestroyFrame( SalFrame* pFrame )
828 {
829 OpenGLContext::prepareForYield();
830 SendMessageW( mhComWnd, SAL_MSG_DESTROYFRAME, 0, reinterpret_cast<LPARAM>(pFrame) );
831 }
832
CreateObject(SalFrame * pParent,SystemWindowData *,bool)833 SalObject* WinSalInstance::CreateObject( SalFrame* pParent,
834 SystemWindowData* /*pWindowData*/, // SystemWindowData meaningless on Windows
835 bool /*bShow*/ )
836 {
837 // to switch to Main-Thread
838 return reinterpret_cast<SalObject*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEOBJECT, 0, reinterpret_cast<LPARAM>(static_cast<WinSalFrame*>(pParent)) )));
839 }
840
DestroyObject(SalObject * pObject)841 void WinSalInstance::DestroyObject( SalObject* pObject )
842 {
843 SendMessageW( mhComWnd, SAL_MSG_DESTROYOBJECT, 0, reinterpret_cast<LPARAM>(pObject) );
844 }
845
GetConnectionIdentifier()846 OUString WinSalInstance::GetConnectionIdentifier()
847 {
848 return OUString();
849 }
850
851 /** Add a file to the system shells recent document list if there is any.
852 This function may have no effect under Unix because there is no
853 standard API among the different desktop managers.
854
855 @param aFileUrl
856 The file url of the document.
857 */
AddToRecentDocumentList(const OUString & rFileUrl,const OUString &,const OUString & rDocumentService)858 void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
859 {
860 if (Application::IsHeadlessModeEnabled())
861 return;
862
863 OUString system_path;
864 osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
865
866 OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
867
868 if (osl::FileBase::E_None == rc)
869 {
870 IShellItem* pShellItem = nullptr;
871
872 HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
873
874 if ( SUCCEEDED(hr) && pShellItem )
875 {
876 OUString sApplicationName;
877
878 if ( rDocumentService == "com.sun.star.text.TextDocument" ||
879 rDocumentService == "com.sun.star.text.GlobalDocument" ||
880 rDocumentService == "com.sun.star.text.WebDocument" ||
881 rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
882 sApplicationName = "Writer";
883 else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
884 rDocumentService == "com.sun.star.chart2.ChartDocument" )
885 sApplicationName = "Calc";
886 else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
887 sApplicationName = "Impress";
888 else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
889 sApplicationName = "Draw";
890 else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
891 sApplicationName = "Math";
892 else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
893 rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
894 rDocumentService == "com.sun.star.sdb.RelationDesign" ||
895 rDocumentService == "com.sun.star.sdb.QueryDesign" ||
896 rDocumentService == "com.sun.star.sdb.TableDesign" ||
897 rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
898 sApplicationName = "Base";
899
900 if ( !sApplicationName.isEmpty() )
901 {
902 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplicationName);
903
904 SHARDAPPIDINFO info;
905 info.psi = pShellItem;
906 info.pszAppID = o3tl::toW(sApplicationID.getStr());
907
908 SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
909 return;
910 }
911 }
912 // For whatever reason, we could not use the SHARD_APPIDINFO semantics
913 SHAddToRecentDocs(SHARD_PATHW, system_path.getStr());
914 }
915 }
916
CreateSalTimer()917 SalTimer* WinSalInstance::CreateSalTimer()
918 {
919 return new WinSalTimer();
920 }
921
CreateSalBitmap()922 std::shared_ptr<SalBitmap> WinSalInstance::CreateSalBitmap()
923 {
924 #if HAVE_FEATURE_SKIA
925 if (SkiaHelper::isVCLSkiaEnabled())
926 return std::make_shared<SkiaSalBitmap>();
927 else
928 #endif
929 return std::make_shared<WinSalBitmap>();
930 }
931
WorkaroundExceptionHandlingInUSER32Lib(int,LPEXCEPTION_POINTERS pExceptionInfo)932 int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo)
933 {
934 // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
935 // Depending on this information we pass process violations directly to our signal handler ...
936 // and c++ (UNO) exceptions are sended to the following code on the current stack.
937 // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
938 // see also #112221#
939
940 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
941
942 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
943 return EXCEPTION_CONTINUE_SEARCH;
944
945 return UnhandledExceptionFilter( pExceptionInfo );
946 }
947
948 typedef LONG NTSTATUS;
949 typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
950 constexpr NTSTATUS STATUS_SUCCESS = 0x00000000;
951
getWinArch()952 static OUString getWinArch()
953 {
954 USHORT nNativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
955
956 using LPFN_ISWOW64PROCESS2 = BOOL(WINAPI*)(HANDLE, USHORT*, USHORT*);
957 auto fnIsWow64Process2 = reinterpret_cast<LPFN_ISWOW64PROCESS2>(
958 GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "IsWow64Process2"));
959 if (fnIsWow64Process2)
960 fnIsWow64Process2(GetCurrentProcess(), &o3tl::temporary(USHORT()), &nNativeMachine);
961
962 if (nNativeMachine == IMAGE_FILE_MACHINE_UNKNOWN)
963 {
964 #if _WIN64
965
966 nNativeMachine = IMAGE_FILE_MACHINE_AMD64;
967
968 #else
969
970 BOOL isWow64 = FALSE;
971
972 IsWow64Process(GetCurrentProcess(), &isWow64);
973
974 if (isWow64)
975 nNativeMachine = IMAGE_FILE_MACHINE_AMD64; // 32-bit process on 64-bit Windows
976 else
977 nNativeMachine = IMAGE_FILE_MACHINE_I386;
978
979 #endif
980 }
981
982 switch (nNativeMachine)
983 {
984 case IMAGE_FILE_MACHINE_I386:
985 return u" X86_32"_ustr;
986 case IMAGE_FILE_MACHINE_R3000:
987 return u" R3000"_ustr;
988 case IMAGE_FILE_MACHINE_R4000:
989 return u" R4000"_ustr;
990 case IMAGE_FILE_MACHINE_R10000:
991 return u" R10000"_ustr;
992 case IMAGE_FILE_MACHINE_WCEMIPSV2:
993 return u" WCEMIPSV2"_ustr;
994 case IMAGE_FILE_MACHINE_ALPHA:
995 return u" ALPHA"_ustr;
996 case IMAGE_FILE_MACHINE_SH3:
997 return u" SH3"_ustr;
998 case IMAGE_FILE_MACHINE_SH3DSP:
999 return u" SH3DSP"_ustr;
1000 case IMAGE_FILE_MACHINE_SH3E:
1001 return u" SH3E"_ustr;
1002 case IMAGE_FILE_MACHINE_SH4:
1003 return u" SH4"_ustr;
1004 case IMAGE_FILE_MACHINE_SH5:
1005 return u" SH5"_ustr;
1006 case IMAGE_FILE_MACHINE_ARM:
1007 return u" ARM"_ustr;
1008 case IMAGE_FILE_MACHINE_THUMB:
1009 return u" THUMB"_ustr;
1010 case IMAGE_FILE_MACHINE_ARMNT:
1011 return u" ARMNT"_ustr;
1012 case IMAGE_FILE_MACHINE_AM33:
1013 return u" AM33"_ustr;
1014 case IMAGE_FILE_MACHINE_POWERPC:
1015 return u" POWERPC"_ustr;
1016 case IMAGE_FILE_MACHINE_POWERPCFP:
1017 return u" POWERPCFP"_ustr;
1018 case IMAGE_FILE_MACHINE_IA64:
1019 return u" IA64"_ustr;
1020 case IMAGE_FILE_MACHINE_MIPS16:
1021 return u" MIPS16"_ustr;
1022 case IMAGE_FILE_MACHINE_ALPHA64:
1023 return u" ALPHA64"_ustr;
1024 case IMAGE_FILE_MACHINE_MIPSFPU:
1025 return u" MIPSFPU"_ustr;
1026 case IMAGE_FILE_MACHINE_MIPSFPU16:
1027 return u" MIPSFPU16"_ustr;
1028 case IMAGE_FILE_MACHINE_TRICORE:
1029 return u" TRICORE"_ustr;
1030 case IMAGE_FILE_MACHINE_CEF:
1031 return u" CEF"_ustr;
1032 case IMAGE_FILE_MACHINE_EBC:
1033 return u" EBC"_ustr;
1034 case IMAGE_FILE_MACHINE_AMD64:
1035 return u" X86_64"_ustr;
1036 case IMAGE_FILE_MACHINE_M32R:
1037 return u" M32R"_ustr;
1038 case IMAGE_FILE_MACHINE_ARM64:
1039 return u" ARM64"_ustr;
1040 case IMAGE_FILE_MACHINE_CEE:
1041 return u" CEE"_ustr;
1042 default:
1043 assert(!"Yet unhandled case");
1044 return OUString();
1045 }
1046 }
1047
getOSVersionString(const OUString & aNtVersionString,DWORD nBuildNumber)1048 static OUString getOSVersionString(const OUString& aNtVersionString, DWORD nBuildNumber)
1049 {
1050 OUStringBuffer result = u"Windows";
1051 if (aNtVersionString == "6.1")
1052 result.append(" 7 Service Pack 1");
1053 else if (aNtVersionString == "6.2")
1054 result.append(" 8");
1055 else if (aNtVersionString == "6.3")
1056 result.append(" 8.1");
1057 else if (aNtVersionString == "10.0")
1058 {
1059 if (nBuildNumber >= 22000)
1060 result.append(" 11");
1061 else
1062 result.append(" 10");
1063 }
1064 else // We don't know what Windows it is
1065 result.append(" unknown");
1066
1067 result.append(getWinArch());
1068
1069 if (!aNtVersionString.isEmpty() || nBuildNumber)
1070 {
1071 result.append(" (");
1072 if (!aNtVersionString.isEmpty())
1073 {
1074 result.append(aNtVersionString);
1075 if (nBuildNumber)
1076 result.append(" ");
1077 }
1078 if (nBuildNumber)
1079 result.append("build " + OUString::number(nBuildNumber));
1080 result.append(")");
1081 }
1082
1083 return result.makeStringAndClear();
1084 }
1085
getOSVersion()1086 OUString WinSalInstance::getOSVersion()
1087 {
1088 static const OUString result = []
1089 {
1090 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
1091 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
1092 // Another approach would be to use NetWkstaGetInfo, but that has some small
1093 // reported delays (some milliseconds), and might get slower in domains with
1094 // poor network connections.
1095 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
1096 bool bHaveVerFromKernel32 = false;
1097 OUString aNtVersion;
1098 if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
1099 {
1100 wchar_t szPath[MAX_PATH];
1101 DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
1102 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
1103 {
1104 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
1105 if (dwCount != 0)
1106 {
1107 std::unique_ptr<char[]> ver(new char[dwCount]);
1108 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
1109 {
1110 void* pBlock = nullptr;
1111 UINT dwBlockSz = 0;
1112 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE
1113 && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
1114 {
1115 VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
1116 aNtVersion = (OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
1117 + OUString::number(LOWORD(vi1->dwProductVersionMS)));
1118 bHaveVerFromKernel32 = true;
1119 }
1120 }
1121 }
1122 }
1123 }
1124 // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
1125 // to get build number and SP info
1126 bool bHaveVerFromRtlGetVersion = false;
1127 DWORD nBuildNumber = 0;
1128 if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
1129 {
1130 if (auto RtlGetVersion
1131 = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
1132 {
1133 RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
1134 vi2.dwOSVersionInfoSize = sizeof(vi2);
1135 if (STATUS_SUCCESS == RtlGetVersion(&vi2))
1136 {
1137 if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
1138 aNtVersion = (OUString::number(vi2.dwMajorVersion) + "."
1139 + OUString::number(vi2.dwMinorVersion));
1140 nBuildNumber = vi2.dwBuildNumber;
1141 bHaveVerFromRtlGetVersion = true;
1142 }
1143 }
1144 }
1145 return getOSVersionString(aNtVersion, nBuildNumber);
1146 }();
1147 return result;
1148 }
1149
BeforeAbort(const OUString &,bool)1150 void WinSalInstance::BeforeAbort(const OUString&, bool)
1151 {
1152 ImplFreeSalGDI();
1153 }
1154
ImplCreateDragSource(const SystemEnvData * pSysEnv)1155 css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDragSource(const SystemEnvData* pSysEnv)
1156 {
1157 return vcl::OleDnDHelper(new DragSource(comphelper::getProcessComponentContext()),
1158 reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drag);
1159 }
1160
ImplCreateDropTarget(const SystemEnvData * pSysEnv)1161 css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDropTarget(const SystemEnvData* pSysEnv)
1162 {
1163 return vcl::OleDnDHelper(new DropTarget(comphelper::getProcessComponentContext()),
1164 reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drop);
1165 }
1166
1167 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1168