/* * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include #include #include #include "awt_DataTransferer.h" #include "java_awt_dnd_DnDConstants.h" #include "sun_awt_windows_WDropTargetContextPeer.h" #include "awt_Container.h" #include "awt_ole.h" #include "awt_Toolkit.h" #include "awt_DnDDT.h" #include "awt_DnDDS.h" // forwards extern "C" { DWORD __cdecl convertActionsToDROPEFFECT(jint actions); jint __cdecl convertDROPEFFECTToActions(DWORD effects); DWORD __cdecl mapModsToDROPEFFECT(DWORD, DWORD); } // extern "C" IDataObject* AwtDropTarget::sm_pCurrentDnDDataObject = (IDataObject*)NULL; /** * constructor */ AwtDropTarget::AwtDropTarget(JNIEnv* env, AwtComponent* component) { m_component = component; m_window = component->GetHWnd(); m_refs = 1U; m_target = env->NewGlobalRef(component->GetTarget(env)); m_registered = 0; m_dataObject = NULL; m_formats = NULL; m_nformats = 0; m_dtcp = NULL; m_cfFormats = NULL; m_mutex = ::CreateMutex(NULL, FALSE, NULL); m_pIDropTargetHelper = NULL; } /** * destructor */ AwtDropTarget::~AwtDropTarget() { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); // fix for 6212440: on application shutdown, this object's // destruction might be suppressed due to dangling COM references. // On destruction, VM might be shut down already, so we should make // a null check on env. if (env) { env->DeleteGlobalRef(m_target); env->DeleteGlobalRef(m_dtcp); } ::CloseHandle(m_mutex); UnloadCache(); } /** * QueryInterface */ HRESULT __stdcall AwtDropTarget::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) { if ( IID_IUnknown == riid || IID_IDropTarget == riid ) { *ppvObject = static_cast(this); AddRef(); return S_OK; } *ppvObject = NULL; return E_NOINTERFACE; } /** * AddRef */ ULONG __stdcall AwtDropTarget::AddRef() { return (ULONG)++m_refs; } /** * Release */ ULONG __stdcall AwtDropTarget::Release() { int refs; if ((refs = --m_refs) == 0) delete this; return (ULONG)refs; } void ScaleDown(POINT &cp, HWND m_window) { int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(m_window); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); if (device) { cp.x = device->ScaleDownX(cp.x); cp.y = device->ScaleDownY(cp.y); } } /** * DragEnter */ HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) { TRY; if (NULL != m_pIDropTargetHelper) { m_pIDropTargetHelper->DragEnter( m_window, pDataObj, (LPPOINT)&pt, *pdwEffect); } AwtInterfaceLocker _lk(this); JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); HRESULT ret = S_OK; DWORD retEffect = DROPEFFECT_NONE; jobject dtcp = NULL; if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(NULL)) || (IsLocalDnD() && !IsLocalDataObject(pDataObj))) { *pdwEffect = retEffect; return ret; } dtcp = call_dTCcreate(env); if (dtcp) { env->DeleteGlobalRef(m_dtcp); m_dtcp = env->NewGlobalRef(dtcp); env->DeleteLocalRef(dtcp); } if (JNU_IsNull(env, m_dtcp) || !JNU_IsNull(env, safe_ExceptionOccurred(env))) { return ret; } LoadCache(pDataObj); { POINT cp; RECT wr; ::GetWindowRect(m_window, &wr); cp.x = pt.x - wr.left; cp.y = pt.y - wr.top; ScaleDown(cp, m_window); jint actions = call_dTCenter(env, m_dtcp, m_target, (jint)cp.x, (jint)cp.y, ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)), ::convertDROPEFFECTToActions(*pdwEffect), m_cfFormats, (jlong)this); try { if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { env->ExceptionDescribe(); env->ExceptionClear(); actions = java_awt_dnd_DnDConstants_ACTION_NONE; } } catch (std::bad_alloc&) { retEffect = ::convertActionsToDROPEFFECT(actions); *pdwEffect = retEffect; throw; } retEffect = ::convertActionsToDROPEFFECT(actions); } *pdwEffect = retEffect; return ret; CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY); } /** * DragOver */ HRESULT __stdcall AwtDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) { TRY; if (NULL != m_pIDropTargetHelper) { m_pIDropTargetHelper->DragOver( (LPPOINT)&pt, *pdwEffect ); } AwtInterfaceLocker _lk(this); JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); HRESULT ret = S_OK; POINT cp; RECT wr; jint actions; if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(m_dataObject)) || (IsLocalDnD() && !IsLocalDataObject(m_dataObject))) { *pdwEffect = DROPEFFECT_NONE; return ret; } ::GetWindowRect(m_window, &wr); cp.x = pt.x - wr.left; cp.y = pt.y - wr.top; ScaleDown(cp, m_window); actions = call_dTCmotion(env, m_dtcp, m_target,(jint)cp.x, (jint)cp.y, ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)), ::convertDROPEFFECTToActions(*pdwEffect), m_cfFormats, (jlong)this); try { if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { env->ExceptionDescribe(); env->ExceptionClear(); actions = java_awt_dnd_DnDConstants_ACTION_NONE; } } catch (std::bad_alloc&) { *pdwEffect = ::convertActionsToDROPEFFECT(actions); throw; } *pdwEffect = ::convertActionsToDROPEFFECT(actions); return ret; CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY); } /** * DragLeave */ HRESULT __stdcall AwtDropTarget::DragLeave() { TRY_NO_VERIFY; if (NULL != m_pIDropTargetHelper) { m_pIDropTargetHelper->DragLeave(); } AwtInterfaceLocker _lk(this); JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); HRESULT ret = S_OK; if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(m_dataObject)) || (IsLocalDnD() && !IsLocalDataObject(m_dataObject))) { DragCleanup(); return ret; } call_dTCexit(env, m_dtcp, m_target, (jlong)this); try { if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { env->ExceptionDescribe(); env->ExceptionClear(); } } catch (std::bad_alloc&) { DragCleanup(); throw; } DragCleanup(); return ret; CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY); } /** * Drop */ HRESULT __stdcall AwtDropTarget::Drop(IDataObject __RPC_FAR *pDataObj, DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) { TRY; if (NULL != m_pIDropTargetHelper) { m_pIDropTargetHelper->Drop( pDataObj, (LPPOINT)&pt, *pdwEffect ); } AwtInterfaceLocker _lk(this); JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); HRESULT ret = S_OK; POINT cp; RECT wr; if ( (!IsLocalDnD() && !IsCurrentDnDDataObject(pDataObj)) || (IsLocalDnD() && !IsLocalDataObject(pDataObj))) { *pdwEffect = DROPEFFECT_NONE; DragCleanup(); return ret; } LoadCache(pDataObj); ::GetWindowRect(m_window, &wr); cp.x = pt.x - wr.left; cp.y = pt.y - wr.top; ScaleDown(cp, m_window); m_dropActions = java_awt_dnd_DnDConstants_ACTION_NONE; call_dTCdrop(env, m_dtcp, m_target, (jint)cp.x, (jint)cp.y, ::convertDROPEFFECTToActions(mapModsToDROPEFFECT(*pdwEffect, grfKeyState)), ::convertDROPEFFECTToActions(*pdwEffect), m_cfFormats, (jlong)this); try { if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { env->ExceptionDescribe(); env->ExceptionClear(); ret = E_FAIL; } } catch (std::bad_alloc&) { AwtToolkit::GetInstance().MessageLoop(AwtToolkit::SecondaryIdleFunc, AwtToolkit::CommonPeekMessageFunc); *pdwEffect = ::convertActionsToDROPEFFECT(m_dropActions); DragCleanup(); throw; } /* * Fix for 4623377. * Dispatch all messages in the nested message loop running while the drop is * processed. This ensures that the modal dialog shown during drop receives * all events and so it is able to close. This way the app won't deadlock. */ AwtToolkit::GetInstance().MessageLoop(AwtToolkit::SecondaryIdleFunc, AwtToolkit::CommonPeekMessageFunc); ret = (m_dropSuccess == JNI_TRUE) ? S_OK : E_FAIL; *pdwEffect = ::convertActionsToDROPEFFECT(m_dropActions); DragCleanup(); return ret; CATCH_BAD_ALLOC_RET(E_OUTOFMEMORY); } /** * DoDropDone */ void AwtDropTarget::DoDropDone(jboolean success, jint action) { DropDoneRec ddr = { this, success, action }; AwtToolkit::GetInstance().InvokeFunction(_DropDone, &ddr); } /** * _DropDone */ void AwtDropTarget::_DropDone(void* param) { DropDonePtr ddrp = (DropDonePtr)param; (ddrp->dropTarget)->DropDone(ddrp->success, ddrp->action); } /** * DropDone */ void AwtDropTarget::DropDone(jboolean success, jint action) { m_dropSuccess = success; m_dropActions = action; AwtToolkit::GetInstance().QuitMessageLoop(AwtToolkit::EXIT_ENCLOSING_LOOP); } /** * DoRegisterTarget */ void AwtDropTarget::_RegisterTarget(void* param) { RegisterTargetPtr rtrp = (RegisterTargetPtr)param; rtrp->dropTarget->RegisterTarget(rtrp->show); } /** * RegisterTarget */ void AwtDropTarget::RegisterTarget(WORD show) { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); HRESULT res; if (!AwtToolkit::IsMainThread()) { RegisterTargetRec rtr = { this, show }; AwtToolkit::GetInstance().InvokeFunction(_RegisterTarget, &rtr); return; } // if we are'nt yet visible, defer until the parent is! if (show) { OLE_TRY OLE_HRT(CoCreateInstance( CLSID_DragDropHelper, NULL, CLSCTX_ALL, IID_IDropTargetHelper, (LPVOID*)&m_pIDropTargetHelper )) OLE_HRT(::RegisterDragDrop(m_window, (IDropTarget*)this)) OLE_CATCH res = OLE_HR; } else { res = ::RevokeDragDrop(m_window); if (NULL != m_pIDropTargetHelper) { m_pIDropTargetHelper->Release(); } } if (res == S_OK) m_registered = show; } /** * DoGetData */ jobject AwtDropTarget::DoGetData(jlong format) { jobject ret = (jobject)NULL; GetDataRec gdr = { this, format, &ret }; AwtToolkit::GetInstance().WaitForSingleObject(m_mutex); AwtToolkit::GetInstance().InvokeFunctionLater(_GetData, &gdr); WaitUntilSignalled(FALSE); return ret; } /** * _GetData */ void AwtDropTarget::_GetData(void* param) { GetDataPtr gdrp = (GetDataPtr)param; *(gdrp->ret) = gdrp->dropTarget->GetData(gdrp->format); gdrp->dropTarget->Signal(); } /** * GetData * * Returns the data object being transferred. */ HRESULT AwtDropTarget::ExtractNativeData( jlong fmt, LONG lIndex, STGMEDIUM *pmedium) { FORMATETC format = { (unsigned short)fmt }; HRESULT hr = E_INVALIDARG; static const DWORD supportedTymeds[] = { TYMED_ISTREAM, TYMED_ENHMF, TYMED_GDI, TYMED_MFPICT, TYMED_FILE, TYMED_HGLOBAL }; for (int i = 0; i < sizeof(supportedTymeds)/sizeof(supportedTymeds[0]); ++i) { // Only TYMED_HGLOBAL is supported for CF_LOCALE. if (fmt == CF_LOCALE && supportedTymeds[i] != TYMED_HGLOBAL) { continue; } format.tymed = supportedTymeds[i]; FORMATETC *cpp = (FORMATETC *)bsearch( (const void *)&format, (const void *)m_formats, (size_t)m_nformats, (size_t)sizeof(FORMATETC), _compar); if (NULL == cpp) { continue; } format = *cpp; format.lindex = lIndex; hr = m_dataObject->GetData(&format, pmedium); if (SUCCEEDED(hr)) { return hr; } } return hr; } HRESULT CheckRetValue( JNIEnv* env, jobject ret) { if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) { return E_UNEXPECTED; } else if (JNU_IsNull(env, ret)) { return E_INVALIDARG; } return S_OK; } jobject AwtDropTarget::ConvertNativeData(JNIEnv* env, jlong fmt, STGMEDIUM *pmedium) /*throw std::bad_alloc */ { jobject ret = NULL; jbyteArray paletteDataLocal = NULL; HRESULT hr = S_OK; switch (pmedium->tymed) { case TYMED_HGLOBAL: { if (fmt == CF_LOCALE) { LCID *lcid = (LCID *)::GlobalLock(pmedium->hGlobal); if (NULL == lcid) { hr = E_INVALIDARG; } else { try{ ret = AwtDataTransferer::LCIDToTextEncoding(env, *lcid); hr = CheckRetValue(env, ret); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } ::GlobalUnlock(pmedium->hGlobal); } } else { ::SetLastError(0); // clear error // Warning C4244. // Cast SIZE_T (__int64 on 64-bit/unsigned int on 32-bit) // to jsize (long). SIZE_T globalSize = ::GlobalSize(pmedium->hGlobal); jsize size = (globalSize <= INT_MAX) ? (jsize)globalSize : INT_MAX; if (size == 0 && ::GetLastError() != 0) { hr = E_INVALIDARG; } else { jbyteArray bytes = env->NewByteArray(size); if (NULL == bytes) { hr = E_OUTOFMEMORY; } else { LPVOID data = ::GlobalLock(pmedium->hGlobal); if (NULL == data) { hr = E_INVALIDARG; } else { env->SetByteArrayRegion(bytes, 0, size, (jbyte *)data); ret = bytes; //bytes is not null here => no CheckRetValue call ::GlobalUnlock(pmedium->hGlobal); } } } } break; } case TYMED_FILE: { jobject local = JNU_NewStringPlatform( env, pmedium->lpszFileName); if (env->ExceptionCheck()) { hr = E_OUTOFMEMORY; break; } jstring fileName = (jstring)env->NewGlobalRef(local); env->DeleteLocalRef(local); STGMEDIUM *stgm = NULL; try { //on success stgm would be deallocated by JAVA call freeStgMedium stgm = (STGMEDIUM *)safe_Malloc(sizeof(STGMEDIUM)); memcpy(stgm, pmedium, sizeof(STGMEDIUM)); // Warning C4311. // Cast pointer to jlong (__int64). ret = call_dTCgetfs(env, fileName, (jlong)stgm); hr = CheckRetValue(env, ret); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } if (FAILED(hr)) { //free just on error env->DeleteGlobalRef(fileName); free(stgm); } break; } case TYMED_ISTREAM: { WDTCPIStreamWrapper* istream = NULL; try { istream = new WDTCPIStreamWrapper(pmedium); // Warning C4311. // Cast pointer to jlong (__int64). ret = call_dTCgetis(env, (jlong)istream); hr = CheckRetValue(env, ret); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } if (FAILED(hr) && NULL!=istream) { //free just on error istream->Close(); } break; } case TYMED_GDI: // Currently support only CF_PALETTE for TYMED_GDI. if (CF_PALETTE == fmt) { ret = AwtDataTransferer::GetPaletteBytes( pmedium->hBitmap, 0, TRUE); hr = CheckRetValue(env, ret); } break; case TYMED_MFPICT: case TYMED_ENHMF: { HENHMETAFILE hEnhMetaFile = NULL; if (pmedium->tymed == TYMED_MFPICT ) { //let's create ENHMF from MFPICT to simplify treatment LPMETAFILEPICT lpMetaFilePict = (LPMETAFILEPICT)::GlobalLock(pmedium->hMetaFilePict); if (NULL == lpMetaFilePict) { hr = E_INVALIDARG; } else { UINT uSize = ::GetMetaFileBitsEx(lpMetaFilePict->hMF, 0, NULL); if (0 == uSize) { hr = E_INVALIDARG; } else { try{ LPBYTE lpMfBits = (LPBYTE)safe_Malloc(uSize); VERIFY(::GetMetaFileBitsEx( lpMetaFilePict->hMF, uSize, lpMfBits) == uSize); hEnhMetaFile = ::SetWinMetaFileBits( uSize, lpMfBits, NULL, lpMetaFilePict); free(lpMfBits); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } } ::GlobalUnlock(pmedium->hMetaFilePict); } } else { hEnhMetaFile = pmedium->hEnhMetaFile; } if (NULL == hEnhMetaFile) { hr = E_INVALIDARG; } else { try { paletteDataLocal = AwtDataTransferer::GetPaletteBytes( hEnhMetaFile, OBJ_ENHMETAFILE, FALSE); //paletteDataLocal can be NULL here - it is not a error! UINT uEmfSize = ::GetEnhMetaFileBits(hEnhMetaFile, 0, NULL); DASSERT(uEmfSize != 0); LPBYTE lpEmfBits = (LPBYTE)safe_Malloc(uEmfSize); //no chance to throw exception before catch => no more try-blocks //and no leaks on lpEmfBits VERIFY(::GetEnhMetaFileBits( hEnhMetaFile, uEmfSize, lpEmfBits) == uEmfSize); jbyteArray bytes = env->NewByteArray(uEmfSize); if (NULL == bytes) { hr = E_OUTOFMEMORY; } else { env->SetByteArrayRegion(bytes, 0, uEmfSize, (jbyte*)lpEmfBits); ret = bytes; //bytes is not null here => no CheckRetValue call } free(lpEmfBits); } catch (std::bad_alloc&) { hr = E_OUTOFMEMORY; } if (pmedium->tymed == TYMED_MFPICT) { //because we create it manually ::DeleteEnhMetaFile(hEnhMetaFile); } } break; } case TYMED_ISTORAGE: default: hr = E_NOTIMPL; break; } if (FAILED(hr)) { //clear exception garbage for hr = E_UNEXPECTED ret = NULL; } else { switch (fmt) { case CF_METAFILEPICT: case CF_ENHMETAFILE: // If we failed to retrieve palette entries from metafile, // fall through and try CF_PALETTE format. case CF_DIB: { if (JNU_IsNull(env, paletteDataLocal)) { jobject paletteData = GetData(CF_PALETTE); if (JNU_IsNull(env, paletteData)) { paletteDataLocal = AwtDataTransferer::GetPaletteBytes(NULL, 0, TRUE); } else { // GetData() returns a global ref. // We want to deal with local ref. paletteDataLocal = (jbyteArray)env->NewLocalRef(paletteData); env->DeleteGlobalRef(paletteData); } } DASSERT(!JNU_IsNull(env, paletteDataLocal) && !JNU_IsNull(env, ret)); jobject concat = AwtDataTransferer::ConcatData(env, paletteDataLocal, ret); env->DeleteLocalRef(ret); ret = concat; hr = CheckRetValue(env, ret); break; } } } if (!JNU_IsNull(env, paletteDataLocal) ) { env->DeleteLocalRef(paletteDataLocal); } jobject global = NULL; if (SUCCEEDED(hr)) { global = env->NewGlobalRef(ret); env->DeleteLocalRef(ret); } else if (E_UNEXPECTED == hr) { //internal Java non-GPF exception env->ExceptionDescribe(); env->ExceptionClear(); } else if (E_OUTOFMEMORY == hr) { throw std::bad_alloc(); } //NULL returns for all other cases return global; } HRESULT AwtDropTarget::SaveIndexToFile(LPCTSTR pFileName, UINT lIndex) { OLE_TRY STGMEDIUM stgmedium; OLE_HRT( ExtractNativeData(CF_FILECONTENTS, lIndex, &stgmedium) ); OLE_NEXT_TRY IStreamPtr spSrc; if (TYMED_HGLOBAL == stgmedium.tymed) { OLE_HRT( CreateStreamOnHGlobal( stgmedium.hGlobal, FALSE, &spSrc )); } else if(TYMED_ISTREAM == stgmedium.tymed) { spSrc = stgmedium.pstm; } if (NULL == spSrc) { OLE_HRT(E_INVALIDARG); } IStreamPtr spDst; OLE_HRT(SHCreateStreamOnFile( pFileName, STGM_WRITE | STGM_CREATE, &spDst )); STATSTG si = {0}; OLE_HRT( spSrc->Stat(&si, STATFLAG_NONAME ) ); OLE_HRT( spSrc->CopyTo(spDst, si.cbSize, NULL, NULL) ); OLE_CATCH ::ReleaseStgMedium(&stgmedium); OLE_CATCH OLE_RETURN_HR; } HRESULT GetTempPathWithSlash(JNIEnv *env, _bstr_t &bsTempPath) /*throws _com_error*/ { static _bstr_t _bsPath; OLE_TRY if (0 == _bsPath.length()) { BOOL bSafeEmergency = TRUE; TCHAR szPath[MAX_PATH*2]; JLClass systemCls(env, env->FindClass("java/lang/System")); if (systemCls) { jmethodID idGetProperty = env->GetStaticMethodID( systemCls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); if (0 != idGetProperty) { static TCHAR param[] = _T("java.io.tmpdir"); JLString tempdir(env, JNU_NewStringPlatform(env, param)); if (tempdir) { JLString jsTempPath(env, (jstring)env->CallStaticObjectMethod( systemCls, idGetProperty, (jstring)tempdir )); if (jsTempPath) { _bsPath = (LPCWSTR)JavaStringBuffer(env, jsTempPath); OLE_HRT(SHGetFolderPath( NULL, CSIDL_WINDOWS, NULL, 0, szPath)); _tcscat(szPath, _T("\\")); //Dead environment block leads to fact that windows folder becomes temporary path. //For example while jtreg execution %TEMP%, %TMP% and etc. aren't defined. bSafeEmergency = ( 0 == _tcsicmp(_bsPath, szPath) ); } } } } if (bSafeEmergency) { OLE_HRT(SHGetFolderPath( NULL, CSIDL_INTERNET_CACHE|CSIDL_FLAG_CREATE, NULL, 0, szPath)); _tcscat(szPath, _T("\\")); _bsPath = szPath; } } OLE_CATCH bsTempPath = _bsPath; OLE_RETURN_HR } jobject AwtDropTarget::ConvertMemoryMappedData(JNIEnv* env, jlong fmt, STGMEDIUM *pmedium) /*throw std::bad_alloc */ { jobject retObj = NULL; OLE_TRY if (TYMED_HGLOBAL != pmedium->tymed) { OLE_HRT(E_INVALIDARG); } FILEGROUPDESCRIPTORA *pfgdHead = (FILEGROUPDESCRIPTORA *)::GlobalLock(pmedium->hGlobal); if (NULL == pfgdHead) { OLE_HRT(E_INVALIDARG); } OLE_NEXT_TRY if (0 == pfgdHead->cItems) { OLE_HRT(E_INVALIDARG); } IStreamPtr spFileNames; OLE_HRT( CreateStreamOnHGlobal( NULL, TRUE, &spFileNames )); _bstr_t sbTempDir; OLE_HRT( GetTempPathWithSlash(env, sbTempDir) ); FILEDESCRIPTORA *pfgdA = pfgdHead->fgd; FILEDESCRIPTORW *pfgdW = (FILEDESCRIPTORW *)pfgdA; for (UINT i = 0; i < pfgdHead->cItems; ++i) { _bstr_t stFullName(sbTempDir); if(CF_FILEGROUPDESCRIPTORA == fmt) { stFullName += pfgdA->cFileName; //as CHAR ++pfgdA; } else { stFullName += pfgdW->cFileName; //as WCHAR ++pfgdW; } OLE_HRT(SaveIndexToFile( stFullName, i)); //write to stream with zero terminator OLE_HRT( spFileNames->Write((LPCTSTR)stFullName, (stFullName.length() + 1)*sizeof(TCHAR), NULL) ); } OLE_HRT( spFileNames->Write(_T(""), sizeof(TCHAR), NULL) ); STATSTG st; OLE_HRT( spFileNames->Stat(&st, STATFLAG_NONAME) ); //empty lists was forbidden: pfgdHead->cItems > 0 jbyteArray bytes = env->NewByteArray(st.cbSize.LowPart); if (NULL == bytes) { OLE_HRT(E_OUTOFMEMORY); } else { HGLOBAL glob; OLE_HRT(GetHGlobalFromStream(spFileNames, &glob)); jbyte *pFileListWithDoubleZeroTerminator = (jbyte *)::GlobalLock(glob); env->SetByteArrayRegion(bytes, 0, st.cbSize.LowPart, pFileListWithDoubleZeroTerminator); ::GlobalUnlock(pFileListWithDoubleZeroTerminator); retObj = bytes; } //std::bad_alloc could happen in JStringBuffer //no leaks due to wrapper OLE_CATCH_BAD_ALLOC ::GlobalUnlock(pmedium->hGlobal); OLE_CATCH jobject global = NULL; if (SUCCEEDED(OLE_HR)) { global = env->NewGlobalRef(retObj); env->DeleteLocalRef(retObj); } else if (E_OUTOFMEMORY == OLE_HR) { throw std::bad_alloc(); } return global; } jobject AwtDropTarget::GetData(jlong fmt) { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(1) < 0) { return (jobject)NULL; } jobject ret = NULL; OLE_TRY STGMEDIUM stgmedium; OLE_HRT( ExtractNativeData(fmt, -1, &stgmedium) ); OLE_NEXT_TRY if (CF_FILEGROUPDESCRIPTORA == fmt || CF_FILEGROUPDESCRIPTORW == fmt) { ret = ConvertMemoryMappedData(env, fmt, &stgmedium); } else { ret = ConvertNativeData(env, fmt, &stgmedium); } OLE_CATCH_BAD_ALLOC ::ReleaseStgMedium(&stgmedium); OLE_CATCH if (E_OUTOFMEMORY == OLE_HR) { throw std::bad_alloc(); } return ret; } /** * */ int __cdecl AwtDropTarget::_compar(const void* first, const void* second) { FORMATETC *fp = (FORMATETC *)first; FORMATETC *sp = (FORMATETC *)second; if (fp->cfFormat == sp->cfFormat) { return fp->tymed - sp->tymed; } return fp->cfFormat - sp->cfFormat; } const unsigned int AwtDropTarget::CACHE_INCR = 16; void AwtDropTarget::LoadCache(IDataObject* pDataObj) { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); unsigned int cnt = 0; HRESULT res; IEnumFORMATETC* pEnumFormatEtc = NULL; if (m_dataObject != (IDataObject*)NULL) UnloadCache(); if (!IsLocalDnD()) { SetCurrentDnDDataObject(pDataObj); } (m_dataObject = pDataObj)->AddRef(); res = m_dataObject->EnumFormatEtc(DATADIR_GET, &pEnumFormatEtc); if (res == S_OK) { for (;;) { FORMATETC tmp; ULONG actual = 1; res = pEnumFormatEtc->Next((ULONG)1, &tmp, &actual); if (res == S_FALSE) break; if (!(tmp.cfFormat >= 1 && tmp.ptd == NULL && (tmp.lindex == -1 || CF_FILECONTENTS==tmp.cfFormat) && tmp.dwAspect == DVASPECT_CONTENT && ( tmp.tymed == TYMED_HGLOBAL || tmp.tymed == TYMED_FILE || tmp.tymed == TYMED_ISTREAM || tmp.tymed == TYMED_GDI || tmp.tymed == TYMED_MFPICT || tmp.tymed == TYMED_ENHMF ) // but not ISTORAGE ) ) continue; if (m_dataObject->QueryGetData(&tmp) != S_OK) continue; if (m_nformats % CACHE_INCR == 0) { m_formats = (FORMATETC *)SAFE_SIZE_ARRAY_REALLOC(safe_Realloc, m_formats, CACHE_INCR + m_nformats, sizeof(FORMATETC)); } memcpy(m_formats + m_nformats, &tmp, sizeof(FORMATETC)); m_nformats++; } // We are responsible for releasing the enumerator. pEnumFormatEtc->Release(); } if (m_nformats > 0) { qsort((void*)m_formats, m_nformats, sizeof(FORMATETC), AwtDropTarget::_compar); } if (m_cfFormats != NULL) { env->DeleteGlobalRef(m_cfFormats); } jlongArray l_cfFormats = env->NewLongArray(m_nformats); if (l_cfFormats == NULL) { throw std::bad_alloc(); } m_cfFormats = (jlongArray)env->NewGlobalRef(l_cfFormats); env->DeleteLocalRef(l_cfFormats); jboolean isCopy; jlong *lcfFormats = env->GetLongArrayElements(m_cfFormats, &isCopy), *saveFormats = lcfFormats; for (unsigned int i = 0; i < m_nformats; i++, lcfFormats++) { *lcfFormats = m_formats[i].cfFormat; } env->ReleaseLongArrayElements(m_cfFormats, saveFormats, 0); } /** * UnloadCache */ void AwtDropTarget::UnloadCache() { if (m_dataObject == (IDataObject*)NULL) return; JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); free((void*)m_formats); m_formats = (FORMATETC *)NULL; m_nformats = 0; // fix for 6212440: on application shutdown, this object's // destruction might be suppressed due to dangling COM references. // This method is called from the destructor. // On destruction, VM might be shut down already, so we should make // a null check on env. if (env) { env->DeleteGlobalRef(m_cfFormats); } m_cfFormats = NULL; if (!IsLocalDnD()) { DASSERT(IsCurrentDnDDataObject(m_dataObject)); SetCurrentDnDDataObject(NULL); } m_dataObject->Release(); m_dataObject = (IDataObject*)NULL; } /** * DragCleanup */ void AwtDropTarget::DragCleanup(void) { UnloadCache(); } BOOL AwtDropTarget::IsLocalDataObject(IDataObject __RPC_FAR *pDataObject) { BOOL local = FALSE; if (pDataObject != NULL) { FORMATETC format; STGMEDIUM stgmedium; format.cfFormat = AwtDragSource::PROCESS_ID_FORMAT; format.ptd = NULL; format.dwAspect = DVASPECT_CONTENT; format.lindex = -1; format.tymed = TYMED_HGLOBAL; if (pDataObject->GetData(&format, &stgmedium) == S_OK) { ::SetLastError(0); // clear error // Warning C4244. SIZE_T size = ::GlobalSize(stgmedium.hGlobal); if (size < sizeof(DWORD) || ::GetLastError() != 0) { ::SetLastError(0); // clear error } else { DWORD id = ::CoGetCurrentProcess(); LPVOID data = ::GlobalLock(stgmedium.hGlobal); if (memcmp(data, &id, sizeof(id)) == 0) { local = TRUE; } ::GlobalUnlock(stgmedium.hGlobal); } ::ReleaseStgMedium(&stgmedium); } } return local; } DECLARE_JAVA_CLASS(dTCClazz, "sun/awt/windows/WDropTargetContextPeer") jobject AwtDropTarget::call_dTCcreate(JNIEnv* env) { DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCcreate, dTCClazz, "getWDropTargetContextPeer", "()Lsun/awt/windows/WDropTargetContextPeer;"); return env->CallStaticObjectMethod(clazz, dTCcreate); } jint AwtDropTarget::call_dTCenter(JNIEnv* env, jobject self, jobject component, jint x, jint y, jint dropAction, jint actions, jlongArray formats, jlong nativeCtxt) { DECLARE_JINT_JAVA_METHOD(dTCenter, dTCClazz, "handleEnterMessage", "(Ljava/awt/Component;IIII[JJ)I"); DASSERT(!JNU_IsNull(env, self)); return env->CallIntMethod(self, dTCenter, component, x, y, dropAction, actions, formats, nativeCtxt); } void AwtDropTarget::call_dTCexit(JNIEnv* env, jobject self, jobject component, jlong nativeCtxt) { DECLARE_VOID_JAVA_METHOD(dTCexit, dTCClazz, "handleExitMessage", "(Ljava/awt/Component;J)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dTCexit, component, nativeCtxt); } jint AwtDropTarget::call_dTCmotion(JNIEnv* env, jobject self, jobject component, jint x, jint y, jint dropAction, jint actions, jlongArray formats, jlong nativeCtxt) { DECLARE_JINT_JAVA_METHOD(dTCmotion, dTCClazz, "handleMotionMessage", "(Ljava/awt/Component;IIII[JJ)I"); DASSERT(!JNU_IsNull(env, self)); return env->CallIntMethod(self, dTCmotion, component, x, y, dropAction, actions, formats, nativeCtxt); } void AwtDropTarget::call_dTCdrop(JNIEnv* env, jobject self, jobject component, jint x, jint y, jint dropAction, jint actions, jlongArray formats, jlong nativeCtxt) { DECLARE_VOID_JAVA_METHOD(dTCdrop, dTCClazz, "handleDropMessage", "(Ljava/awt/Component;IIII[JJ)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dTCdrop, component, x, y, dropAction, actions, formats, nativeCtxt); } jobject AwtDropTarget::call_dTCgetfs(JNIEnv* env, jstring fileName, jlong stgmedium) { DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCgetfs, dTCClazz, "getFileStream", "(Ljava/lang/String;J)Ljava/io/FileInputStream;"); return env->CallStaticObjectMethod(clazz, dTCgetfs, fileName, stgmedium); } jobject AwtDropTarget::call_dTCgetis(JNIEnv* env, jlong istream) { DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCgetis, dTCClazz, "getIStream", "(J)Ljava/lang/Object;"); return env->CallStaticObjectMethod(clazz, dTCgetis, istream); } /*****************************************************************************/ /** * construct a wrapper */ WDTCPIStreamWrapper::WDTCPIStreamWrapper(STGMEDIUM* stgmedium) { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); m_stgmedium = *stgmedium; m_istream = stgmedium->pstm; m_istream->AddRef(); m_mutex = ::CreateMutex(NULL, FALSE, NULL); } /** * destroy a wrapper */ WDTCPIStreamWrapper::~WDTCPIStreamWrapper() { ::CloseHandle(m_mutex); m_istream->Release(); ::ReleaseStgMedium(&m_stgmedium); } /** * return available data */ jint WDTCPIStreamWrapper::DoAvailable(WDTCPIStreamWrapper* istream) { WDTCPIStreamWrapperRec iswr = { istream, 0 }; AwtToolkit::GetInstance().WaitForSingleObject(istream->m_mutex); AwtToolkit::GetInstance().InvokeFunctionLater( _Available, &iswr); istream->WaitUntilSignalled(FALSE); return iswr.ret; } /** * return available data */ void WDTCPIStreamWrapper::_Available(void *param) { WDTCPIStreamWrapperPtr iswrp = (WDTCPIStreamWrapperPtr)param; iswrp->ret = (iswrp->istream)->Available(); iswrp->istream->Signal(); } /** * return available data */ jint WDTCPIStreamWrapper::Available() { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (m_istream->Stat(&m_statstg, STATFLAG_NONAME) != S_OK) { JNU_ThrowIOException(env, "IStream::Stat() failed"); return 0; } if (m_statstg.cbSize.QuadPart > 0x7ffffffL) { JNU_ThrowIOException(env, "IStream::Stat() cbSize > 0x7ffffff"); return 0; } return (jint)m_statstg.cbSize.LowPart; } /** * read 1 byte */ jint WDTCPIStreamWrapper::DoRead(WDTCPIStreamWrapper* istream) { WDTCPIStreamWrapperRec iswr = { istream, 0 }; AwtToolkit::GetInstance().WaitForSingleObject(istream->m_mutex); AwtToolkit::GetInstance().InvokeFunctionLater(_Read, &iswr); istream->WaitUntilSignalled(FALSE); return iswr.ret; } /** * read 1 byte */ void WDTCPIStreamWrapper::_Read(void* param) { WDTCPIStreamWrapperPtr iswrp = (WDTCPIStreamWrapperPtr)param; iswrp->ret = (iswrp->istream)->Read(); iswrp->istream->Signal(); } /** * read 1 byte */ jint WDTCPIStreamWrapper::Read() { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jint b = 0; ULONG actual = 0; HRESULT res; switch (res = m_istream->Read((void *)&b, (ULONG)1, &actual)) { case S_FALSE: return (jint)-1; case S_OK: return (jint)(actual == 0 ? -1 : b); default: JNU_ThrowIOException(env, "IStream::Read failed"); } return (jint)-1; } /** * read Buffer */ jint WDTCPIStreamWrapper::DoReadBytes(WDTCPIStreamWrapper* istream, jbyteArray array, jint off, jint len) { WDTCPIStreamWrapperReadBytesRec iswrbr = { istream, 0, array, off, len }; AwtToolkit::GetInstance().WaitForSingleObject(istream->m_mutex); AwtToolkit::GetInstance().InvokeFunctionLater(_ReadBytes, &iswrbr); istream->WaitUntilSignalled(FALSE); return iswrbr.ret; } /** * read buffer */ void WDTCPIStreamWrapper::_ReadBytes(void* param) { WDTCPIStreamWrapperReadBytesPtr iswrbrp = (WDTCPIStreamWrapperReadBytesPtr)param; iswrbrp->ret = (iswrbrp->istream)->ReadBytes(iswrbrp->array, iswrbrp->off, iswrbrp->len); iswrbrp->istream->Signal(); } /** * read buffer */ jint WDTCPIStreamWrapper::ReadBytes(jbyteArray buf, jint off, jint len) { JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jboolean isCopy = JNI_FALSE; ULONG actual = 0; jbyte* local = env->GetByteArrayElements(buf, &isCopy); HRESULT res; CHECK_NULL_RETURN(local, (jint)-1); switch (res = m_istream->Read((void *)(local + off), (ULONG)len, &actual)) { case S_FALSE: case S_OK: { int eof = (actual == 0); env->ReleaseByteArrayElements(buf, local, !eof ? 0 : JNI_ABORT); return (jint)(!eof ? actual : -1); } default: env->ReleaseByteArrayElements(buf, local, JNI_ABORT); JNU_ThrowIOException(env, "IStream::Read failed"); } return (jint)-1; } /** * close */ void WDTCPIStreamWrapper::DoClose(WDTCPIStreamWrapper* istream) { AwtToolkit::GetInstance().InvokeFunctionLater(_Close, istream); } /** * close */ void WDTCPIStreamWrapper::_Close(void* param) { ((WDTCPIStreamWrapper*)param)->Close(); } /** * close */ void WDTCPIStreamWrapper::Close() { delete this; } /*****************************************************************************/ extern "C" { /** * awt_dnd_initialize: initial DnD system */ void awt_dnd_initialize() { ::OleInitialize((LPVOID)NULL); } /** * awt_dnd_uninitialize: deactivate DnD system */ void awt_dnd_uninitialize() { ::OleUninitialize(); } /** * convertActionsToDROPEFFECT */ DWORD convertActionsToDROPEFFECT(jint actions) { DWORD effects = DROPEFFECT_NONE; if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) effects |= DROPEFFECT_LINK; if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) effects |= DROPEFFECT_MOVE; if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) effects |= DROPEFFECT_COPY; return effects; } /** * convertDROPEFFECTToAction */ jint convertDROPEFFECTToActions(DWORD effects) { jint actions = java_awt_dnd_DnDConstants_ACTION_NONE; if (effects & DROPEFFECT_LINK) actions |= java_awt_dnd_DnDConstants_ACTION_LINK; if (effects & DROPEFFECT_MOVE) actions |= java_awt_dnd_DnDConstants_ACTION_MOVE; if (effects & DROPEFFECT_COPY) actions |= java_awt_dnd_DnDConstants_ACTION_COPY; return actions; } /** * map keyboard modifiers to a DROPEFFECT */ DWORD mapModsToDROPEFFECT(DWORD effects, DWORD mods) { DWORD ret = DROPEFFECT_NONE; /* * Fix for 4285634. * Calculate the drop action to match Motif DnD behavior. * If the user selects an operation (by pressing a modifier key), * return the selected operation or DROPEFFECT_NONE if the selected * operation is not supported by the drag source. * If the user doesn't select an operation search the set of operations * supported by the drag source for DROPEFFECT_MOVE, then for * DROPEFFECT_COPY, then for DROPEFFECT_LINK and return the first operation * found. */ switch (mods & (MK_CONTROL | MK_SHIFT)) { case MK_CONTROL: ret = DROPEFFECT_COPY; break; case MK_CONTROL | MK_SHIFT: ret = DROPEFFECT_LINK; break; case MK_SHIFT: ret = DROPEFFECT_MOVE; break; default: if (effects & DROPEFFECT_MOVE) { ret = DROPEFFECT_MOVE; } else if (effects & DROPEFFECT_COPY) { ret = DROPEFFECT_COPY; } else if (effects & DROPEFFECT_LINK) { ret = DROPEFFECT_LINK; } break; } return ret & effects; } /** * downcall to fetch data ... gets scheduled on message thread */ JNIEXPORT jobject JNICALL Java_sun_awt_windows_WDropTargetContextPeer_getData(JNIEnv* env, jobject self, jlong dropTarget, jlong format) { TRY; AwtDropTarget* pDropTarget = (AwtDropTarget*)dropTarget; DASSERT(!::IsBadReadPtr(pDropTarget, sizeof(AwtDropTarget))); return pDropTarget->DoGetData(format); CATCH_BAD_ALLOC_RET(NULL); } /** * downcall to signal drop done ... gets scheduled on message thread */ JNIEXPORT void JNICALL Java_sun_awt_windows_WDropTargetContextPeer_dropDone(JNIEnv* env, jobject self, jlong dropTarget, jboolean success, jint actions) { TRY_NO_HANG; AwtDropTarget* pDropTarget = (AwtDropTarget*)dropTarget; DASSERT(!::IsBadReadPtr(pDropTarget, sizeof(AwtDropTarget))); pDropTarget->DoDropDone(success, actions); CATCH_BAD_ALLOC; } /** * downcall to free up storage medium for FileStream */ JNIEXPORT void JNICALL Java_sun_awt_windows_WDropTargetContextPeerFileStream_freeStgMedium(JNIEnv* env, jobject self, jlong stgmedium) { TRY; ::ReleaseStgMedium((STGMEDIUM*)stgmedium); free((void*)stgmedium); CATCH_BAD_ALLOC; } /** * */ JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Available(JNIEnv* env, jobject self, jlong istream) { TRY; return WDTCPIStreamWrapper::DoAvailable((WDTCPIStreamWrapper*)istream); CATCH_BAD_ALLOC_RET(0); } /** * */ JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Read(JNIEnv* env, jobject self, jlong istream) { TRY; return WDTCPIStreamWrapper::DoRead((WDTCPIStreamWrapper*)istream); CATCH_BAD_ALLOC_RET(0); } /** * */ JNIEXPORT jint JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_ReadBytes(JNIEnv* env, jobject self, jlong istream, jbyteArray buf, jint off, jint len) { TRY; return WDTCPIStreamWrapper::DoReadBytes((WDTCPIStreamWrapper*)istream, buf, off, len); CATCH_BAD_ALLOC_RET(0); } /** * */ JNIEXPORT void JNICALL Java_sun_awt_windows_WDropTargetContextPeerIStream_Close(JNIEnv* env, jobject self, jlong istream) { TRY_NO_VERIFY; WDTCPIStreamWrapper::DoClose((WDTCPIStreamWrapper*)istream); CATCH_BAD_ALLOC; } } /* extern "C" */