You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
550 lines
18 KiB
550 lines
18 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORPORATION, 1998-2000
|
|
*
|
|
* TITLE: MINTRANS.CPP
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* AUTHOR: ShaunIv
|
|
*
|
|
* DATE: 12/6/1999
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
*******************************************************************************/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <initguid.h>
|
|
#include <wiaregst.h>
|
|
#include <shlguid.h>
|
|
#include "shellext.h"
|
|
#include "shlobj.h"
|
|
#include "resource.h" // resource ids
|
|
#include "itranhlp.h"
|
|
#include "mintrans.h"
|
|
#include "comctrlp.h"
|
|
#include "shlwapip.h"
|
|
#include "acqmgrcw.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
//
|
|
// Define constants for dwords stored in the registry
|
|
#define ACTION_RUNAPP 0
|
|
#define ACTION_AUTOSAVE 1
|
|
#define ACTION_NOTHING 2
|
|
#define ACTION_MAX 2
|
|
|
|
static const TCHAR c_szConnectionSettings[] = TEXT("OnConnect\\%ls");
|
|
|
|
struct CMinimalTransferSettings
|
|
{
|
|
DWORD dwAction;
|
|
BOOL bDeleteImages;
|
|
CSimpleString strFolderPath;
|
|
CComPtr<IWiaTransferHelper> pXfer;
|
|
BOOL bSaveInDatedDir;
|
|
};
|
|
|
|
|
|
#ifndef REGSTR_VALUE_USEDATE
|
|
#define REGSTR_VALUE_USEDATE TEXT("UseDate")
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
|
|
ConstructDatedFolderPath
|
|
|
|
Concatenate the date to an existing folder name
|
|
|
|
*******************************************************************************/
|
|
static
|
|
CSimpleString
|
|
ConstructDatedFolderPath(
|
|
const CSimpleString &strOriginal
|
|
)
|
|
{
|
|
CSimpleString strPath = strOriginal;
|
|
|
|
//
|
|
// Get the current date and format it as a string
|
|
//
|
|
SYSTEMTIME SystemTime;
|
|
TCHAR szDate[MAX_PATH] = TEXT("");
|
|
GetLocalTime( &SystemTime );
|
|
GetDateFormat( LOCALE_USER_DEFAULT, 0, &SystemTime, CSimpleString(IDS_DATEFORMAT,g_hInstance), szDate, ARRAYSIZE(szDate) );
|
|
|
|
//
|
|
// Make sure there is a trailing backslash
|
|
//
|
|
if (!strPath.MatchLastCharacter( TEXT('\\')))
|
|
{
|
|
strPath += CSimpleString(TEXT("\\"));
|
|
}
|
|
|
|
//
|
|
// Append the date
|
|
//
|
|
strPath += szDate;
|
|
|
|
return strPath;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPersistCallback and helpers
|
|
|
|
/*******************************************************************************
|
|
CheckAndCreateFolder
|
|
|
|
Make sure the target path exists or can be created. Failing that, prompt the
|
|
user for a folder.
|
|
|
|
*******************************************************************************/
|
|
void
|
|
CheckAndCreateFolder (CSimpleString &strFolderPath)
|
|
{
|
|
|
|
// Convert to a full path name. If strFolderPath is not a full path,
|
|
// we want it to be a subfolder of My Pictures
|
|
|
|
TCHAR szFullPath[MAX_PATH] = TEXT("");
|
|
SHGetFolderPath (NULL, CSIDL_MYPICTURES, NULL, 0, szFullPath);
|
|
LPTSTR szUnused;
|
|
BOOL bPrompt = false;
|
|
if (*szFullPath)
|
|
{
|
|
SetCurrentDirectory (szFullPath);
|
|
}
|
|
GetFullPathName (strFolderPath, ARRAYSIZE(szFullPath), szFullPath, &szUnused);
|
|
strFolderPath = szFullPath;
|
|
// make sure the folder exists
|
|
DWORD dw = GetFileAttributes(strFolderPath);
|
|
|
|
if (dw == 0xffffffff)
|
|
{
|
|
bPrompt = !CAcquisitionManagerControllerWindow::RecursiveCreateDirectory( strFolderPath );
|
|
}
|
|
else if (!(dw & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
bPrompt = TRUE;
|
|
}
|
|
|
|
// Ask the user for a valid folder
|
|
if (bPrompt)
|
|
{
|
|
BROWSEINFO bi;
|
|
TCHAR szPath[MAX_PATH] = TEXT("\0");
|
|
LPITEMIDLIST pidl;
|
|
TCHAR szTitle[200];
|
|
LoadString (g_hInstance,
|
|
IDS_MINTRANS_FOLDERPATH_CAPTION,
|
|
szTitle,
|
|
200);
|
|
ZeroMemory (&bi, sizeof(bi));
|
|
bi.hwndOwner = NULL;
|
|
bi.lpszTitle = szTitle;
|
|
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
|
|
pidl = SHBrowseForFolder (&bi);
|
|
if (pidl)
|
|
{
|
|
SHGetPathFromIDList (pidl, szPath);
|
|
}
|
|
strFolderPath = szPath;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
GetSaveSettings
|
|
|
|
Find out what the user configured us to do with the images
|
|
|
|
*******************************************************************************/
|
|
|
|
void
|
|
GetSaveSettings (CMinimalTransferSettings &settings, BSTR bstrDeviceId)
|
|
{
|
|
|
|
CSimpleReg regSettings(HKEY_CURRENT_USER,
|
|
REGSTR_PATH_USER_SETTINGS,
|
|
true,
|
|
KEY_READ);
|
|
|
|
|
|
// Default to My Pictures/no delete if registry settings not there
|
|
TCHAR szMyPictures[MAX_PATH];
|
|
SHGetFolderPath (NULL, CSIDL_MYPICTURES, NULL, 0, szMyPictures);
|
|
settings.bDeleteImages = 0;
|
|
settings.strFolderPath = const_cast<LPCTSTR>(szMyPictures);
|
|
settings.dwAction = ACTION_RUNAPP;
|
|
settings.bSaveInDatedDir = FALSE;
|
|
|
|
// BUGBUG: Should we prompt the user if the registry path
|
|
// isn't set?
|
|
if (regSettings.OK())
|
|
{
|
|
|
|
CSimpleString strSubKey;
|
|
strSubKey.Format (c_szConnectionSettings, bstrDeviceId);
|
|
CSimpleReg regActions (regSettings, strSubKey, true, KEY_READ);
|
|
settings.bDeleteImages = regActions.Query (REGSTR_VALUE_AUTODELETE, 0);
|
|
settings.strFolderPath = regActions.Query (REGSTR_VALUE_SAVEFOLDER,
|
|
CSimpleString(szMyPictures));
|
|
settings.dwAction = regActions.Query (REGSTR_VALUE_CONNECTACT,
|
|
ACTION_AUTOSAVE);
|
|
settings.bSaveInDatedDir = (regActions.Query(REGSTR_VALUE_USEDATE,0) != 0);
|
|
if (settings.bSaveInDatedDir)
|
|
{
|
|
settings.strFolderPath = ConstructDatedFolderPath( settings.strFolderPath );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// For the short term, have an array of format/extension pairs
|
|
struct MYFMTS
|
|
{
|
|
const GUID *pFmt;
|
|
LPCWSTR pszExt;
|
|
} FMTS [] =
|
|
{
|
|
{&WiaImgFmt_BMP, L".bmp"},
|
|
{&WiaImgFmt_JPEG, L".jpg"},
|
|
{&WiaImgFmt_FLASHPIX, L".fpx"},
|
|
{&WiaImgFmt_TIFF, L".tif"},
|
|
{NULL, L""}
|
|
};
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
GetDropTarget
|
|
|
|
Get an IDropTarget interface for the given folder
|
|
|
|
|
|
*******************************************************************************/
|
|
HRESULT
|
|
GetDropTarget (IShellFolder *pDesktop, LPCTSTR szPath, IDropTarget **ppDrop)
|
|
{
|
|
HRESULT hr;
|
|
LPITEMIDLIST pidl;
|
|
CSimpleStringWide strPath = CSimpleStringConvert::WideString (CSimpleString(szPath));
|
|
CComPtr<IShellFolder> psf;
|
|
hr = pDesktop->ParseDisplayName(NULL,
|
|
NULL,
|
|
const_cast<LPWSTR>(static_cast<LPCWSTR>(strPath)),
|
|
NULL,
|
|
&pidl,
|
|
NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pDesktop->BindToObject(const_cast<LPCITEMIDLIST>(pidl),
|
|
NULL,
|
|
IID_IShellFolder,
|
|
reinterpret_cast<LPVOID*>(&psf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->CreateViewObject (NULL,
|
|
IID_IDropTarget,
|
|
reinterpret_cast<LPVOID*>(ppDrop));
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
FreePidl
|
|
Called when the array of pidls is destroyed, to free the pidls
|
|
*******************************************************************************/
|
|
INT
|
|
FreePidl (LPITEMIDLIST pidl, IMalloc *pMalloc)
|
|
{
|
|
pMalloc->Free (pidl);
|
|
return 1;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SaveItemsFromFolder (IShellFolder *pRoot, CSimpleString &strPath, BOOL bDelete)
|
|
{
|
|
CComPtr<IEnumIDList> pEnum;
|
|
LPITEMIDLIST pidl;
|
|
HRESULT hr = S_FALSE;
|
|
|
|
CComPtr<IMalloc> pMalloc;
|
|
if (SUCCEEDED(SHGetMalloc (&pMalloc)))
|
|
{
|
|
|
|
CComPtr<IShellFolder> pDesktop;
|
|
if (SUCCEEDED(SHGetDesktopFolder (&pDesktop)))
|
|
{
|
|
// enum the non-folder objects first
|
|
if (SUCCEEDED(pRoot->EnumObjects (NULL,
|
|
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS ,
|
|
&pEnum)))
|
|
{
|
|
HDPA dpaItems;
|
|
|
|
dpaItems = DPA_Create(10);
|
|
while (NOERROR == pEnum->Next(1, &pidl, NULL))
|
|
{
|
|
DPA_AppendPtr (dpaItems, pidl);
|
|
|
|
}
|
|
//
|
|
// Now create the array of pidls and get the IDataObject
|
|
//
|
|
INT nSize = DPA_GetPtrCount (dpaItems);
|
|
if (nSize > 0)
|
|
{
|
|
LPITEMIDLIST *aidl = new LPITEMIDLIST[nSize];
|
|
if (aidl)
|
|
{
|
|
CComPtr<IDataObject> pdo;
|
|
for (INT i=0;i<nSize;i++)
|
|
{
|
|
aidl[i] = reinterpret_cast<LPITEMIDLIST>(DPA_FastGetPtr(dpaItems, i));
|
|
}
|
|
hr = pRoot->GetUIObjectOf (NULL,
|
|
nSize,
|
|
const_cast<LPCITEMIDLIST*>(aidl),
|
|
IID_IDataObject,
|
|
NULL,
|
|
reinterpret_cast<LPVOID*>(&pdo));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComPtr<IDropTarget> pDrop;
|
|
CComQIPtr<IAsyncOperation, &IID_IAsyncOperation> pasync(pdo);
|
|
if (pasync.p)
|
|
{
|
|
pasync->SetAsyncMode(FALSE);
|
|
}
|
|
CheckAndCreateFolder (strPath);
|
|
if (strPath.Length())
|
|
{
|
|
|
|
//
|
|
// Get an IDropTarget for the destination folder
|
|
// and do the drag/drop
|
|
//
|
|
|
|
hr = GetDropTarget (pDesktop,
|
|
strPath,
|
|
&pDrop);
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
DWORD dwKeyState;
|
|
if (bDelete)
|
|
{
|
|
// the "move" keys
|
|
dwKeyState = MK_SHIFT | MK_LBUTTON;
|
|
}
|
|
else
|
|
{ // the copy keys
|
|
dwKeyState = MK_CONTROL|MK_LBUTTON;
|
|
}
|
|
hr = SHSimulateDrop (pDrop,
|
|
pdo,
|
|
dwKeyState,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE; // no images to download
|
|
}
|
|
DPA_DestroyCallback (dpaItems,
|
|
reinterpret_cast<PFNDPAENUMCALLBACK>(FreePidl),
|
|
reinterpret_cast<LPVOID>(pMalloc.p));
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
SaveItems
|
|
|
|
This function uses IShellFolder and IDataObject interfaces to simulate
|
|
a drag/drop operation from the WIA virtual folder for the given device.
|
|
|
|
*******************************************************************************/
|
|
|
|
#define STR_WIASHEXT TEXT("wiashext.dll")
|
|
|
|
static
|
|
HRESULT
|
|
SaveItems (BSTR strDeviceId, CMinimalTransferSettings &settings)
|
|
{
|
|
WIA_PUSH_FUNCTION((TEXT("SaveItems( %ws, ... )"), strDeviceId ));
|
|
|
|
CComPtr<IShellFolder>pRoot;
|
|
HRESULT hr = SHGetDesktopFolder (&pRoot);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Get the system directory, which is where wiashext.dll lives
|
|
//
|
|
TCHAR szShellExtensionPath[MAX_PATH] = {0};
|
|
if (GetSystemDirectory( szShellExtensionPath, ARRAYSIZE(szShellExtensionPath)))
|
|
{
|
|
//
|
|
// Make sure the path variable is long enough to hold this path
|
|
//
|
|
if (lstrlen(szShellExtensionPath) + lstrlen(STR_WIASHEXT) + lstrlen(TEXT("\\")) < ARRAYSIZE(szShellExtensionPath))
|
|
{
|
|
//
|
|
// Concatenate the backslash and module name to the system path
|
|
//
|
|
lstrcat( szShellExtensionPath, TEXT("\\") );
|
|
lstrcat( szShellExtensionPath, STR_WIASHEXT );
|
|
|
|
//
|
|
// Load the DLL
|
|
//
|
|
HINSTANCE hInstanceShellExt = LoadLibrary(szShellExtensionPath);
|
|
if (hInstanceShellExt)
|
|
{
|
|
//
|
|
// Get the pidl making function
|
|
//
|
|
WIAMAKEFULLPIDLFORDEVICE pfnMakeFullPidlForDevice = reinterpret_cast<WIAMAKEFULLPIDLFORDEVICE>(GetProcAddress(hInstanceShellExt, "MakeFullPidlForDevice"));
|
|
if (pfnMakeFullPidlForDevice)
|
|
{
|
|
//
|
|
// Get the pidl
|
|
//
|
|
LPITEMIDLIST pidlDevice = NULL;
|
|
hr = pfnMakeFullPidlForDevice( strDeviceId, &pidlDevice );
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Bind to the folder for this device
|
|
//
|
|
CComPtr<IShellFolder> pDevice;
|
|
hr = pRoot->BindToObject (const_cast<LPCITEMIDLIST> (pidlDevice), NULL, IID_IShellFolder, reinterpret_cast<LPVOID*>(&pDevice));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
hr = SaveItemsFromFolder (pDevice, settings.strFolderPath, settings.bDeleteImages);
|
|
if (S_OK == hr && settings.bDeleteImages)
|
|
{
|
|
//
|
|
// DoDeleteAllItems will pop up a dialog to confirm the delete.
|
|
//
|
|
DoDeleteAllItems (strDeviceId, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WIA_PRINTHRESULT((hr,TEXT("BindToObject failed!")));
|
|
}
|
|
|
|
CComPtr<IMalloc> pMalloc;
|
|
if (SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc)
|
|
{
|
|
pMalloc->Free(pidlDevice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WIA_PRINTHRESULT((hr,TEXT("MakeFullPidlForDevice failed!")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
WIA_PRINTHRESULT((hr,TEXT("GetProcAddress for MakeFullPidlForDevice failed!")));
|
|
}
|
|
FreeLibrary(hInstanceShellExt);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
WIA_PRINTHRESULT((hr,TEXT("Unable to load wiashext.dll!")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
WIA_PRINTHRESULT((hr,TEXT("Buffer size was too small")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
WIA_PRINTHRESULT((hr,TEXT("Unable to get system folder!")));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WIA_PRINTHRESULT((hr,TEXT("SHGetDesktopFolder failed!")));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
} // End namespace MinimalTransfer
|
|
|
|
LRESULT
|
|
MinimalTransferThreadProc (BSTR bstrDeviceId)
|
|
{
|
|
if (bstrDeviceId)
|
|
{
|
|
CMinimalTransferSettings settings;
|
|
|
|
HRESULT hr = CoInitialize(NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
GetSaveSettings (settings, bstrDeviceId);
|
|
// Bail if the default action is donothing or if the user cancelled
|
|
// the browse for folder
|
|
if (settings.dwAction == ACTION_AUTOSAVE)
|
|
{
|
|
hr = SaveItems (bstrDeviceId, settings);
|
|
// Show the folder the user saved to
|
|
if (NOERROR == hr)
|
|
{
|
|
SHELLEXECUTEINFO sei;
|
|
ZeroMemory (&sei, sizeof(sei));
|
|
sei.cbSize = sizeof(sei);
|
|
sei.lpDirectory = settings.strFolderPath;
|
|
sei.nShow = SW_SHOW;
|
|
ShellExecuteEx (&sei);
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
WIA_PRINTHRESULT((hr,TEXT("SaveItems failed!")));
|
|
// we can rely on SaveItems reporting errors to the user
|
|
|
|
}
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
#ifndef DBG_GENERATE_PRETEND_EVENT
|
|
WIA_TRACE((TEXT("Module::m_nLockCnt: %d"),_Module.m_nLockCnt));
|
|
_Module.Unlock();
|
|
#endif
|
|
SysFreeString(bstrDeviceId);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|