2025-11-28 00:35:46 +09:00

682 lines
18 KiB
C

/*****************************************************************************
*
* SendTo.c
*
* Copyright (c) 1997 Microsoft Corporation. All rights reserved.
*
* This source code is only intended as a supplement to
* Microsoft Development Tools, q.v. for detailed
* information regarding the Microsoft samples programs.
*
* Abstract:
*
* Sample program to demonstrate how a program can display a
* "Send To" menu and send documents to items thereon.
*
*****************************************************************************/
#include "SendTo.h"
/*****************************************************************************
*
* Overview
*
* This sample program illustrates the following points:
*
* - Enumerating the contents of the "Send To" folder.
*
* - Obtaining the IDataObject interface for a file.
*
* - Obtaining the IDropTarget for a shell extension.
*
* - Dropping a file onto an item thus enumerated.
*
*****************************************************************************/
/*****************************************************************************
*
* Notes
*
* Since this program is written in C, invoking OLE methods is
* done via
*
* p->lpVtbl->MethodName(p, arg, arg)
*
* To convert this program to C++, the code that invokes OLE
* methods should be changed to
*
* p->MethodName(arg, arg)
*
* just like any other C++ method.
*
* For example,
*
* peidl->lpVtbl->Release(peidl);
*
* becomes
*
* peidl->Release();
*
* Also, REFIID parameters in C are passed as
*
* &IID_IName
*
* but they are passed in C++ as
*
* IID_IName
*
* Therefore,
*
* hres = g_psfDesktop->lpVtbl->BindToObject(g_psfDesktop,
* pidl, NULL, &IID_IShellFolder, (LPVOID *)&psf);
*
* becomes
*
* hres = g_psfDesktop->BindToObject(
* pidl, NULL, IID_IShellFolder, (LPVOID *)&psf);
*
*****************************************************************************/
HINSTANCE g_hinst; /* My hinstance */
LPSHELLFOLDER g_psfDesktop; /* The desktop folder */
HMENU g_hmenuSendTo; /* Our SendTo popup */
TCHAR g_szFileName[MAX_PATH]; /* The name of the "open" file */
/*****************************************************************************
*
* Our SendTo menu occupies the IDs IDM_SENDTOFIRST through IDM_SENDTOLAST.
* The text of the menu item is the name of the target, and the reference
* data points to the corresponding PIDL relative to the SENDTO folder.
*
*****************************************************************************/
/*****************************************************************************
*
* GetSpecialFolder
*
* Create an LPSHELLFOLDER for the specified special folder.
*
* It is the responsibility of the caller to Release() the
* IShellFolder that is returned.
*
*****************************************************************************/
LPSHELLFOLDER
GetSpecialFolder(HWND hwnd, int idFolder)
{
HRESULT hres;
LPSHELLFOLDER psf;
LPITEMIDLIST pidl;
hres = SHGetSpecialFolderLocation(hwnd, CSIDL_SENDTO, &pidl);
if (SUCCEEDED(hres)) {
hres = g_psfDesktop->lpVtbl->BindToObject(g_psfDesktop,
pidl, NULL, &IID_IShellFolder, (LPVOID *)&psf);
if (SUCCEEDED(hres)) {
/* Woo-hoo, we're done */
} else {
psf = NULL;
}
CoTaskMemFree(pidl);
} else {
psf = NULL;
}
return psf;
}
/*****************************************************************************
*
* PidlFromPath
*
* Convert a path to an LPITEMIDLIST.
*
*****************************************************************************/
LPITEMIDLIST
PidlFromPath(HWND hwnd, LPCTSTR pszPath)
{
LPITEMIDLIST pidl;
ULONG ulEaten;
DWORD dwAttributes;
HRESULT hres;
WCHAR wszName[MAX_PATH];
#ifdef UNICODE
if (FAILED(StringCchCopy(wszName, ARRAYSIZE(wszName), pszPath))) {
return NULL;
}
#else
if (!MultiByteToWideChar(CP_ACP, 0, pszPath, -1, wszName, ARRAYSIZE(wszName))) {
return NULL;
}
#endif
hres = g_psfDesktop->lpVtbl->ParseDisplayName(g_psfDesktop, hwnd,
NULL, wszName, &ulEaten, &pidl, &dwAttributes);
if (FAILED(hres)) {
return NULL;
}
return pidl;
}
/*****************************************************************************
*
* GetUIObjectOfAbsPidl
*
* Given an absolute (desktop-relative) LPITEMIDLIST, get the
* specified UI object.
*
*****************************************************************************/
HRESULT
GetUIObjectOfAbsPidl(HWND hwnd, LPITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
{
/*
* To get the UI object of an absolute pidl, we must first bind
* to its parent, and then call GetUIObjectOf on the last part.
*/
LPITEMIDLIST pidlLast;
LPSHELLFOLDER psf;
HRESULT hres;
/*
* Just for safety's sake.
*/
*ppvOut = NULL;
/*
* Bind to the parent folder of the item we are interested in.
*/
hres = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID *)&psf, &pidlLast);
if (FAILED(hres)) {
/*
* Couldn't even get to the parent; we have no chance of
* getting to the item itself.
*/
return hres;
}
/*
* Now ask the parent for the the UI object of the child.
*/
hres = psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidlLast,
riid, NULL, ppvOut);
/*
* Regardless of whether or not the GetUIObjectOf succeeded,
* we have no further use for the parent folder.
*/
psf->lpVtbl->Release(psf);
return hres;
}
/*****************************************************************************
*
* GetUIObjectOfPath
*
* Given an absolute path, get its specified UI object.
*
*****************************************************************************/
HRESULT
GetUIObjectOfPath(HWND hwnd, LPCTSTR pszPath, REFIID riid, LPVOID *ppvOut)
{
LPITEMIDLIST pidl;
HRESULT hres;
/*
* Just for safety's sake.
*/
*ppvOut = NULL;
pidl = PidlFromPath(hwnd, pszPath);
if (!pidl) {
return E_FAIL;
}
hres = GetUIObjectOfAbsPidl(hwnd, pidl, riid, ppvOut);
CoTaskMemFree(pidl);
return hres;
}
/*****************************************************************************
*
* DoDrop
*
* Drop a data object on a drop target.
*
*****************************************************************************/
void
DoDrop(LPDATAOBJECT pdto, LPDROPTARGET pdt)
{
POINTL pt = { 0, 0 };
DWORD dwEffect;
HRESULT hres;
/*
* The data object enters the drop target via the left button
* with all drop effects permitted.
*/
dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
hres = pdt->lpVtbl->DragEnter(pdt, pdto, MK_LBUTTON, pt, &dwEffect);
if (SUCCEEDED(hres) && dwEffect) {
/*
* The drop target likes the data object and the effect.
* Go drop it.
*/
hres = pdt->lpVtbl->Drop(pdt, pdto, MK_LBUTTON, pt, &dwEffect);
} else {
/*
* The drop target didn't like us. Tell it we're leaving,
* sorry to bother you.
*/
hres = pdt->lpVtbl->DragLeave(pdt);
}
}
/*****************************************************************************
*
* SendTo_OnCreate
*
* When we are created, remember the handle of our SendTo menu
* so we can recognize it later.
*
*****************************************************************************/
BOOL
SendTo_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
MENUITEMINFO mii;
HMENU hmenu;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_SUBMENU;
hmenu = GetMenu(hwnd);
if (GetMenuItemInfo(hmenu, IDM_SENDTOPOPUP, FALSE, &mii)) {
g_hmenuSendTo = mii.hSubMenu;
}
return TRUE;
}
/*****************************************************************************
*
* SendTo_ResetSendToMenu
*
* Wipe out all the items in the menu, freeing the associated memory.
*
*****************************************************************************/
void
SendTo_ResetSendToMenu(HMENU hmenu)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
while (GetMenuItemInfo(hmenu, 0, TRUE, &mii)) {
if (mii.dwItemData) {
CoTaskMemFree((LPVOID)mii.dwItemData);
}
DeleteMenu(hmenu, 0, MF_BYPOSITION);
}
}
/*****************************************************************************
*
* SendTo_FillSendToMenu
*
* Enumerate the contents of the SendTo folder and fill the
* menu with the items therein.
*
*****************************************************************************/
void
SendTo_FillSendToMenu(HWND hwnd, HMENU hmenu)
{
MENUITEMINFO mii;
LPSHELLFOLDER psf;
LPITEMIDLIST pidl;
LPENUMIDLIST peidl;
HRESULT hres;
STRRET str;
UINT idm = IDM_SENDTOFIRST;
psf = GetSpecialFolder(hwnd, CSIDL_SENDTO);
if (psf) {
hres = psf->lpVtbl->EnumObjects(psf, hwnd,
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
&peidl);
if (SUCCEEDED(hres)) {
while (peidl->lpVtbl->Next(peidl, 1, &pidl, NULL) == S_OK &&
idm < IDM_SENDTOLAST) {
hres = psf->lpVtbl->GetDisplayNameOf(psf, pidl,
SHGDN_NORMAL, &str);
if (SUCCEEDED(hres)) {
LPTSTR pszName;
hres = StrRetToStr(&str, pidl, &pszName);
if (SUCCEEDED(hres)) {
if (AppendMenu(hmenu, MF_ENABLED | MF_STRING,
idm, pszName)) {
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
mii.dwItemData = (ULONG_PTR)pidl;
SetMenuItemInfo(hmenu, idm, FALSE, &mii);
idm++;
}
CoTaskMemFree(pszName);
}
}
}
peidl->lpVtbl->Release(peidl);
}
psf->lpVtbl->Release(psf);
}
/*
* If the menu is still empty (the user has an empty SendTo folder),
* then add a disabled "None" item so we have at least something
* to display.
*/
if (idm == IDM_SENDTOFIRST) {
AppendMenu(hmenu, MF_GRAYED | MF_DISABLED | MF_STRING,
idm, TEXT("(none)"));
}
}
/*****************************************************************************
*
* SendTo_OnInitMenuPopup
*
* When the SendTo menu pops up, enumerate the contents of the
* SendTo folder and populate the menu.
*
*****************************************************************************/
void
SendTo_OnInitMenuPopup(HWND hwnd, HMENU hmenu, UINT item, BOOL fSystemMenu)
{
/*
* If it's the SendTo menu, then rebuild it.
*/
if (hmenu == g_hmenuSendTo) {
SendTo_ResetSendToMenu(hmenu);
SendTo_FillSendToMenu(hwnd, hmenu);
}
}
/*****************************************************************************
*
* SendTo_OnOpen
*
* "Open" a file. Just get its name and save it in our global
* g_szFileName variable. (And update the title too, just to
* look pretty.)
*
*****************************************************************************/
void
SendTo_OnOpen(HWND hwnd)
{
OPENFILENAME ofn;
TCHAR szFileName[MAX_PATH];
TCHAR szTitle[64];
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
StringCchCopy(szFileName, ARRAYSIZE(szFileName), g_szFileName);
ofn.lpstrFile = szFileName;
ofn.nMaxFile = ARRAYSIZE(szFileName);
ofn.lpstrFileTitle = szTitle;
ofn.nMaxFileTitle = ARRAYSIZE(szTitle);
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
if (GetOpenFileName(&ofn)) {
StringCchCopy(g_szFileName, ARRAYSIZE(g_szFileName), szFileName);
StringCchPrintf(szFileName, ARRAYSIZE(szFileName), TEXT("%s - SendTo Demo"), szTitle);
SetWindowText(hwnd, szFileName);
}
}
/*****************************************************************************
*
* SendTo_SendToItem
*
* The user selected an item from our SendTo menu.
*
* hwnd - window
* idm - menu item id
*
*****************************************************************************/
void
SendTo_SendToItem(HWND hwnd, int idm)
{
MENUITEMINFO mii;
LPSHELLFOLDER psf;
LPITEMIDLIST pidl;
LPDATAOBJECT pdto;
LPDROPTARGET pdt;
HRESULT hres;
/*
* First convert our filename to a data object.
*/
hres = GetUIObjectOfPath(hwnd, g_szFileName, &IID_IDataObject,
(LPVOID *)&pdto);
if (SUCCEEDED(hres)) {
/*
* Now go find the item we should send to.
*/
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA;
if (GetMenuItemInfo(g_hmenuSendTo, idm, FALSE, &mii) &&
mii.dwItemData) {
pidl = (LPITEMIDLIST)mii.dwItemData;
/*
* Now convert the send to pidl to a drop target.
*/
psf = GetSpecialFolder(hwnd, CSIDL_SENDTO);
if (psf) {
hres = psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidl,
&IID_IDropTarget, NULL, (LPVOID *)&pdt);
if (SUCCEEDED(hres)) {
/*
* Now drop the file on the drop target.
*/
DoDrop(pdto, pdt);
pdt->lpVtbl->Release(pdt);
}
psf->lpVtbl->Release(psf);
}
}
pdto->lpVtbl->Release(pdto);
}
}
/*****************************************************************************
*
* SendTo_OnCommand
*
* Handle our menu messages.
*
* Our "Send To" commands live in the range
* IDM_SENDTOFIRST through IDM_SENDTOLAST.
*
*****************************************************************************/
void
SendTo_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
if (id >= IDM_SENDTOFIRST && id <= IDM_SENDTOLAST) {
SendTo_SendToItem(hwnd, id);
} else if (id == IDM_OPEN) {
SendTo_OnOpen(hwnd);
}
}
/*****************************************************************************
*
* SendTo_OnDestroy
*
* When our window is destroyed, clean up the memory associated
* with the SendTo submenu.
*
* Also post a quit message because our application is over.
*
*****************************************************************************/
void
SendTo_OnDestroy(HWND hwnd)
{
if (g_hmenuSendTo) {
SendTo_ResetSendToMenu(g_hmenuSendTo);
}
PostQuitMessage(0);
}
/*****************************************************************************
*
* SendTo_WndProc
*
* Window procedure for the Send To demo.
*
*****************************************************************************/
LRESULT CALLBACK
SendTo_WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) {
HANDLE_MSG(hwnd, WM_CREATE, SendTo_OnCreate);
HANDLE_MSG(hwnd, WM_COMMAND, SendTo_OnCommand);
HANDLE_MSG(hwnd, WM_INITMENUPOPUP, SendTo_OnInitMenuPopup);
HANDLE_MSG(hwnd, WM_DESTROY, SendTo_OnDestroy);
}
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}
/*****************************************************************************
*
* InitApp
*
* Register our window classes and otherwise prepare for action.
*
*****************************************************************************/
BOOL
InitApp(void)
{
WNDCLASS wc;
HRESULT hr;
wc.style = 0;
wc.lpfnWndProc = SendTo_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hinst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = TEXT("SendToMenu");
wc.lpszClassName = TEXT("SendTo");
RegisterClass(&wc);
hr = SHGetDesktopFolder(&g_psfDesktop);
if (FAILED(hr)) {
return FALSE;
}
return TRUE;
}
/*****************************************************************************
*
* TermApp
*
* Clean up.
*
*****************************************************************************/
void
TermApp(void)
{
if (g_psfDesktop) {
g_psfDesktop->lpVtbl->Release(g_psfDesktop);
g_psfDesktop = NULL;
}
}
/*****************************************************************************
*
* WinMain
*
* Program entry point.
*
* Demonstrate Send To menu.
*
*****************************************************************************/
int WINAPI
WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine, int nCmdShow)
{
MSG msg;
HWND hwnd;
HRESULT hrInit;
g_hinst = hinst;
if (!InitApp()) return 0;
hrInit = CoInitialize(NULL);
hwnd = CreateWindow(
TEXT("SendTo"), /* Class Name */
TEXT("SendTo Demo"), /* Title */
WS_OVERLAPPEDWINDOW, /* Style */
CW_USEDEFAULT, CW_USEDEFAULT, /* Position */
CW_USEDEFAULT, CW_USEDEFAULT, /* Size */
NULL, /* Parent */
NULL, /* No menu */
hinst, /* Instance */
0); /* No special parameters */
ShowWindow(hwnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
TermApp();
if (SUCCEEDED(hrInit))
CoUninitialize();
return 0;
}