VeraCrypt
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMounir IDRASSI <mounir.idrassi@idrix.fr>2020-07-06 18:00:25 +0200
committerMounir IDRASSI <mounir.idrassi@idrix.fr>2020-07-06 18:18:47 +0200
commitff391d9a6afb856e20e827edbad0aff9e6caffe3 (patch)
treee754e62b246821b7ebf222de94e6785b724776c7
parentb54729de48020ca18faa82a92e95bcc130b454ff (diff)
downloadVeraCrypt-ff391d9a6afb856e20e827edbad0aff9e6caffe3.tar.gz
VeraCrypt-ff391d9a6afb856e20e827edbad0aff9e6caffe3.zip
Windows: Support direct password drag-n-drop from external applications (e.g. KeePass) which is more secure than using clipboard.
-rw-r--r--src/Common/Dlgcode.c422
-rw-r--r--src/Common/Dlgcode.h55
-rw-r--r--src/ExpandVolume/WinMain.cpp24
-rw-r--r--src/Format/Tcformat.c28
-rw-r--r--src/Mount/Mount.c70
5 files changed, 597 insertions, 2 deletions
diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c
index bd1f6f84..0203a931 100644
--- a/src/Common/Dlgcode.c
+++ b/src/Common/Dlgcode.c
@@ -11638,6 +11638,17 @@ BOOL CALLBACK SecurityTokenPasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wPara
SetForegroundWindow (hwndDlg);
SetFocus (GetDlgItem (hwndDlg, IDC_TOKEN_PASSWORD));
+
+ if (!bSecureDesktopOngoing)
+ {
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
+ }
}
return 0;
@@ -11673,6 +11684,19 @@ BOOL CALLBACK SecurityTokenPasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wPara
EndDialog (hwndDlg, lw);
}
return 1;
+
+ case WM_NCDESTROY:
+ {
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
}
return 0;
@@ -14474,3 +14498,401 @@ BitLockerEncryptionStatus GetBitLockerEncryptionStatus(WCHAR driveLetter)
CoUninitialize();
return blStatus;
}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static CLIPFORMAT g_supportedFormats[] = { CF_UNICODETEXT, CF_TEXT, CF_OEMTEXT};
+
+//*************************************************************
+// GenericDropTarget
+//*************************************************************
+GenericDropTarget::GenericDropTarget(CLIPFORMAT* pFormats, size_t count)
+ : m_DropTargetWnd(NULL),
+ m_dwRefCount(1),
+ m_KeyState(0L),
+ m_Data(NULL)
+{
+ m_DropPoint.x = 0;
+ m_DropPoint.y = 0;
+
+ if (pFormats && count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ m_SupportedFormat.push_back (pFormats[i]);
+ }
+ }
+}
+
+GenericDropTarget::~GenericDropTarget()
+{
+}
+
+HRESULT GenericDropTarget::QueryInterface(REFIID iid, void **ppvObject)
+{
+ if(ppvObject == NULL)
+ return E_FAIL;
+
+ if (iid == IID_IUnknown)
+ {
+ AddRef();
+ (*ppvObject) = this;
+ return S_OK;
+ }
+ // compare guids fast and dirty
+ if (IsEqualGUID (iid, IID_IDropTarget))
+ {
+ AddRef();
+ (*ppvObject) = this;
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+ULONG GenericDropTarget::AddRef(void)
+{
+ return (ULONG) InterlockedIncrement (&m_dwRefCount);
+}
+
+ULONG GenericDropTarget::Release(void)
+{
+ if (InterlockedDecrement (&m_dwRefCount) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ else
+ return (ULONG) m_dwRefCount;
+}
+
+//*************************************************************
+// Register
+// Called by whom implements us so we can serve
+//*************************************************************
+BOOL GenericDropTarget::Register(HWND hWnd)
+{
+ if(NULL == hWnd)
+ return E_FAIL;
+
+ OleInitialize(NULL);
+
+ // required: these MUST be strong locked
+ CoLockObjectExternal(this, TRUE, 0);
+
+ // this is ok, we have it
+ DWORD hRes = ::RegisterDragDrop(hWnd, this);
+ if(SUCCEEDED(hRes))
+ {
+ // keep
+ m_DropTargetWnd = hWnd;
+ return TRUE;
+ }
+
+ // unlock
+ CoLockObjectExternal(this, FALSE, 0);
+
+ // bye bye COM
+ OleUninitialize();
+
+ // wont accept data now
+ return FALSE;
+}
+
+//*************************************************************
+// Revoke
+// Unregister us as a target
+//*************************************************************
+void GenericDropTarget::Revoke()
+{
+ if(NULL == m_DropTargetWnd)
+ return;
+
+ RevokeDragDrop(m_DropTargetWnd);
+
+ m_DropTargetWnd = NULL;
+
+ // unlock
+ CoLockObjectExternal(this, FALSE, 0);
+
+ // bye bye COM
+ OleUninitialize();
+}
+
+//*************************************************************
+// DragEnter
+//*************************************************************
+HRESULT GenericDropTarget::DragEnter(struct IDataObject *pDataObject, unsigned long grfKeyState, struct _POINTL pMouse, unsigned long * pDropEffect)
+{
+ if(pDataObject == NULL)
+ return E_FAIL; // must have data
+
+ // keep point
+ m_DropPoint.x = pMouse.x;
+ m_DropPoint.y = pMouse.y;
+
+ // keep key
+ m_KeyState = grfKeyState;
+
+ // call top
+ *pDropEffect = GotEnter();
+
+ return S_OK;
+}
+
+//*************************************************************
+// DragOver
+// Coming over!
+//*************************************************************
+HRESULT GenericDropTarget::DragOver(unsigned long grfKeyState, struct _POINTL pMouse, unsigned long *pEffect)
+{
+ // keep point
+ m_DropPoint.x = pMouse.x;
+ m_DropPoint.y = pMouse.y;
+
+ // keep key
+ m_KeyState = grfKeyState;
+
+ // call top
+ *pEffect = GotDrag();
+
+ return S_OK;
+}
+
+//*************************************************************
+// DragLeave
+// Free! At last!
+//*************************************************************
+HRESULT GenericDropTarget::DragLeave(void)
+{
+ GotLeave();
+
+ return S_OK;
+}
+
+//*************************************************************
+// Drop
+//*************************************************************
+HRESULT GenericDropTarget::Drop(struct IDataObject *pDataObject, unsigned long grfKeyState, struct _POINTL pMouse, unsigned long *pdwEffect)
+{
+ if(NULL == pDataObject)
+ return E_FAIL;
+
+ // do final effect
+ *pdwEffect = DROPEFFECT_COPY;
+
+ // Check the data
+ FORMATETC iFormat;
+ ZeroMemory(&iFormat, sizeof(FORMATETC));
+
+ STGMEDIUM iMedium;
+ ZeroMemory(&iMedium, sizeof(STGMEDIUM));
+
+ HRESULT hRes;
+ size_t i;
+ bool bFound = false;
+
+ for (i = 0; i < m_SupportedFormat.size(); i++)
+ {
+ // data
+ iFormat.cfFormat = m_SupportedFormat[i];
+ iFormat.dwAspect = DVASPECT_CONTENT;
+ iFormat.lindex = -1; // give me all baby
+ iFormat.tymed = TYMED_HGLOBAL; // want mem
+
+ hRes = pDataObject->GetData(&iFormat, &iMedium);
+ if(SUCCEEDED(hRes))
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ return hRes;
+
+ // we have the data, get it
+ BYTE *iMem = (BYTE *)::GlobalLock(iMedium.hGlobal);
+
+ // pass over
+ m_Data = iMem;
+
+ // keep point
+ m_DropPoint.x = pMouse.x;
+ m_DropPoint.y = pMouse.y;
+
+ // keep key
+ m_KeyState = grfKeyState;
+
+ // notify parent of drop
+ GotDrop(m_SupportedFormat[i]);
+
+ ::GlobalUnlock(iMedium.hGlobal);
+
+ // free data
+ if(iMedium.pUnkForRelease != NULL)
+ iMedium.pUnkForRelease->Release();
+
+ return S_OK;
+}
+
+//*************************************************************
+// Stub implementation
+// Real stuff would be done in parent
+//*************************************************************
+void GenericDropTarget::GotDrop(CLIPFORMAT format)
+{
+}
+
+DWORD GenericDropTarget::GotDrag(void)
+{
+ return DROPEFFECT_LINK;
+}
+
+void GenericDropTarget::GotLeave(void)
+{
+}
+
+DWORD GenericDropTarget::GotEnter(void)
+{
+ return DROPEFFECT_LINK;
+}
+
+// ************************************************************
+// PasswordEditDropTarget
+// Constructor
+// ************************************************************
+PasswordEditDropTarget::PasswordEditDropTarget() : GenericDropTarget (g_supportedFormats, ARRAYSIZE (g_supportedFormats))
+{
+
+}
+
+// ************************************************************
+// GotDrag
+
+// ************************************************************
+DWORD PasswordEditDropTarget::GotDrag(void)
+{
+ return GotEnter();
+}
+
+// ************************************************************
+// GotLeave
+// ************************************************************
+void PasswordEditDropTarget::GotLeave(void)
+{
+}
+
+// ************************************************************
+// GotEnter
+// ************************************************************
+DWORD PasswordEditDropTarget::GotEnter(void)
+{
+ TCHAR szClassName[64];
+ DWORD dwStyles;
+ int maxLen;
+ HWND hChild = WindowFromPoint (m_DropPoint);
+ // check that we are on password edit control (we use maximum length to correctly identify password fields since they don't always have ES_PASSWORD style (if the the user checked show password)
+ if (hChild && GetClassName (hChild, szClassName, ARRAYSIZE (szClassName)) && (0 == _tcsicmp (szClassName, _T("EDIT")))
+ && (dwStyles = GetWindowLong (hChild, GWL_STYLE)) && !(dwStyles & ES_NUMBER)
+ && (maxLen = (int) SendMessage (hChild, EM_GETLIMITTEXT, 0, 0)) && (maxLen == MAX_PASSWORD || maxLen == MAX_LEGACY_PASSWORD)
+ )
+ {
+ return DROPEFFECT_COPY;
+ }
+
+ return DROPEFFECT_LINK;
+}
+
+// ************************************************************
+// GotDrop
+// Called if we have a drop text drop here.
+//
+// ************************************************************
+void PasswordEditDropTarget::GotDrop(CLIPFORMAT format)
+{
+ // value contains the material itself
+ if(m_Data)
+ {
+ TCHAR szClassName[64];
+ DWORD dwStyles;
+ int maxLen;
+ HWND hChild = WindowFromPoint (m_DropPoint);
+ if (hChild && GetClassName (hChild, szClassName, ARRAYSIZE (szClassName)) && (0 == _tcsicmp (szClassName, _T("EDIT")))
+ && (dwStyles = GetWindowLong (hChild, GWL_STYLE)) && !(dwStyles & ES_NUMBER)
+ && (maxLen = (int) SendMessage (hChild, EM_GETLIMITTEXT, 0, 0)) && (maxLen == MAX_PASSWORD || maxLen == MAX_LEGACY_PASSWORD)
+ )
+ {
+ WCHAR* wszText;
+ int wlen;
+ bool bFree = false;
+ // get the text
+ if (format == CF_UNICODETEXT)
+ {
+ wszText = (WCHAR *)m_Data;
+ }
+ else
+ {
+ char *iText = (char *)m_Data;
+ wlen = MultiByteToWideChar ((format == CF_OEMTEXT)? CP_OEMCP : CP_ACP, 0, iText, -1, NULL, 0);
+ wszText = new WCHAR[wlen];
+ if (wszText)
+ {
+ wlen = MultiByteToWideChar (CP_ACP, 0, iText, -1, wszText, wlen);
+ bFree = true;
+ }
+ }
+
+ WCHAR* pchData = wszText;
+ int txtlen = 0;
+ bool bTruncated = false;
+
+ // remove any appended \r or \n
+ while (*pchData)
+ {
+ if (*pchData == '\r' || *pchData == '\n')
+ break;
+ else
+ {
+ txtlen++;
+ pchData++;
+ }
+ }
+
+ if (txtlen)
+ {
+ if (txtlen > maxLen)
+ {
+ bTruncated = true;
+ txtlen = maxLen;
+ }
+
+ SetFocus (hChild);
+
+ wszText[txtlen] = 0;
+ SetWindowText(hChild , wszText);
+
+ if (bTruncated)
+ {
+ EDITBALLOONTIP ebt;
+
+ ebt.cbStruct = sizeof( EDITBALLOONTIP );
+ ebt.pszText = GetString ("PASSWORD_PASTED_TRUNCATED");
+ ebt.pszTitle = lpszTitle;
+ ebt.ttiIcon = TTI_WARNING_LARGE; // tooltip warning icon
+
+ SendMessage(hChild, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt);
+
+ MessageBeep (0xFFFFFFFF);
+ }
+ }
+
+ if (bFree)
+ {
+ burn (wszText, wlen * sizeof (WCHAR));
+ delete [] wszText;
+ }
+ }
+ }
+}
+
diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h
index 9e77c0a9..52d94f10 100644
--- a/src/Common/Dlgcode.h
+++ b/src/Common/Dlgcode.h
@@ -691,6 +691,61 @@ typedef void (CALLBACK* WaitThreadProc)(void* pArg, HWND hWaitDlg);
void BringToForeground(HWND hWnd);
void ShowWaitDialog(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, void* pArg);
+// classes used to implement support for password drag-n-drop from KeePass Password Safe
+// Implementation based the following source code with many modifications to fix isses and add features
+// URL: https://www.codeguru.com/cpp/misc/misc/draganddrop/article.php/c349/Drag-And-Drop-between-Window-Controls.htm
+
+interface GenericDropTarget : public IDropTarget
+{
+public:
+ GenericDropTarget(CLIPFORMAT* pFormats, size_t count);
+ ~GenericDropTarget();
+
+ // basic IUnknown stuff
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);
+ ULONG STDMETHODCALLTYPE AddRef(void);
+ ULONG STDMETHODCALLTYPE Release(void);
+
+ HRESULT STDMETHODCALLTYPE DragEnter(struct IDataObject *,unsigned long,struct _POINTL,unsigned long *);
+ HRESULT STDMETHODCALLTYPE DragOver(unsigned long,struct _POINTL,unsigned long *);
+ HRESULT STDMETHODCALLTYPE DragLeave(void);
+ HRESULT STDMETHODCALLTYPE Drop(struct IDataObject *,unsigned long,struct _POINTL,unsigned long *);
+
+ // called by parents
+ BOOL Register(HWND hWnd);
+ void Revoke();
+
+ // call parent we have goodies
+ virtual void GotDrop(CLIPFORMAT format);
+ virtual DWORD GotDrag(void);
+ virtual void GotLeave(void);
+ virtual DWORD GotEnter(void);
+public:
+ BYTE *m_Data;
+
+ POINT m_DropPoint;
+
+ DWORD m_KeyState;
+
+protected:
+ HWND m_DropTargetWnd;
+ std::vector<CLIPFORMAT> m_SupportedFormat;
+ volatile LONG m_dwRefCount;
+};
+
+class PasswordEditDropTarget : public GenericDropTarget
+{
+public:
+ PasswordEditDropTarget();
+
+ // called by child we have drop
+ void GotDrop(CLIPFORMAT format);
+ DWORD GotDrag(void);
+ void GotLeave(void);
+ DWORD GotEnter(void);
+};
+
+
#endif // __cplusplus
#endif // TC_HEADER_DLGCODE
diff --git a/src/ExpandVolume/WinMain.cpp b/src/ExpandVolume/WinMain.cpp
index 10c1af40..49422319 100644
--- a/src/ExpandVolume/WinMain.cpp
+++ b/src/ExpandVolume/WinMain.cpp
@@ -490,6 +490,17 @@ BOOL CALLBACK ExtcvPasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARA
SetWindowPos (hwndDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
+
+ if (!bSecureDesktopOngoing)
+ {
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
+ }
}
return 0;
@@ -782,6 +793,19 @@ BOOL CALLBACK ExtcvPasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARA
DragFinish (hdrop);
}
return 1;
+
+ case WM_NCDESTROY:
+ {
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
}
return 0;
diff --git a/src/Format/Tcformat.c b/src/Format/Tcformat.c
index dd999f0c..ab7b165b 100644
--- a/src/Format/Tcformat.c
+++ b/src/Format/Tcformat.c
@@ -420,8 +420,8 @@ static void WipePasswordsAndKeyfiles (bool bFull)
// Attempt to wipe passwords stored in the input field buffers
wmemset (tmp, L'X', MAX_PASSWORD);
tmp [MAX_PASSWORD] = 0;
- SetWindowText (hPasswordInputField, tmp);
- SetWindowText (hVerifyPasswordInputField, tmp);
+ SetWindowText (hPasswordInputField, tmp);
+ SetWindowText (hVerifyPasswordInputField, tmp);
burn (&szVerify[0], sizeof (szVerify));
burn (&volumePassword, sizeof (volumePassword));
@@ -6372,6 +6372,14 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa
strcpy (szRawPassword, "q");
#endif
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
+
PostMessage (hwndDlg, TC_APPMSG_PERFORM_POST_WMINIT_TASKS, 0, 0);
}
return 0;
@@ -8995,6 +9003,22 @@ ovf_end:
case WM_CLOSE:
PostMessage (hwndDlg, TC_APPMSG_FORMAT_USER_QUIT, 0, 0);
return 1;
+
+ case WM_NCDESTROY:
+ {
+ hPasswordInputField = NULL;
+ hVerifyPasswordInputField = NULL;
+
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
}
return 0;
diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c
index 59ef7ba2..30714560 100644
--- a/src/Mount/Mount.c
+++ b/src/Mount/Mount.c
@@ -2415,6 +2415,17 @@ BOOL CALLBACK PasswordChangeDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPAR
}
CheckCapsLock (hwndDlg, FALSE);
+
+ if (!bSecureDesktopOngoing)
+ {
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
+ }
return 0;
}
@@ -2880,6 +2891,19 @@ err:
return 1;
}
return 0;
+
+ case WM_NCDESTROY:
+ {
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
}
return 0;
@@ -3014,9 +3038,18 @@ BOOL CALLBACK PasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPa
SetFocus (GetDlgItem (hwndDlg, IDC_PASSWORD));
/* Start the timer to check if we are foreground only if Secure Desktop is not used */
+ /* Implement Text drag-n-drop in order to support droping password from KeePass directly only if Secure Desktop is not used */
if (!bSecureDesktopOngoing)
{
SetTimer (hwndDlg, TIMER_ID_CHECK_FOREGROUND, TIMER_INTERVAL_CHECK_FOREGROUND, NULL);
+
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
}
}
return 0;
@@ -3281,6 +3314,19 @@ BOOL CALLBACK PasswordDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lPa
}
return 0;
+ case WM_NCDESTROY:
+ {
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
+
case WM_CONTEXTMENU:
{
RECT buttonRect;
@@ -3694,6 +3740,17 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM
ToHyperlink (hwndDlg, IDC_LINK_HIDVOL_PROTECTION_INFO);
+ if (!bSecureDesktopOngoing)
+ {
+ PasswordEditDropTarget* pTarget = new PasswordEditDropTarget ();
+ if (pTarget->Register (hwndDlg))
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) pTarget);
+ }
+ else
+ delete pTarget;
+ }
+
}
return 0;
@@ -3851,6 +3908,19 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM
}
return 0;
+
+ case WM_NCDESTROY:
+ {
+ /* unregister drap-n-drop support */
+ PasswordEditDropTarget* pTarget = (PasswordEditDropTarget*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (pTarget)
+ {
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) 0);
+ pTarget->Revoke ();
+ pTarget->Release();
+ }
+ }
+ return 0;
}
return 0;