reactos/modules/rosapps/lib/vfdlib/vfdshmenu.cpp
Pierre Schweitzer 25c7e1a8d0
[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
floppy drives in ReactOS and mount images on them.
Only the cmd got imported. The GUI interface may come later on.
Note that, as for vcdrom, the driver is left disabled and you need to explicitely
start it through vfd command line interface.

CORE-14090
2017-12-16 21:48:34 +01:00

598 lines
12 KiB
C++

/*
vfdshmenu.cpp
Virtual Floppy Drive for Windows
Driver control library
COM shell extension class context menu functions
Copyright (c) 2003-2005 Ken Kato
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#include "vfdtypes.h"
#include "vfdapi.h"
#include "vfdlib.h"
#ifndef __REACTOS__
#include "vfdmsg.h"
#else
#include "vfdmsg_lib.h"
#endif
// class header
#include "vfdshext.h"
//
// Undocumented windows API to handle shell property sheets
//
typedef BOOL (WINAPI *SHOBJECTPROPERTIES)(
HWND hwnd, DWORD dwType, LPCWSTR lpObject, LPCWSTR lpPage);
#ifndef SHOP_FILEPATH
#define SHOP_FILEPATH 0x00000002
#endif
#define SHOP_EXPORT_ORDINAL 178
//
// Context Menu Items
//
enum {
VFD_CMD_OPEN = 0,
VFD_CMD_SAVE,
VFD_CMD_CLOSE,
VFD_CMD_PROTECT,
VFD_CMD_DROP,
VFD_CMD_PROP,
VFD_CMD_MAX
};
static struct _vfd_menu {
UINT textid; // menu item text id
UINT helpid; // menu item help id
#ifndef __REACTOS__
PCHAR verbA; // ansi verb text
PWCHAR verbW; // unicode verb text
#else
LPCSTR verbA; // ansi verb text
LPCWSTR verbW; // unicode verb text
#endif
}
g_VfdMenu[VFD_CMD_MAX] = {
{ MSG_MENU_OPEN, MSG_HELP_OPEN, "vfdopen", L"vfdopen" },
{ MSG_MENU_SAVE, MSG_HELP_SAVE, "vfdsave", L"vfdsave" },
{ MSG_MENU_CLOSE, MSG_HELP_CLOSE, "vfdclose", L"vfdclose" },
{ MSG_MENU_PROTECT, MSG_HELP_PROTECT, "protect", L"protect" },
{ MSG_MENU_DROP, MSG_HELP_DROP, "vfddrop", L"vfddrop" },
{ MSG_MENU_PROP, MSG_HELP_PROP, "vfdprop", L"vfdprop" },
};
//
// local functions
//
static void AddMenuItem(
HMENU hMenu,
UINT uPos,
UINT uFlags,
UINT uCmd,
UINT uText)
{
PSTR text = ModuleMessage(uText);
if (text) {
InsertMenu(hMenu, uPos, uFlags, uCmd, text);
LocalFree(text);
}
}
//
// FUNCTION: CVfdShExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
//
// PURPOSE: Called by the shell just before the context menu is displayed.
// This is where you add your specific menu items.
//
// PARAMETERS:
// hMenu - Handle to the context menu
// indexMenu - Index of where to begin inserting menu items
// idCmdFirst - Lowest value for new menu ID's
// idCmtLast - Highest value for new menu ID's
// uFlags - Specifies the context of the menu event
//
STDMETHODIMP CVfdShExt::QueryContextMenu(
HMENU hMenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
UNREFERENCED_PARAMETER(idCmdLast);
VFDTRACE(0, ("CVfdShExt::QueryContextMenu()\n"));
//
// Check if menu items should be added
//
if ((CMF_DEFAULTONLY & uFlags) ||
!m_pDataObj || m_nDevice == (ULONG)-1) {
VFDTRACE(0, ("Don't add any items.\n"));
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}
//
// Drag & Drop handler?
//
if (m_bDragDrop) {
VFDTRACE(0, ("Invoked as the Drop handler.\n"));
if (GetFileAttributes(m_sTarget) & FILE_ATTRIBUTE_DIRECTORY) {
// if the dropped item is a directory, nothing to do here
VFDTRACE(0, ("Dropped object is a directory.\n"));
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}
// Add a drop context menu item
AddMenuItem(
hMenu,
indexMenu,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_DROP,
g_VfdMenu[VFD_CMD_DROP].textid);
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, VFD_CMD_DROP + 1);
}
//
// Context menu handler
//
VFDTRACE(0, ("Invoked as the context menu handler.\n"));
//
// Get the VFD media state
//
HANDLE hDevice = VfdOpenDevice(m_nDevice);
if (hDevice == INVALID_HANDLE_VALUE) {
VFDTRACE(0, ("device open failed.\n"));
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}
DWORD status = VfdGetMediaState(hDevice);
CloseHandle(hDevice);
//
// Add context menu items
//
InsertMenu(hMenu, indexMenu++,
MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
if (status == ERROR_SUCCESS ||
status == ERROR_WRITE_PROTECT) {
// An image is opened
// insert the "save" menu item
AddMenuItem(
hMenu,
indexMenu++,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_SAVE,
g_VfdMenu[VFD_CMD_SAVE].textid);
// insert the "close" menu item
AddMenuItem(
hMenu,
indexMenu++,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_CLOSE,
g_VfdMenu[VFD_CMD_CLOSE].textid);
// insert the "protect" menu item
AddMenuItem(
hMenu,
indexMenu++,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_PROTECT,
g_VfdMenu[VFD_CMD_PROTECT].textid);
// check "protect" menu item
if (status == ERROR_WRITE_PROTECT) {
CheckMenuItem(hMenu, indexMenu - 1,
MF_BYPOSITION | MF_CHECKED);
}
}
else {
// The drive is empty
// insert the "open" menu item
AddMenuItem(
hMenu,
indexMenu++,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_OPEN,
g_VfdMenu[VFD_CMD_OPEN].textid);
}
// Insert the "proterty" menu item
AddMenuItem(
hMenu,
indexMenu++,
MF_BYPOSITION | MF_STRING,
idCmdFirst + VFD_CMD_PROP,
g_VfdMenu[VFD_CMD_PROP].textid);
// Insert a separator
InsertMenu(hMenu, indexMenu,
MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, VFD_CMD_PROP + 1);
}
//
// FUNCTION: CVfdShExt::GetCommandString(LPCMINVOKECOMMANDINFO)
//
// PURPOSE: Retrieves information about a shortcut menu command,
// including the Help string and the language-independent,
// or canonical, name for the command.
//
// PARAMETERS:
// idCmd - Menu command identifier offset.
// uFlags - Flags specifying the information to return.
// This parameter can have one of the following values.
// GCS_HELPTEXTA Sets pszName to an ANSI string containing the Help text for the command.
// GCS_HELPTEXTW Sets pszName to a Unicode string containing the Help text for the command.
// GCS_VALIDATEA Returns S_OK if the menu item exists, or S_FALSE otherwise.
// GCS_VALIDATEW Returns S_OK if the menu item exists, or S_FALSE otherwise.
// GCS_VERBA Sets pszName to an ANSI string containing the language-independent command name for the menu item.
// GCS_VERBW Sets pszName to a Unicode string containing the language-independent command name for the menu item.
// pwReserved - Reserved. Applications must specify NULL when calling this method, and handlers must ignore this parameter when called.
// pszName - Address of the buffer to receive the null-terminated string being retrieved.
// cchMax - Size of the buffer to receive the null-terminated string.
//
STDMETHODIMP CVfdShExt::GetCommandString(
UINT idCmd,
UINT uFlags,
UINT *reserved,
LPSTR pszName,
UINT cchMax)
{
VFDTRACE(0,
("CVfdShExt::GetCommandString(%u,...)\n", idCmd));
UNREFERENCED_PARAMETER(reserved);
if (idCmd >= sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0])) {
return S_FALSE;
}
switch (uFlags) {
case GCS_HELPTEXTA:
FormatMessageA(
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
g_hDllModule, g_VfdMenu[idCmd].helpid,
0, pszName, cchMax, NULL);
VFDTRACE(0, ("HELPTEXTA: %s\n", pszName));
break;
case GCS_HELPTEXTW:
FormatMessageW(
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
g_hDllModule, g_VfdMenu[idCmd].helpid,
0, (LPWSTR)pszName, cchMax, NULL);
VFDTRACE(0, ("HELPTEXTW: %ws\n", pszName));
break;
case GCS_VERBA:
lstrcpynA(pszName, g_VfdMenu[idCmd].verbA, cchMax);
break;
case GCS_VERBW:
lstrcpynW((LPWSTR)pszName, g_VfdMenu[idCmd].verbW, cchMax);
break;
}
return NOERROR;
}
//
// FUNCTION: CVfdShExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
STDMETHODIMP CVfdShExt::InvokeCommand(
LPCMINVOKECOMMANDINFO lpcmi)
{
VFDTRACE(0, ("CVfdShExt::InvokeCommand()\n"));
BOOL unicode = FALSE;
UINT id;
DWORD ret;
CMINVOKECOMMANDINFOEX *excmi = (CMINVOKECOMMANDINFOEX *)lpcmi;
if (lpcmi->cbSize >= sizeof(CMINVOKECOMMANDINFOEX) &&
(lpcmi->fMask & CMIC_MASK_UNICODE)) {
unicode = TRUE;
}
if (!unicode && HIWORD(lpcmi->lpVerb)) {
VFDTRACE(0, ("ANSI: %s\n", lpcmi->lpVerb));
// ANSI verb
for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
if (!lstrcmpi(lpcmi->lpVerb, g_VfdMenu[id].verbA)) {
break;
}
}
}
else if (unicode && HIWORD(excmi->lpVerbW)) {
VFDTRACE(0, ("UNICODE: %ws\n", excmi->lpVerbW));
// UNICODE verb
for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
if (!lstrcmpiW(excmi->lpVerbW, g_VfdMenu[id].verbW)) {
break;
}
}
}
else {
VFDTRACE(0, ("Command: %u\n", LOWORD(lpcmi->lpVerb)));
// Command ID
id = LOWORD(lpcmi->lpVerb);
}
VFDTRACE(0, ("MenuItem: %u\n", id));
switch (id) {
case VFD_CMD_OPEN:
ret = DoVfdOpen(lpcmi->hwnd);
if (ret == ERROR_SUCCESS) {
VfdImageTip(lpcmi->hwnd, m_nDevice);
}
break;
case VFD_CMD_SAVE:
ret = DoVfdSave(lpcmi->hwnd);
break;
case VFD_CMD_CLOSE:
ret = DoVfdClose(lpcmi->hwnd);
break;
case VFD_CMD_PROTECT:
ret = DoVfdProtect(lpcmi->hwnd);
if (ret == ERROR_SUCCESS) {
VfdImageTip(lpcmi->hwnd, m_nDevice);
}
else if (ret == ERROR_WRITE_PROTECT) {
VfdImageTip(lpcmi->hwnd, m_nDevice);
ret = ERROR_SUCCESS;
}
break;
case VFD_CMD_DROP:
ret = DoVfdDrop(lpcmi->hwnd);
if (ret == ERROR_SUCCESS) {
VfdImageTip(lpcmi->hwnd, m_nDevice);
}
break;
case VFD_CMD_PROP:
{
SHOBJECTPROPERTIES pSHObjectProperties;
WCHAR path[4] = L" :\\";
pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
LoadLibrary("shell32"), "SHObjectProperties");
if (!pSHObjectProperties) {
pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
LoadLibrary("shell32"), (LPCSTR)SHOP_EXPORT_ORDINAL);
}
if (pSHObjectProperties) {
path[0] = m_sTarget[0];
pSHObjectProperties(lpcmi->hwnd,
SHOP_FILEPATH, path, L"VFD");
}
}
ret = ERROR_SUCCESS;
break;
default:
return E_INVALIDARG;
}
if (ret != ERROR_SUCCESS &&
ret != ERROR_CANCELLED) {
MessageBox(lpcmi->hwnd,
SystemMessage(ret), VFD_MSGBOX_TITLE, MB_ICONSTOP);
}
return NOERROR;
}
//=====================================
// perform VFD menu operation
//=====================================
DWORD CVfdShExt::DoVfdOpen(
HWND hParent)
{
DWORD ret = VfdGuiOpen(hParent, m_nDevice);
if (ret != ERROR_SUCCESS && ret != ERROR_CANCELLED) {
MessageBox(hParent, SystemMessage(ret),
VFD_MSGBOX_TITLE, MB_ICONSTOP);
}
return ret;
}
//
// Save the VFD image
//
DWORD CVfdShExt::DoVfdSave(
HWND hParent)
{
return VfdGuiSave(hParent, m_nDevice);
}
//
// Close current VFD image
//
DWORD CVfdShExt::DoVfdClose(
HWND hParent)
{
return VfdGuiClose(hParent, m_nDevice);
}
//
// Enable/disable media write protection
//
DWORD CVfdShExt::DoVfdProtect(
HWND hParent)
{
HANDLE hDevice;
DWORD ret;
UNREFERENCED_PARAMETER(hParent);
VFDTRACE(0, ("CVfdShExt::DoVfdProtect()\n"));
hDevice = VfdOpenDevice(m_nDevice);
if (hDevice == INVALID_HANDLE_VALUE) {
return GetLastError();
}
ret = VfdGetMediaState(hDevice);
if (ret == ERROR_SUCCESS) {
ret = VfdWriteProtect(hDevice, TRUE);
}
else if (ret == ERROR_WRITE_PROTECT) {
ret = VfdWriteProtect(hDevice, FALSE);
}
if (ret == ERROR_SUCCESS) {
ret = VfdGetMediaState(hDevice);
}
CloseHandle(hDevice);
return ret;
}
//
// Open dropped file with VFD
//
DWORD CVfdShExt::DoVfdDrop(
HWND hParent)
{
HANDLE hDevice;
DWORD file_attr;
ULONG file_size;
VFD_FILETYPE file_type;
VFD_DISKTYPE disk_type;
VFD_MEDIA media_type;
DWORD ret;
VFDTRACE(0, ("CVfdShExt::DoVfdDropOpen()\n"));
// check if dropped file is a valid image
ret = VfdCheckImageFile(
m_sTarget, &file_attr, &file_type, &file_size);
if (ret != ERROR_SUCCESS) {
return ret;
}
// check file size
media_type = VfdLookupMedia(file_size);
if (!media_type) {
PSTR msg = ModuleMessage(MSG_FILE_TOO_SMALL);
MessageBox(hParent, msg ? msg : "Bad size",
VFD_MSGBOX_TITLE, MB_ICONSTOP);
if (msg) {
LocalFree(msg);
}
return ERROR_CANCELLED;
}
if ((file_type == VFD_FILETYPE_ZIP) ||
(file_attr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))) {
disk_type = VFD_DISKTYPE_RAM;
}
else {
disk_type = VFD_DISKTYPE_FILE;
}
// close current image (if opened)
ret = DoVfdClose(hParent);
if (ret != ERROR_SUCCESS &&
ret != ERROR_NOT_READY) {
return ret;
}
// open dropped file
hDevice = VfdOpenDevice(m_nDevice);
if (hDevice == INVALID_HANDLE_VALUE) {
return GetLastError();
}
ret = VfdOpenImage(
hDevice, m_sTarget, disk_type, media_type, FALSE);
CloseHandle(hDevice);
return ret;
}