31#include <systools/win32/comtools.hxx>
33#include <twain/twain.h>
36#define WM_TWAIN_FALLBACK (WM_SHIM_INTERNAL + 0)
40double FixToDouble(
const TW_FIX32& rFix) {
return rFix.Whole + rFix.Frac / 65536.; }
42const wchar_t sTwainWndClass[] = L
"TwainClass";
47 ImpTwain(HANDLE hParentThread);
64 DWORD m_nParentThreadId;
66 DSMENTRYPROC m_pDSM =
nullptr;
67 HMODULE m_hMod =
nullptr;
68 TWAINState m_nCurState = TWAINState::DSMunloaded;
69 HWND m_hTwainWnd =
nullptr;
70 HHOOK m_hTwainHook =
nullptr;
71 HANDLE m_hMap =
nullptr;
73 static bool IsTwainClassWnd(HWND hWnd);
74 static ImpTwain* GetImpFromWnd(HWND hWnd);
75 static void ImplCreateWnd(HWND hWnd, LPARAM lParam);
76 static LRESULT
CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
77 static LRESULT
CALLBACK MsgHook(
int nCode, WPARAM wParam, LPARAM lParam);
83 void NotifyParent(WPARAM nEvent, LPARAM lParam);
84 bool ImplHandleMsg(MSG* pMsg);
85 void ImplOpenSourceManager();
86 void ImplOpenSource();
87 bool ImplEnableSource();
89 void ImplFallback(WPARAM nEvent);
91 void ImplFallbackHdl(WPARAM nEvent);
92 void ImplRequestHdl(WPARAM nRequest);
96bool ImpTwain::IsTwainClassWnd(HWND hWnd)
99 wchar_t sClassName[nBufSize];
100 return (GetClassNameW(hWnd, sClassName, nBufSize) && wcscmp(sClassName, sTwainWndClass) == 0);
104ImpTwain* ImpTwain::GetImpFromWnd(HWND hWnd)
106 if (!IsTwainClassWnd(hWnd))
108 return reinterpret_cast<ImpTwain*
>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
112void ImpTwain::ImplCreateWnd(HWND hWnd, LPARAM lParam)
114 CREATESTRUCT* pCS =
reinterpret_cast<CREATESTRUCT*
>(lParam);
115 if (pCS && IsTwainClassWnd(hWnd))
116 SetWindowLongPtrW(hWnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(pCS->lpCreateParams));
120LRESULT
CALLBACK ImpTwain::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
122 ImpTwain* pImpTwain = GetImpFromWnd(hWnd);
126 ImplCreateWnd(hWnd, lParam);
130 pImpTwain->ImplFallbackHdl(wParam);
134 pImpTwain->ImplRequestHdl(wParam);
137 return DefWindowProcW(hWnd, nMsg, wParam, lParam);
141LRESULT
CALLBACK ImpTwain::MsgHook(
int nCode, WPARAM wParam, LPARAM lParam)
143 MSG* pMsg =
reinterpret_cast<MSG*
>(lParam);
144 if (nCode >= 0 && pMsg)
146 ImpTwain* pImpTwain = GetImpFromWnd(pMsg->hwnd);
147 if (pImpTwain && pImpTwain->ImplHandleMsg(pMsg))
149 pMsg->message = WM_USER;
156 return CallNextHookEx(
nullptr, nCode, wParam, lParam);
159HANDLE GetProcOfThread(HANDLE hThread)
161 DWORD nProcId = GetProcessIdOfThread(hThread);
164 HANDLE hRet = OpenProcess(PROCESS_DUP_HANDLE,
FALSE, nProcId);
170ImpTwain::ImpTwain(HANDLE hParentThread)
179 DG_IMAGE | DG_CONTROL,
183 , m_nParentThreadId(GetThreadId(hParentThread))
184 , m_hProc(GetProcOfThread(hParentThread))
186 WNDCLASSW aWc = { 0, &WndProc, 0,
sizeof(WNDCLASSW), GetModuleHandleW(
nullptr),
187 nullptr,
nullptr,
nullptr,
nullptr, sTwainWndClass };
188 if (!RegisterClassW(&aWc))
190 m_hTwainWnd = CreateWindowExW(WS_EX_TOPMOST, aWc.lpszClassName, L
"TWAIN", 0, 0, 0, 0, 0,
191 HWND_DESKTOP,
nullptr, aWc.hInstance,
this);
194 m_hTwainHook = SetWindowsHookExW(WH_GETMESSAGE, &MsgHook,
nullptr, GetCurrentThreadId());
203 DestroyWindow(m_hTwainWnd);
204 UnhookWindowsHookEx(m_hTwainHook);
207bool ImpTwain::SelectSource()
209 TW_UINT16 nRet = TWRC_FAILURE;
211 ImplOpenSourceManager();
213 if (TWAINState::DSMopened == m_nCurState)
218 aIdent.ProductName[0] =
'\0';
220 nRet = m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &aIdent);
224 return (TWRC_SUCCESS == nRet);
227bool ImpTwain::InitXfer()
231 ImplOpenSourceManager();
233 if (TWAINState::DSMopened == m_nCurState)
237 if (TWAINState::DSopened == m_nCurState)
238 bRet = ImplEnableSource();
247void ImpTwain::ImplOpenSourceManager()
249 if (TWAINState::DSMunloaded == m_nCurState)
251 m_hMod = LoadLibraryW(L
"TWAIN_32.DLL");
255 sal::systools::CoTaskMemAllocated<wchar_t> sPath;
256 if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Windows, 0,
nullptr, &sPath)))
258 std::wstring sPathAndFile(sPath);
259 sPathAndFile += L
"\\TWAIN_32.DLL";
260 m_hMod = LoadLibraryW(sPathAndFile.c_str());
265 m_nCurState = TWAINState::DSMloaded;
267 m_pDSM =
reinterpret_cast<DSMENTRYPROC
>(GetProcAddress(m_hMod,
"DSM_Entry"));
269 && (m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &m_hTwainWnd)
272 m_nCurState = TWAINState::DSMopened;
278void ImpTwain::ImplOpenSource()
280 if (TWAINState::DSMopened == m_nCurState)
282 if ((m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &m_aSrcId)
284 && (m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &m_aSrcId)
288 = { CAP_XFERCOUNT, TWON_ONEVALUE, GlobalAlloc(GHND,
sizeof(TW_ONEVALUE)) };
289 TW_ONEVALUE* pVal =
static_cast<TW_ONEVALUE*
>(GlobalLock(aCap.hContainer));
291 pVal->ItemType = TWTY_INT16;
293 GlobalUnlock(aCap.hContainer);
294 m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &aCap);
295 GlobalFree(aCap.hContainer);
296 m_nCurState = TWAINState::DSopened;
301bool ImpTwain::ImplEnableSource()
305 if (TWAINState::DSopened == m_nCurState)
307 TW_USERINTERFACE aUI = {
true,
true, m_hTwainWnd };
310 m_nCurState = TWAINState::DSenabled;
312 if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI)
320 m_nCurState = TWAINState::DSopened;
327void ImpTwain::NotifyParent(WPARAM nEvent, LPARAM lParam)
329 PostThreadMessageW(m_nParentThreadId,
WM_TWAIN_EVENT, nEvent, lParam);
332bool ImpTwain::ImplHandleMsg(MSG* pMsg)
337 TW_EVENT aEvt = { pMsg, MSG_NULL };
338 TW_UINT16 nRet = m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt);
340 switch (aEvt.TWMessage)
346 if (TWAINState::DSenabled == m_nCurState)
348 m_nCurState = TWAINState::DSreadyToXfer;
354 else if (TWAINState::Xferring == m_nCurState && m_hMap)
361 ImplFallback(nEvent);
370 nRet = TWRC_NOTDSEVENT;
374 return (TWRC_DSEVENT == nRet);
377void ImpTwain::ImplXfer()
379 if (m_nCurState == TWAINState::DSreadyToXfer)
382 HANDLE hDIB =
nullptr;
385 if (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo) == TWRC_SUCCESS)
387 nXRes = FixToDouble(aInfo.XResolution);
388 nYRes = FixToDouble(aInfo.YResolution);
393 switch (m_pDSM(&m_aAppId, &m_aSrcId, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB))
396 m_nCurState = TWAINState::Xferring;
404 const HGLOBAL hGlob =
static_cast<HGLOBAL
>(hDIB);
405 const SIZE_T nDIBSize = GlobalSize(hGlob);
406 const DWORD nMapSize = nDIBSize + 4;
407 if (nMapSize > nDIBSize)
409 if (LPVOID pBmpMem = GlobalLock(hGlob))
411 if ((nXRes > 0) && (nYRes > 0))
414 BITMAPINFOHEADER* pBIH =
static_cast<BITMAPINFOHEADER*
>(pBmpMem);
422 HANDLE hMap = CreateFileMappingW(INVALID_HANDLE_VALUE,
nullptr,
423 PAGE_READWRITE, 0, nMapSize,
nullptr);
426 LPVOID pMap = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, nMapSize);
429 memcpy(pMap, &nMapSize, 4);
430 memcpy(
static_cast<char*
>(pMap) + 4, pBmpMem, nDIBSize);
431 FlushViewOfFile(pMap, nDIBSize);
432 UnmapViewOfFile(pMap);
434 DuplicateHandle(GetCurrentProcess(), hMap, m_hProc, &m_hMap, 0,
435 FALSE, DUPLICATE_SAME_ACCESS);
446 GlobalFree(
static_cast<HGLOBAL
>(hDIB));
448 m_nCurState = TWAINState::Xferring;
458void ImpTwain::ImplFallback(WPARAM nEvent)
463void ImpTwain::ImplFallbackHdl(WPARAM nEvent)
465 bool bFallback =
true;
469 case TWAINState::Xferring:
470 case TWAINState::DSreadyToXfer:
472 TW_PENDINGXFERS aXfers;
474 if (m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, &aXfers)
477 if (aXfers.Count != 0)
478 m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, &aXfers);
481 m_nCurState = TWAINState::DSenabled;
485 case TWAINState::DSenabled:
487 TW_USERINTERFACE aUI = {
true,
true, m_hTwainWnd };
489 m_pDSM(&m_aAppId, &m_aSrcId, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI);
490 m_nCurState = TWAINState::DSopened;
494 case TWAINState::DSopened:
496 m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &m_aSrcId);
497 m_nCurState = TWAINState::DSMopened;
501 case TWAINState::DSMopened:
503 m_pDSM(&m_aAppId,
nullptr, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &m_hTwainWnd);
504 m_nCurState = TWAINState::DSMloaded;
508 case TWAINState::DSMloaded:
513 m_nCurState = TWAINState::DSMunloaded;
517 case TWAINState::DSMunloaded:
520 NotifyParent(nEvent,
reinterpret_cast<LPARAM
>(m_hMap));
529 ImplFallback(nEvent);
532void ImpTwain::ImplRequestHdl(WPARAM nRequest)
549int WINAPI
wWinMain(HINSTANCE, HINSTANCE, LPWSTR,
int)
552 LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
557 HANDLE hParentThread =
reinterpret_cast<HANDLE
>(wcstoul(argv[1],
nullptr, 10));
565 ImpTwain aImpTwain(hParentThread);
571 = MsgWaitForMultipleObjects(1, &hParentThread,
FALSE, INFINITE, QS_ALLINPUT);
572 if (nWaitResult == WAIT_OBJECT_0)
574 if (nWaitResult == WAIT_FAILED)
578 while (PeekMessageW(&msg,
nullptr, 0, 0, PM_REMOVE))
581 if (msg.message == WM_QUIT)
588 TranslateMessage(&msg);
589 DispatchMessageW(&msg);
596 catch (
const std::exception& e)
598 printf(
"Exception thrown: %s", e.what());
#define SAL_N_ELEMENTS(arr)
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
#define WM_TWAIN_FALLBACK
#define TWAIN_REQUEST_SELECTSOURCE
void ThrowLastError(const char *sFunc)
#define TWAIN_EVENT_REQUESTRESULT
#define TWAIN_REQUEST_INITXFER
#define TWAIN_EVENT_NOTIFYHWND
#define TWAIN_REQUEST_QUIT
#define TWAIN_EVENT_SCANNING