1726 lines
53 KiB
C++
1726 lines
53 KiB
C++
// Copyright : 2024-11-30 Yutaka Sawada
|
||
// License : The MIT license
|
||
|
||
// ShellExt.cpp : DLL アプリケーション用のエントリ ポイントを定義します。
|
||
//
|
||
|
||
#ifndef _UNICODE
|
||
#define _UNICODE
|
||
#endif
|
||
#ifndef UNICODE
|
||
#define UNICODE
|
||
#endif
|
||
#ifndef _WIN32_WINNT
|
||
#define _WIN32_WINNT 0x0601 // Windows 7 or later
|
||
#endif
|
||
|
||
#include <windows.h>
|
||
#include <shlobj.h>
|
||
#include <uxtheme.h>
|
||
|
||
#pragma comment(lib, "uxtheme.lib")
|
||
|
||
|
||
// 定義
|
||
#define MAX_LEN 1024 // ファイル名の最大文字数 (末尾のNULL文字も含む)
|
||
//#define DEBUG_OUTPUT // デバッグ出力するかどうか
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
// クラスファクトリの作成 (IClassFactoryインターフェイスを継承する)
|
||
class CShellExtClassFactory : public IClassFactory
|
||
{
|
||
private:
|
||
// 参照カウント
|
||
long m_cRef;
|
||
|
||
public:
|
||
// コンストラクタ・デストラクタ
|
||
CShellExtClassFactory();
|
||
~CShellExtClassFactory();
|
||
|
||
//IUnknown インターフェイスのメソッド
|
||
STDMETHODIMP QueryInterface(REFIID, void **);
|
||
STDMETHODIMP_(ULONG) AddRef();
|
||
STDMETHODIMP_(ULONG) Release();
|
||
|
||
//IClassFactory インターフェイスのメソッド
|
||
STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
|
||
STDMETHODIMP LockServer(BOOL);
|
||
};
|
||
|
||
class CShellExtension : public IShellExtInit,
|
||
public IContextMenu
|
||
{
|
||
private:
|
||
long m_cRef; // オブジェクトの参照カウント
|
||
LPDATAOBJECT m_pDataObj; // エクスプローラから受け取るデータオブジェクト
|
||
int single_file;
|
||
int CheckData(void); // 選択したファイルの検査
|
||
int DoCommand(UINT idCmd); // 選択した拡張メニューのコマンド実行
|
||
int DoCommand7zip(void);
|
||
|
||
public:
|
||
// コンストラクタ・デストラクタ
|
||
CShellExtension();
|
||
~CShellExtension();
|
||
|
||
// IUnknown インターフェイスのメソッド
|
||
STDMETHODIMP QueryInterface(REFIID, void **);
|
||
STDMETHODIMP_(ULONG) AddRef();
|
||
STDMETHODIMP_(ULONG) Release();
|
||
|
||
// IShellExtInit インターフェイスのメソッド
|
||
STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
|
||
|
||
// IContextMenu インターフェイスのメソッド
|
||
STDMETHODIMP QueryContextMenu(
|
||
HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
|
||
STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
|
||
STDMETHODIMP GetCommandString(
|
||
UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax);
|
||
};
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
// {333EFDA5-A74E-4df4-A225-92A7AF81F29A}
|
||
static const GUID CLSID_ShellExt =
|
||
{ 0x333efda5, 0xa74e, 0x4df4, { 0xa2, 0x25, 0x92, 0xa7, 0xaf, 0x81, 0xf2, 0x9a } };
|
||
|
||
HINSTANCE g_inst = NULL;
|
||
HBITMAP g_bmp, g_bmp2;
|
||
long g_cRefDll = 0;
|
||
|
||
#define TOTAL_LENGTH 256
|
||
#define MIN_LENGTH 2
|
||
|
||
// 動作設定 (MultiPar.ini から読み込む)
|
||
int menu_behavior;
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
// デバッグ用の作業領域
|
||
wchar_t debug_buf[MAX_LEN];
|
||
HANDLE hDebug = NULL;
|
||
|
||
// ファイルを開いて末尾に移動する
|
||
BOOL open_debug_file(void)
|
||
{
|
||
DWORD len = GetModuleFileName(g_inst, debug_buf, MAX_LEN);
|
||
if ((len > 0) && (len < MAX_LEN)){
|
||
while (len > 0){
|
||
len--;
|
||
if (debug_buf[len] == '\\'){
|
||
len++;
|
||
break;
|
||
}
|
||
}
|
||
debug_buf[len] = 0; // ファイル名を消す
|
||
wcscat(debug_buf, L"debug.txt");
|
||
// ファイルを開いて末尾に移動する
|
||
hDebug = CreateFile(debug_buf, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL);
|
||
if (hDebug != INVALID_HANDLE_VALUE){
|
||
len = SetFilePointer(hDebug, 0, NULL, FILE_END);
|
||
if (len == 0){
|
||
// BOMを書き込む
|
||
debug_buf[0] = 0xFEFF;
|
||
debug_buf[1] = 0;
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
hDebug = NULL;
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
// 管理者権限で動いてるか調べる Administrator privileges
|
||
// http://umezawa.dyndns.info/wordpress/?p=5191
|
||
static BOOL check_admin(void)
|
||
{
|
||
HANDLE hToken;
|
||
TOKEN_ELEVATION elevation;
|
||
DWORD cb;
|
||
|
||
if (OpenProcessToken(GetCurrentProcess(), GENERIC_READ, &hToken)){
|
||
if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &cb)){
|
||
if (elevation.TokenIsElevated){ // Run as Administrator
|
||
TOKEN_ELEVATION_TYPE elevtype;
|
||
if (GetTokenInformation(hToken, TokenElevationType, &elevtype, sizeof(elevtype), &cb)){
|
||
if (elevtype == TokenElevationTypeDefault){
|
||
// User is administrator and UAC (User Account Control) is disabled.
|
||
CloseHandle(hToken);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
CloseHandle(hToken);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// 7-Zip のパスが正しいか確認してキーを閉じる
|
||
static int check_path_7zip(HKEY hKey, wchar_t *buf)
|
||
{
|
||
unsigned long ret, type, size;
|
||
|
||
size = (MAX_PATH - 7) * 2;
|
||
ret = RegQueryValueEx(hKey, L"Path", NULL, &type, (LPBYTE)buf, &size);
|
||
if (ret == ERROR_SUCCESS){
|
||
if ((type == REG_SZ) && (size >= 6) && (buf[size / 2 - 2] == '\\')){
|
||
// 7-Zip の実行ファイルが存在するか確かめる
|
||
wcscat(buf, L"7zG.exe");
|
||
type = GetFileAttributes(buf);
|
||
if ((type == INVALID_FILE_ATTRIBUTES) || (type & FILE_ATTRIBUTE_DIRECTORY)){
|
||
buf[0] = 0;
|
||
ret = 100002; // ファイルが存在しない
|
||
}
|
||
} else {
|
||
buf[0] = 0;
|
||
ret = 100001; // key は存在するけど形式が違う
|
||
}
|
||
}
|
||
RegCloseKey(hKey);
|
||
return ret;
|
||
}
|
||
|
||
// 7-Zip のディレクトリを取得する
|
||
static void get_path_7zip(wchar_t *buf)
|
||
{
|
||
int ret;
|
||
HKEY hKey;
|
||
|
||
buf[0] = 0; // 正常に取得できた場合は、ここに値が入る
|
||
ret = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\7-Zip", 0, KEY_READ, &hKey);
|
||
if (ret == ERROR_SUCCESS)
|
||
ret = check_path_7zip(hKey, buf);
|
||
|
||
if (ret != ERROR_SUCCESS){
|
||
// 関連付けやインストール時の選択肢によっては HKLM に値が記録される?32-bit 版の 7-Zip
|
||
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\7-Zip", 0, KEY_READ, &hKey);
|
||
if (ret == ERROR_SUCCESS)
|
||
ret = check_path_7zip(hKey, buf);
|
||
}
|
||
|
||
if (ret != ERROR_SUCCESS){
|
||
// 64-bit 版の 7-Zip のキーは 32-bit 用のレジストリには書き込まれてない。
|
||
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\7-Zip", 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
|
||
if (ret == ERROR_SUCCESS)
|
||
check_path_7zip(hKey, buf);
|
||
}
|
||
}
|
||
|
||
static void InitBitmapInfo(BITMAPINFO *pbmi, LONG cx, LONG cy)
|
||
{
|
||
ZeroMemory(pbmi, sizeof(BITMAPINFO));
|
||
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||
pbmi->bmiHeader.biWidth = cx;
|
||
pbmi->bmiHeader.biHeight = cy;
|
||
pbmi->bmiHeader.biPlanes = 1;
|
||
pbmi->bmiHeader.biBitCount = 32;
|
||
pbmi->bmiHeader.biCompression = BI_RGB;
|
||
}
|
||
|
||
// メニュー・アイコン用のビットマップを読み込む
|
||
static int load_menu_icon(void)
|
||
{
|
||
int size_w, size_h;
|
||
HICON hIcon;
|
||
LPVOID lpBits;
|
||
BITMAPINFO bmi;
|
||
|
||
if (g_bmp != NULL)
|
||
return 0;
|
||
|
||
size_w = GetSystemMetrics(SM_CXSMICON);
|
||
size_h = GetSystemMetrics(SM_CYSMICON);
|
||
|
||
// ARGB 形式のビットマップを作成する
|
||
InitBitmapInfo(&bmi, size_w, size_h);
|
||
g_bmp = CreateDIBSection(NULL, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, &lpBits, NULL, 0);
|
||
if (g_bmp == NULL)
|
||
return 1;
|
||
|
||
// アイコンをロードする
|
||
hIcon = (HICON)LoadImage(g_inst, MAKEINTRESOURCE(100), IMAGE_ICON, size_w, size_h, LR_DEFAULTCOLOR);
|
||
if (hIcon == NULL){
|
||
DeleteObject(g_bmp);
|
||
g_bmp = NULL;
|
||
return 2;
|
||
}
|
||
|
||
// ビットマップに ARGB 形式のアイコンをコピーする(アルファ値が無いと見た目がおかしくなる)
|
||
HDC hDC;
|
||
HBITMAP prev_bmp;
|
||
hDC = CreateCompatibleDC(NULL);
|
||
if (hDC == NULL){
|
||
DestroyIcon(hIcon);
|
||
DeleteObject(g_bmp);
|
||
g_bmp = NULL;
|
||
return 3;
|
||
}
|
||
prev_bmp = (HBITMAP)SelectObject(hDC, g_bmp);
|
||
if (DrawIconEx(hDC, 0, 0, hIcon, size_w, size_h, 0, NULL, DI_NORMAL) == 0){
|
||
SelectObject(hDC, prev_bmp);
|
||
DeleteDC(hDC);
|
||
DestroyIcon(hIcon);
|
||
DeleteObject(g_bmp);
|
||
g_bmp = NULL;
|
||
return 4;
|
||
}
|
||
SelectObject(hDC, prev_bmp);
|
||
DeleteDC(hDC);
|
||
DestroyIcon(hIcon);
|
||
|
||
return 0;
|
||
}
|
||
|
||
// Uxtheme.h を参照すること。Windows Vista 以降なら対応してる
|
||
typedef DWORD ARGB;
|
||
|
||
static int ConvertToPARGB32(HDC hdc, __inout ARGB *pargb, HBITMAP hbmp, int size_x, int size_y, int cxRow)
|
||
{
|
||
BITMAPINFO bmi;
|
||
InitBitmapInfo(&bmi, size_x, size_y);
|
||
|
||
HRESULT hr = E_OUTOFMEMORY;
|
||
HANDLE hHeap = GetProcessHeap();
|
||
void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);
|
||
if (pvBits){
|
||
hr = E_UNEXPECTED;
|
||
if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight){
|
||
ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;
|
||
ARGB *pargbMask = static_cast<ARGB *>(pvBits);
|
||
|
||
for (ULONG y = bmi.bmiHeader.biHeight; y; --y){
|
||
for (ULONG x = bmi.bmiHeader.biWidth; x; --x){
|
||
if (*pargbMask++){ // transparent pixel
|
||
*pargb++ = 0;
|
||
} else { // opaque pixel
|
||
*pargb++ |= 0xFF000000;
|
||
}
|
||
}
|
||
|
||
pargb += cxDelta;
|
||
}
|
||
|
||
hr = S_OK;
|
||
}
|
||
|
||
HeapFree(hHeap, 0, pvBits);
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
static bool HasAlpha(__in ARGB *pargb, int size_x, int size_y, int cxRow)
|
||
{
|
||
int cxDelta = cxRow - size_x;
|
||
for (int y = size_y; y; --y){
|
||
for (int x = size_x; x; --x){
|
||
if (*pargb++ & 0xFF000000){
|
||
return true;
|
||
}
|
||
}
|
||
|
||
pargb += cxDelta;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// 実行ファイルからメニュー・アイコン用のビットマップを読み込む
|
||
static int load_menu_icon2(wchar_t *file_path)
|
||
{
|
||
int size_w, size_h, rv;
|
||
HICON hIcon;
|
||
LPVOID lpBits;
|
||
BITMAPINFO bmi;
|
||
|
||
if (g_bmp2 != NULL)
|
||
return 0;
|
||
|
||
size_w = GetSystemMetrics(SM_CXSMICON);
|
||
size_h = GetSystemMetrics(SM_CYSMICON);
|
||
|
||
// ARGB 形式のビットマップを作成する
|
||
InitBitmapInfo(&bmi, size_w, size_h);
|
||
g_bmp2 = CreateDIBSection(NULL, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, &lpBits, NULL, 0);
|
||
if (g_bmp2 == NULL){
|
||
return 3;
|
||
}
|
||
|
||
// アイコンを実行ファイルから読み取る
|
||
rv = ExtractIconEx(file_path, 0, NULL, &hIcon, 1);
|
||
if ((hIcon == NULL) || (rv != 1)){
|
||
DeleteObject(g_bmp2);
|
||
g_bmp2 = NULL;
|
||
return 4;
|
||
}
|
||
|
||
// ビットマップにアイコンをコピーする
|
||
HDC hDC;
|
||
HBITMAP prev_bmp;
|
||
hDC = CreateCompatibleDC(NULL);
|
||
if (hDC == NULL){
|
||
DeleteObject(g_bmp2);
|
||
g_bmp2 = NULL;
|
||
DestroyIcon(hIcon);
|
||
return 5;
|
||
}
|
||
prev_bmp = (HBITMAP)SelectObject(hDC, g_bmp2);
|
||
// ARGB形式に変換する作業バッファーを用意する
|
||
BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
|
||
BP_PAINTPARAMS paintParams = {0};
|
||
paintParams.cbSize = sizeof(paintParams);
|
||
paintParams.dwFlags = BPPF_ERASE;
|
||
paintParams.pBlendFunction = &bfAlpha;
|
||
HDC hdcBuffer;
|
||
RECT rcIcon;
|
||
SetRect(&rcIcon, 0, 0, size_w, size_h);
|
||
HPAINTBUFFER hPaintBuffer = BeginBufferedPaint(hDC, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);
|
||
if (hPaintBuffer){
|
||
if (DrawIconEx(hdcBuffer, 0, 0, hIcon, size_w, size_h, 0, NULL, DI_NORMAL) == 0){
|
||
EndBufferedPaint(hPaintBuffer, FALSE);
|
||
SelectObject(hDC, prev_bmp);
|
||
DeleteDC(hDC);
|
||
DestroyIcon(hIcon);
|
||
DeleteObject(g_bmp2);
|
||
g_bmp2 = NULL;
|
||
return 6;
|
||
}
|
||
// ConvertBufferToPARGB32 の間はエラーになってもアルファ無しのアイコンを表示する
|
||
RGBQUAD *prgbQuad;
|
||
int cxRow;
|
||
if (GetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow) == S_OK){
|
||
ARGB *pargb = reinterpret_cast<ARGB *>(prgbQuad);
|
||
if (!HasAlpha(pargb, size_w, size_h, cxRow)){ // アルファ値が無ければ
|
||
ICONINFO info;
|
||
if (GetIconInfo(hIcon, &info)){
|
||
if (info.hbmMask)
|
||
ConvertToPARGB32(hdcBuffer, pargb, info.hbmMask, size_w, size_h, cxRow);
|
||
DeleteObject(info.hbmColor);
|
||
DeleteObject(info.hbmMask);
|
||
}
|
||
}
|
||
}
|
||
EndBufferedPaint(hPaintBuffer, TRUE);
|
||
}
|
||
SelectObject(hDC, prev_bmp);
|
||
DeleteDC(hDC);
|
||
DestroyIcon(hIcon);
|
||
|
||
return 0;
|
||
}
|
||
|
||
// 言語別のテキスト (MultiParShlExt.ini から読み込む)
|
||
int offset_c, offset_v, offset_a;
|
||
wchar_t menu_item[TOTAL_LENGTH];
|
||
|
||
static int load_setting(void)
|
||
{
|
||
wchar_t path[MAX_PATH], path2[MAX_PATH], lang_num[8];
|
||
int rv, lang_id;
|
||
|
||
// 設定ファイルが存在するディレクトリ
|
||
// DLL と呼び出す EXE ファイルは同じディレクトリに置くこと
|
||
rv = GetModuleFileName(g_inst, path, MAX_PATH);
|
||
if ((rv == 0) || (rv >= MAX_PATH))
|
||
return 1;
|
||
while (rv > 0){
|
||
rv--;
|
||
if (path[rv] == '\\'){
|
||
path[rv] = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// アプリケーション・データのディレクトリを決める
|
||
rv = 0;
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, path2))){
|
||
//MessageBox(NULL, path2, L"program files path", MB_OK);
|
||
// ProgramFiles の位置と比較する
|
||
int i, max = 0;
|
||
while (path2[max] != 0)
|
||
max++;
|
||
for (i = 0; i < max; i++){
|
||
if (path2[i] != path[i])
|
||
break;
|
||
}
|
||
if (i == max){
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path2))){
|
||
//MessageBox(NULL, path2, L"application data path", MB_OK);
|
||
i = 0;
|
||
while (path2[i] != 0)
|
||
i++;
|
||
if (i < MAX_PATH - 10 - 18){
|
||
wcscat(path2, L"\\MultiPar");
|
||
rv = 2; // AppData を使う
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (rv != 2)
|
||
wcscpy(path2, path); // DLL が存在するディレクトリを使う
|
||
wcscat(path2, L"\\MultiPar.ini");
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
wsprintf(debug_buf, L"MultiPar.ini path = %s\r\n", path2);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// 動作設定を読み込む
|
||
menu_behavior = GetPrivateProfileInt(L"Option", L"ShellExtension", 0, path2);
|
||
lang_id = GetPrivateProfileInt(L"Option", L"Language", -1, path2);
|
||
|
||
if (menu_behavior & 64){ // サブ・メニューを全て表示しない
|
||
menu_behavior |= (8 | 32);
|
||
} else if (menu_behavior & 8){ // サブ・メニューを作成だけにする
|
||
menu_behavior |= 32;
|
||
}
|
||
|
||
if ((menu_behavior & 16) == 0){ // メニューにアイコンを付ける
|
||
if (load_menu_icon() != 0)
|
||
menu_behavior |= 16; // アイコン作成失敗
|
||
}
|
||
|
||
if ((menu_behavior & 32) == 0){ // 7-Zip がインストールされてなければ使わない
|
||
get_path_7zip(path2);
|
||
if (path2[0] == 0){
|
||
menu_behavior |= 32; // メニューを表示しない
|
||
} else if ((menu_behavior & 16) == 0){
|
||
load_menu_icon2(path2); // アイコン取得に失敗してもメニューは表示する
|
||
}
|
||
}
|
||
|
||
// 言語ごとのメニュー項目を取得する
|
||
wcscat(path, L"\\MultiParShlExt.ini");
|
||
if (GetFileAttributes(path) == INVALID_FILE_ATTRIBUTES)
|
||
return 1; // 言語別テキストが無い
|
||
// 指定された言語のテキストを読み込む
|
||
if (lang_id < 0)
|
||
lang_id = GetUserDefaultLangID();
|
||
wsprintf(lang_num, L"0x%04x", lang_id);
|
||
rv = GetPrivateProfileString(lang_num, L"MenuTitle", L"", menu_item, TOTAL_LENGTH, path);
|
||
if (rv < MIN_LENGTH){ // その言語の設定項目が無いので、読み込みに失敗した
|
||
// 同じ言語に複数の地域が割り振られてる場合は別ので代用する
|
||
unsigned int pri_id, sub_id;
|
||
pri_id = lang_id & 0x03FF; // PRIMARYLANGID だけにする
|
||
for (sub_id = 1; sub_id < 64; sub_id++){ // SUBLANGID を変更する
|
||
lang_id = pri_id | (sub_id << 10);
|
||
wsprintf(lang_num, L"0x%04x", lang_id);
|
||
rv = GetPrivateProfileString(lang_num, L"MenuTitle", L"", menu_item, TOTAL_LENGTH, path);
|
||
if (rv >= MIN_LENGTH)
|
||
break;
|
||
}
|
||
if (rv < MIN_LENGTH){ // 英語のリソースが存在すればそれで代用する
|
||
lang_id = 0x409;
|
||
wsprintf(lang_num, L"0x%04x", lang_id);
|
||
rv = GetPrivateProfileString(lang_num, L"MenuTitle", L"", menu_item, TOTAL_LENGTH, path);
|
||
}
|
||
}
|
||
if (rv >= MIN_LENGTH){ // 設定項目を読み込み成功した
|
||
offset_c = rv + 1;
|
||
if (offset_c >= TOTAL_LENGTH - MIN_LENGTH)
|
||
return 1;
|
||
rv = GetPrivateProfileString(lang_num, L"Create", L"", menu_item + offset_c, TOTAL_LENGTH - offset_c, path);
|
||
if (rv < MIN_LENGTH)
|
||
return 1;
|
||
offset_v = offset_c + rv + 1;
|
||
if (offset_v >= TOTAL_LENGTH - MIN_LENGTH){
|
||
menu_behavior |= 8; // disable sub-menu for Verify
|
||
menu_behavior |= 32; // disable archive
|
||
return 0;
|
||
}
|
||
if ((menu_behavior & 8) == 0){
|
||
rv = GetPrivateProfileString(lang_num, L"Verify", L"", menu_item + offset_v, TOTAL_LENGTH - offset_v, path);
|
||
offset_a = offset_v + rv + 1;
|
||
if (rv < MIN_LENGTH)
|
||
menu_behavior |= 8; // disable sub-menu for Verify
|
||
if (offset_a >= TOTAL_LENGTH - MIN_LENGTH){
|
||
menu_behavior |= 32; // disable archive
|
||
return 0;
|
||
}
|
||
} else {
|
||
offset_a = offset_v;
|
||
}
|
||
if ((menu_behavior & 32) == 0){
|
||
rv = GetPrivateProfileString(lang_num, L"Archive", L"", menu_item + offset_a, TOTAL_LENGTH - offset_a, path);
|
||
if (rv < MIN_LENGTH){
|
||
menu_behavior |= 32; // disable archive
|
||
return 0;
|
||
}
|
||
}
|
||
} else {
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
extern "C" BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
||
{
|
||
if (dwReason == DLL_PROCESS_ATTACH){
|
||
g_inst = hInstance;
|
||
DisableThreadLibraryCalls(hInstance);
|
||
g_bmp = NULL;
|
||
g_bmp2 = NULL;
|
||
menu_behavior = -1; // 設定はまだ読み込まれてない
|
||
} else if (dwReason == DLL_PROCESS_DETACH){
|
||
if (g_bmp != NULL){
|
||
DeleteObject(g_bmp);
|
||
g_bmp = NULL;
|
||
}
|
||
if (g_bmp2 != NULL){
|
||
DeleteObject(g_bmp2);
|
||
g_bmp2 = NULL;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
STDAPI DllCanUnloadNow(void)
|
||
{
|
||
#ifdef DEBUG_OUTPUT
|
||
if ((hDebug != NULL) && (g_cRefDll == 0)){
|
||
SYSTEMTIME st;
|
||
GetLocalTime(&st);
|
||
wsprintf(debug_buf, L"DllCanUnloadNow = %04d/%02d/%02d %02d:%02d:%02d\r\n",
|
||
st.wYear, st.wMonth, st.wDay,
|
||
st.wHour, st.wMinute, st.wSecond);
|
||
DWORD write_size;
|
||
WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL);
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
#endif
|
||
|
||
//参照カウントが 0 ならS_OK
|
||
return (g_cRefDll == 0 ? S_OK : S_FALSE);
|
||
}
|
||
|
||
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
|
||
{
|
||
*ppvOut = NULL;
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
if (open_debug_file()){
|
||
SYSTEMTIME st;
|
||
GetLocalTime(&st);
|
||
wsprintf(debug_buf, L"DllGetClassObject = %04d/%02d/%02d %02d:%02d:%02d\r\n",
|
||
st.wYear, st.wMonth, st.wDay,
|
||
st.wHour, st.wMinute, st.wSecond);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (IsEqualIID(rclsid, CLSID_ShellExt)){
|
||
// クラスファクトリの作成。
|
||
CShellExtClassFactory *pcf = new CShellExtClassFactory;
|
||
if (pcf){
|
||
HRESULT hr = pcf->QueryInterface(riid, ppvOut);
|
||
pcf->Release();
|
||
return hr;
|
||
} else {
|
||
return E_OUTOFMEMORY;
|
||
}
|
||
}
|
||
//失敗時はCLASS_E_CLASSNOTAVAILABLEを返す
|
||
return CLASS_E_CLASSNOTAVAILABLE;
|
||
}
|
||
|
||
// レジストリ登録 regsvr32.exe ShellExt.dll
|
||
// https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/shell/reg-shell-exts.md
|
||
STDAPI DllRegisterServer(void)
|
||
{
|
||
wchar_t CLSID_str[] = L"{333EFDA5-A74E-4df4-A225-92A7AF81F29A}";
|
||
wchar_t ext_name[] = L"MultiPar Shell Extension";
|
||
wchar_t path[MAX_PATH], buf[128];
|
||
unsigned int len;
|
||
HKEY hBaseKey, hKey = NULL;
|
||
|
||
len = GetModuleFileName(g_inst, path, MAX_PATH);
|
||
if ((len == 0) || (len >= MAX_PATH))
|
||
return E_FAIL;
|
||
|
||
// 管理者権限で動いてる場合は参照先を変える
|
||
if (check_admin()){
|
||
hBaseKey = HKEY_LOCAL_MACHINE;
|
||
} else {
|
||
hBaseKey = HKEY_CURRENT_USER;
|
||
}
|
||
|
||
// CLSID に COM オブジェクトを登録する
|
||
wsprintf(buf, L"Software\\Classes\\CLSID\\%s", CLSID_str);
|
||
if (RegCreateKeyEx(hBaseKey, buf, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
RegCloseKey(hKey);
|
||
hKey = NULL;
|
||
wsprintf(buf, L"Software\\Classes\\CLSID\\%s\\InprocServer32", CLSID_str);
|
||
if (RegCreateKeyEx(hBaseKey, buf, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
if (RegSetValueEx(hKey, NULL, 0, REG_SZ, (const BYTE*)path, (len+1)*2) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
lstrcpy(buf, L"Apartment");
|
||
len = lstrlen(buf);
|
||
if (RegSetValueEx(hKey, L"ThreadingModel", 0, REG_SZ, (const BYTE*)buf, (len+1)*2) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
RegCloseKey(hKey);
|
||
hKey = NULL;
|
||
|
||
// 全てのファイルとフォルダに Shell Extension を追加する
|
||
wsprintf(buf, L"Software\\Classes\\*\\shellex\\ContextMenuHandlers\\%s", ext_name);
|
||
if (RegCreateKeyEx(hBaseKey, buf, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
len = lstrlen(CLSID_str);
|
||
if (RegSetValueEx(hKey, NULL, 0, REG_SZ, (const BYTE*)CLSID_str, (len+1)*2) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
RegCloseKey(hKey);
|
||
hKey = NULL;
|
||
wsprintf(buf, L"Software\\Classes\\Directory\\shellex\\ContextMenuHandlers\\%s", ext_name);
|
||
if (RegCreateKeyEx(hBaseKey, buf, 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
if (RegSetValueEx(hKey, NULL, 0, REG_SZ, (const BYTE*)CLSID_str, (len+1)*2) != ERROR_SUCCESS)
|
||
goto error_end;
|
||
RegCloseKey(hKey);
|
||
hKey = NULL;
|
||
|
||
// 許可を登録する
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS){
|
||
len = lstrlen(ext_name);
|
||
RegSetValueEx(hKey, CLSID_str, 0, REG_SZ, (const BYTE*)ext_name, (len+1)*2); // 登録できなくてもエラーにしない
|
||
RegCloseKey(hKey);
|
||
}
|
||
hKey = NULL;
|
||
|
||
// 更新を通知する
|
||
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||
|
||
return S_OK;
|
||
error_end:
|
||
if (hKey)
|
||
RegCloseKey(hKey);
|
||
return E_FAIL;
|
||
}
|
||
|
||
// レジストリ登録解除 regsvr32.exe /u ShellExt.dll
|
||
STDAPI DllUnregisterServer(void)
|
||
{
|
||
wchar_t CLSID_str[] = L"{333EFDA5-A74E-4df4-A225-92A7AF81F29A}";
|
||
wchar_t ext_name[] = L"MultiPar Shell Extension";
|
||
wchar_t buf[128];
|
||
unsigned long err = 0, empty, len, ret;
|
||
HKEY hBaseKey, hKey;
|
||
FILETIME ft;
|
||
|
||
// 管理者権限で動いてる場合は参照先を変える
|
||
if (check_admin()){
|
||
hBaseKey = HKEY_LOCAL_MACHINE;
|
||
} else {
|
||
hBaseKey = HKEY_CURRENT_USER;
|
||
}
|
||
|
||
// CLSID の COM オブジェクトを削除する
|
||
wsprintf(buf, L"Software\\Classes\\CLSID\\%s\\InprocServer32", CLSID_str);
|
||
ret = RegDeleteKey(hBaseKey, buf);
|
||
if ((ret != ERROR_SUCCESS) && (ret != ERROR_FILE_NOT_FOUND))
|
||
err++;
|
||
wsprintf(buf, L"Software\\Classes\\CLSID\\%s", CLSID_str);
|
||
ret = RegDeleteKey(hBaseKey, buf);
|
||
if ((ret != ERROR_SUCCESS) && (ret != ERROR_FILE_NOT_FOUND))
|
||
err++;
|
||
|
||
// 全てのファイルとフォルダの Shell Extension を削除する
|
||
wsprintf(buf, L"Software\\Classes\\*\\shellex\\ContextMenuHandlers\\%s", ext_name);
|
||
ret = RegDeleteKey(hBaseKey, buf);
|
||
if ((ret != ERROR_SUCCESS) && (ret != ERROR_FILE_NOT_FOUND))
|
||
err++;
|
||
wsprintf(buf, L"Software\\Classes\\Directory\\shellex\\ContextMenuHandlers\\%s", ext_name);
|
||
ret = RegDeleteKey(hBaseKey, buf);
|
||
if ((ret != ERROR_SUCCESS) && (ret != ERROR_FILE_NOT_FOUND))
|
||
err++;
|
||
|
||
// 空のエントリーを削除する
|
||
empty = 1;
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\*\\shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// ContextMenuHandlers 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\*\\shellex\\ContextMenuHandlers") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
if (empty){
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\*\\shellex", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// shellex 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\*\\shellex") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
if (empty){
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\*", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// * 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\*") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
}
|
||
}
|
||
empty = 1;
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\Directory\\shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// ContextMenuHandlers 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\Directory\\shellex\\ContextMenuHandlers") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
if (empty){
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\Directory\\shellex", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// shellex 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\Directory\\shellex") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
if (empty){
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Classes\\Directory", 0, KEY_READ, &hKey) == ERROR_SUCCESS){
|
||
// Directory 内に他のデータが無ければ項目自体を削除する。
|
||
len = _countof(buf);
|
||
if (RegEnumKeyEx(hKey, 0, buf, &len, NULL, NULL, NULL, &ft) != ERROR_NO_MORE_ITEMS){
|
||
empty = 0;
|
||
} else {
|
||
len = _countof(buf);
|
||
if (RegEnumValue(hKey, 0, buf, &len, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
|
||
empty = 0;
|
||
}
|
||
RegCloseKey(hKey);
|
||
if (empty && (RegDeleteKey(hBaseKey, L"Software\\Classes\\Directory") != ERROR_SUCCESS))
|
||
err++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 許可を削除する
|
||
if (RegOpenKeyEx(hBaseKey, L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS){
|
||
ret = RegDeleteValue(hKey, CLSID_str);
|
||
if ((ret != ERROR_SUCCESS) && (ret != ERROR_FILE_NOT_FOUND)) // 項目自体が存在しない場合はエラーにしない
|
||
err++;
|
||
RegCloseKey(hKey);
|
||
}
|
||
|
||
// 更新を通知する
|
||
// https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/shell/reg-shell-exts.md
|
||
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||
|
||
if (err == 0)
|
||
return S_OK;
|
||
return E_FAIL;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
CShellExtClassFactory::CShellExtClassFactory()
|
||
{
|
||
m_cRef = 1;
|
||
InterlockedIncrement(&g_cRefDll);
|
||
}
|
||
|
||
CShellExtClassFactory::~CShellExtClassFactory()
|
||
{
|
||
InterlockedDecrement(&g_cRefDll);
|
||
}
|
||
|
||
STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid, void **ppvObject)
|
||
{
|
||
*ppvObject = NULL;
|
||
|
||
// Any interface on this object is the object pointer
|
||
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)){
|
||
*ppvObject = (LPCLASSFACTORY)this;
|
||
} else {
|
||
return E_NOINTERFACE;
|
||
}
|
||
|
||
AddRef();
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
|
||
{
|
||
return InterlockedIncrement(&m_cRef);
|
||
}
|
||
|
||
STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
|
||
{
|
||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||
if (cRef == 0){
|
||
delete this;
|
||
}
|
||
return cRef;
|
||
}
|
||
|
||
// IClassFactory::CreateInstance()
|
||
STDMETHODIMP CShellExtClassFactory::CreateInstance(
|
||
LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObject)
|
||
{
|
||
*ppvObject = NULL;
|
||
|
||
// 集合をサポートしないので却下(?)。SDKサンプルより
|
||
if (pUnkOuter)
|
||
return CLASS_E_NOAGGREGATION;
|
||
|
||
// シェル拡張オブジェクトを作成する。
|
||
// そのあとシェルはppvObjectのIID_IShellExtInitを引数に
|
||
// QueryInterfaceメソッドを呼び出し、初期化します
|
||
CShellExtension *pShellExt = new CShellExtension();
|
||
if (pShellExt == NULL)
|
||
return E_OUTOFMEMORY;
|
||
|
||
// 目的のインターフェイスのポインタを取得
|
||
HRESULT hr = pShellExt->QueryInterface(riid, ppvObject);
|
||
pShellExt->Release();
|
||
|
||
return hr;
|
||
}
|
||
|
||
// IClassFactory::LockServer()
|
||
STDMETHODIMP CShellExtClassFactory::LockServer(BOOL fLock)
|
||
{
|
||
if (fLock){
|
||
InterlockedIncrement(&g_cRefDll);
|
||
} else {
|
||
InterlockedDecrement(&g_cRefDll);
|
||
}
|
||
return S_OK;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
CShellExtension::CShellExtension()
|
||
{
|
||
m_cRef = 1;
|
||
m_pDataObj = NULL;
|
||
|
||
InterlockedIncrement(&g_cRefDll);
|
||
}
|
||
|
||
CShellExtension::~CShellExtension()
|
||
{
|
||
if (m_pDataObj)
|
||
m_pDataObj->Release();
|
||
|
||
InterlockedDecrement(&g_cRefDll);
|
||
}
|
||
|
||
STDMETHODIMP CShellExtension::QueryInterface(REFIID riid, void **ppvObject)
|
||
{
|
||
*ppvObject = NULL;
|
||
|
||
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IContextMenu)){
|
||
*ppvObject = (LPCONTEXTMENU)this;
|
||
} else if (IsEqualIID(riid, IID_IShellExtInit)){
|
||
*ppvObject = (LPSHELLEXTINIT)this;
|
||
} else {
|
||
return E_NOINTERFACE;
|
||
}
|
||
|
||
AddRef();
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
STDMETHODIMP_(ULONG) CShellExtension::AddRef()
|
||
{
|
||
return InterlockedIncrement(&m_cRef);
|
||
}
|
||
|
||
STDMETHODIMP_(ULONG) CShellExtension::Release()
|
||
{
|
||
ULONG cRef = InterlockedDecrement(&m_cRef);
|
||
if (cRef == 0){
|
||
delete this;
|
||
}
|
||
return cRef; // ローカル変数を使っているのは delete 後でも値を返せるように
|
||
}
|
||
|
||
|
||
// Initializing Shell Extension Handlers
|
||
// https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/shell/int-shell-exts.md
|
||
STDMETHODIMP CShellExtension::Initialize(
|
||
LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hRegKey)
|
||
{
|
||
// 何回も呼ばれるので、二回目以降は、オブジェクトを解放
|
||
if (m_pDataObj){
|
||
m_pDataObj->Release();
|
||
m_pDataObj = NULL;
|
||
}
|
||
if (pDataObj){
|
||
m_pDataObj = pDataObj;
|
||
pDataObj->AddRef();
|
||
return CheckData();
|
||
}
|
||
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
STDMETHODIMP CShellExtension::QueryContextMenu(
|
||
HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
||
{
|
||
// uFlags に CMF_DEFAULTONLY が含まれる場合は何もしない
|
||
if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_NOVERBS))
|
||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
wsprintf(debug_buf, L"QueryContextMenu\r\nindexMenu = %u\r\nidCmdFirst = %u\r\nidCmdLast = %u\r\nuFlags = 0x%X\r\n", indexMenu, idCmdFirst, idCmdLast, uFlags & 0xFFFF);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
UINT idCmdMax = 0;
|
||
HMENU hSubmenu = NULL;
|
||
MENUITEMINFO mii;
|
||
|
||
if ((menu_behavior < 0) && (load_setting() != 0)){ // 読み込むのは一回だけ
|
||
//if (load_setting() != 0){ // read setting everytime for debug
|
||
wcscpy(menu_item, L"&MultiPar");
|
||
offset_c = (int)wcslen(menu_item) + 1;
|
||
wcscpy(menu_item + offset_c, L"Create Recovery Files");
|
||
if ((menu_behavior & 8) == 0){
|
||
offset_v = offset_c + (int)wcslen(menu_item + offset_c) + 1;
|
||
wcscpy(menu_item + offset_v, L"Verify Recovery File");
|
||
if ((menu_behavior & 32) == 0){
|
||
offset_a = offset_v + (int)wcslen(menu_item + offset_v) + 1;
|
||
wcscpy(menu_item + offset_a, L"Archive and Create Recovery Files");
|
||
}
|
||
} else {
|
||
offset_a = offset_v;
|
||
}
|
||
}
|
||
|
||
if (menu_behavior & 1){ // 上に区切りを追加する
|
||
UINT menu_state;
|
||
|
||
if (menu_behavior & 2){ // 上下を区切る場合は QuickPar と同じ独立配置にする
|
||
do {
|
||
menu_state = GetMenuState(hMenu, indexMenu, MF_BYPOSITION);
|
||
indexMenu++;
|
||
if (menu_state & MF_SEPARATOR)
|
||
break;
|
||
} while (menu_state != -1);
|
||
} else { // 上にだけ区切りを付ける
|
||
menu_state = GetMenuState(hMenu, indexMenu, MF_BYPOSITION);
|
||
if ((menu_state & MF_SEPARATOR) == 0) // まだ区切りがない場合だけ
|
||
InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
|
||
}
|
||
}
|
||
|
||
// 右クリック・メニューに項目を追加する
|
||
// ID = 0: Create or Verify
|
||
// ID = 1: Verify
|
||
// ID = 2: Archive and Create (and Append)
|
||
ZeroMemory(&mii, sizeof(MENUITEMINFO));
|
||
mii.cbSize = sizeof(MENUITEMINFO);
|
||
if (idCmdLast - idCmdFirst >= 1){ // サブ・メニューを追加できるか
|
||
if ((menu_behavior & 64) == 0)
|
||
hSubmenu = CreatePopupMenu();
|
||
if (idCmdLast - idCmdFirst < 3){
|
||
menu_behavior |= 32;
|
||
if (idCmdLast - idCmdFirst < 2)
|
||
menu_behavior |= 8;
|
||
}
|
||
}
|
||
if (hSubmenu == NULL){ // 選択肢無しでトップ・メニューだけにする
|
||
mii.fMask = MIIM_ID | MIIM_STRING;
|
||
mii.wID = idCmdFirst;
|
||
} else {
|
||
mii.fMask = MIIM_STRING | MIIM_SUBMENU;
|
||
mii.hSubMenu = hSubmenu;
|
||
}
|
||
mii.dwTypeData = menu_item;
|
||
if (((menu_behavior & 16) == 0) && (g_bmp != NULL)){ // メニューにアイコンを付ける
|
||
mii.fMask |= MIIM_BITMAP;
|
||
mii.hbmpItem = g_bmp;
|
||
}
|
||
if (hSubmenu != NULL){ // サブ・メニューを作る
|
||
InsertMenu(hSubmenu, -1, MF_STRING | MF_BYPOSITION, idCmdFirst, menu_item + offset_c);
|
||
if ((menu_behavior & 32) == 0){
|
||
if (menu_behavior & 4)
|
||
InsertMenu(hSubmenu, -1, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
|
||
if ((menu_behavior & 16) || (g_bmp2 == NULL)){ // アイコンを表示しないなら
|
||
InsertMenu(hSubmenu, -1, MF_STRING | MF_BYPOSITION, idCmdFirst + 2, menu_item + offset_a);
|
||
} else {
|
||
MENUITEMINFO mii2;
|
||
ZeroMemory(&mii2, sizeof(MENUITEMINFO));
|
||
mii2.cbSize = sizeof(MENUITEMINFO);
|
||
mii2.fMask = MIIM_ID | MIIM_STRING | MIIM_BITMAP;
|
||
mii2.wID = idCmdFirst + 2;
|
||
mii2.dwTypeData = menu_item + offset_a;
|
||
mii2.hbmpItem = g_bmp2;
|
||
InsertMenuItem(hSubmenu, -1, TRUE, &mii2);
|
||
}
|
||
idCmdMax = 2;
|
||
}
|
||
if ((single_file) && ((menu_behavior & 8) == 0)){
|
||
if (menu_behavior & 4)
|
||
InsertMenu(hSubmenu, -1, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
|
||
InsertMenu(hSubmenu, -1, MF_STRING | MF_BYPOSITION, idCmdFirst + 1, menu_item + offset_v);
|
||
if (idCmdMax < 1)
|
||
idCmdMax = 1;
|
||
}
|
||
}
|
||
InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
|
||
|
||
if (menu_behavior & 2)
|
||
InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
|
||
|
||
// Microsoft のページによって説明が異なる・・・でも、サンプル・コードは最大 offset +1 を返す
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
wsprintf(debug_buf, L"menu_behavior = %d\r\nidCmdMax = %u\r\n", menu_behavior, idCmdMax);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// How to Implement the IContextMenu Interface
|
||
// https://docs.microsoft.com/en-us/windows/win32/shell/how-to-implement-the-icontextmenu-interface
|
||
// 追加したコマンド ID の最大番号 + 1 を返す(ID は連番で無くてもいい)
|
||
//return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(idCmdFirst + idCmdMax + 1));
|
||
|
||
// Shobjidl.h (Windows XP or later)
|
||
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icontextmenu-querycontextmenu
|
||
// 追加したコマンド ID の最大番号 - idCmdFirst + 1 を返す(ID は連番で無くてもいい)
|
||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(idCmdMax + 1));
|
||
}
|
||
|
||
STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
|
||
{
|
||
// HIWORD(lpcmi->lpVerb)が0の時だけ処理する。
|
||
if (HIWORD(lpcmi->lpVerb) == 0){
|
||
// LOWORD(lpcmi->lpVerb) はクリックされたメニューIDです。
|
||
// これは QueryContextMenu() の InsertMenu で指定した コマンド ID - idCmdFirst です。
|
||
UINT idCmd = LOWORD(lpcmi->lpVerb);
|
||
|
||
//wsprintf(menu_item + 128, L"ID = %d", idCmd);
|
||
//MessageBox(lpcmi->hwnd, NULL, menu_item + 128, MB_OK);
|
||
|
||
if (idCmd <= 1)
|
||
return DoCommand(idCmd);
|
||
if (idCmd == 2)
|
||
return DoCommand7zip();
|
||
}
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// Windows 2000/XP には非対応なのでヘルプのテキストは表示しない
|
||
STDMETHODIMP CShellExtension::GetCommandString(
|
||
UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax)
|
||
{
|
||
if (idCmd > 2) // コマンド番号が範囲外
|
||
return E_FAIL;
|
||
|
||
if (uFlags == GCS_VALIDATEW){
|
||
return S_OK;
|
||
} else if (uFlags == GCS_VERBW){
|
||
int offset;
|
||
if (idCmd == 1){ // Verify
|
||
offset = offset_v;
|
||
} if (idCmd == 2){ // Archive
|
||
offset = offset_a;
|
||
} else { // Create or Verify
|
||
offset = offset_c;
|
||
}
|
||
lstrcpyn((LPWSTR)pszName, menu_item + offset, cchMax);
|
||
} else {
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||
|
||
// 取得データを検査する
|
||
// S_OK 以外を返すとメニューを表示しない
|
||
int CShellExtension::CheckData(void){
|
||
// 以下のコードでまず、HDROPを得る
|
||
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||
STGMEDIUM stg = { TYMED_HGLOBAL };
|
||
HDROP hDrop;
|
||
|
||
if (FAILED(m_pDataObj->GetData(&fmt, &stg)))
|
||
return E_INVALIDARG;
|
||
|
||
// HDROPを取得
|
||
hDrop = (HDROP)GlobalLock(stg.hGlobal);
|
||
if (hDrop == NULL)
|
||
return E_INVALIDARG;
|
||
|
||
// ファイル数チェック
|
||
UINT uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
wsprintf(debug_buf, L"uNumFiles = %u\r\n", uNumFiles);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// ファイルが0個なら帰る(一応チェック)
|
||
if (uNumFiles == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
// 最初のファイルのパスを記録する
|
||
UINT req = DragQueryFile(hDrop, 0, NULL, 0);
|
||
wsprintf(debug_buf, L"First path length = %u\r\nFirst path = ", req);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
|
||
// 文字数を多めに確保しておくこと
|
||
if ((hDebug != NULL) && (req < _countof(debug_buf))){
|
||
if (DragQueryFile(hDrop, 0, debug_buf, _countof(debug_buf)) > 0){
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
}
|
||
if (hDebug){
|
||
wcscpy(debug_buf, L"\r\n");
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
single_file = 0;
|
||
if (uNumFiles == 1){ // ファイルが一個なら
|
||
wchar_t buf[MAX_PATH + 32];
|
||
unsigned int attr;
|
||
|
||
if (DragQueryFile(hDrop, 0, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// ファイルの属性を検査する
|
||
attr = GetFileAttributes(buf);
|
||
if (((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
|
||
((attr & FILE_ATTRIBUTE_SYSTEM) == 0)){
|
||
single_file = 1;
|
||
}
|
||
}
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
|
||
#ifdef DEBUG_OUTPUT
|
||
if (hDebug){
|
||
wsprintf(debug_buf, L"single_file = %d\r\n", single_file);
|
||
DWORD write_size;
|
||
if (!WriteFile(hDebug, debug_buf, (DWORD)wcslen(debug_buf) * 2, &write_size, NULL)){
|
||
CloseHandle(hDebug);
|
||
hDebug = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
int CShellExtension::DoCommand(UINT idCmd){
|
||
wchar_t path[MAX_PATH], buf[MAX_PATH + 32];
|
||
unsigned int i, len, uNumFiles;
|
||
|
||
// DLL が存在するディレクトリ
|
||
// DLL と呼び出す EXE ファイルは同じディレクトリに置くこと
|
||
len = GetModuleFileName(g_inst, path, MAX_PATH);
|
||
if ((len == 0) || (len >= MAX_PATH))
|
||
return E_INVALIDARG;
|
||
for (i = len - 1; i > 0; i--){
|
||
if (path[i] == '\\'){
|
||
path[i] = 0;
|
||
break;
|
||
}
|
||
}
|
||
//MessageBox(NULL, path , L"DLL path", MB_OK);
|
||
|
||
// 以下のコードでまず、HDROPを得る
|
||
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||
STGMEDIUM stg = { TYMED_HGLOBAL };
|
||
HDROP hDrop;
|
||
|
||
if (FAILED(m_pDataObj->GetData(&fmt, &stg)))
|
||
return E_INVALIDARG;
|
||
|
||
// HDROPを取得
|
||
hDrop = (HDROP)GlobalLock(stg.hGlobal);
|
||
if (hDrop == NULL)
|
||
return E_INVALIDARG;
|
||
uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); // ファイル数
|
||
|
||
// 検査なら
|
||
if ((idCmd == 1) || ((idCmd == 0) && (uNumFiles == 1))){
|
||
wchar_t argvs[MAX_PATH + 16];
|
||
|
||
// コマンドラインを作る
|
||
if (idCmd == 1){
|
||
wcscpy(argvs, L"/verify ");
|
||
} else {
|
||
argvs[0] = 0;
|
||
}
|
||
|
||
// 選択されたファイル名の取得
|
||
if (DragQueryFile(hDrop, 0, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// コマンドラインに追加する
|
||
if (wcschr(buf, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscat(argvs, L"\"");
|
||
wcscat(argvs, buf);
|
||
wcscat(argvs, L"\"");
|
||
} else {
|
||
wcscat(argvs, buf);
|
||
}
|
||
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
|
||
//MessageBox(NULL, argvs, L"param command", MB_OK);
|
||
ShellExecute(NULL, L"open", L"MultiPar.exe", argvs, path, SW_SHOWNORMAL);
|
||
return NOERROR;
|
||
}
|
||
|
||
// ファイル数チェック
|
||
len = 0;
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の文字数
|
||
len += DragQueryFile(hDrop, i, NULL, 0);
|
||
}
|
||
len += uNumFiles * 3 + 8; // コマンドと "" の分だけ余裕を見ておく
|
||
|
||
// 文字数が多いならファイル・リストを使う
|
||
if (len >= 32768){
|
||
char buf2[MAX_PATH * 3];
|
||
wchar_t list_path[MAX_PATH];
|
||
unsigned long rv;
|
||
HANDLE hFile;
|
||
|
||
// アプリケーション・データのディレクトリを決める
|
||
rv = 0;
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, list_path))){
|
||
//MessageBox(NULL, list_path, L"program files path", MB_OK);
|
||
// ProgramFiles の位置と比較する
|
||
int i, max = 0;
|
||
while (list_path[max] != 0)
|
||
max++;
|
||
for (i = 0; i < max; i++){
|
||
if (list_path[i] != path[i])
|
||
break;
|
||
}
|
||
if (i == max){
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, list_path))){
|
||
//MessageBox(NULL, list_path, L"application data path", MB_OK);
|
||
i = 0;
|
||
while (list_path[i] != 0)
|
||
i++;
|
||
if (i < MAX_PATH - 10 - 18){
|
||
wcscat(list_path, L"\\MultiPar");
|
||
rv = 2; // AppData を使う
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (rv != 2)
|
||
wcscpy(list_path, path); // DLL が存在するディレクトリを使う
|
||
wcscat(list_path, L"\\MultiPar_list.tmp");
|
||
//MessageBox(NULL, list_path, L"file-list path", MB_OK);
|
||
|
||
// テキスト・ファイルを開く
|
||
hFile = CreateFile(list_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// ファイルごとに記録する
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の取得
|
||
if (DragQueryFile(hDrop, i, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
CloseHandle(hFile);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// UTF-8 に変換する
|
||
len = WideCharToMultiByte(CP_UTF8, 0, buf, -1, buf2, MAX_PATH * 3, NULL, NULL);
|
||
if (len > 0){ // len は末尾の null 文字を含む
|
||
buf2[len - 1] = 0x0A; // 末尾に改行を追加する
|
||
if (!WriteFile(hFile, buf2, len, &rv, NULL)){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
CloseHandle(hFile);
|
||
}
|
||
}
|
||
}
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
|
||
// テキスト・ファイルを閉じる
|
||
CloseHandle(hFile);
|
||
|
||
// コマンドラインを作る(先頭の空白は実行ファイルのパスの代わり)
|
||
wcscpy(buf, L" /create /list ");
|
||
if (wcschr(list_path, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscat(buf, L"\"");
|
||
wcscat(buf, list_path);
|
||
wcscat(buf, L"\"");
|
||
} else {
|
||
wcscat(buf, list_path);
|
||
}
|
||
//MessageBox(NULL, buf, L"file-list command", MB_OK);
|
||
ShellExecute(NULL, L"open", L"MultiPar.exe", buf, path, SW_SHOWNORMAL);
|
||
|
||
} else { // ファイル数が少ないならコマンドラインで渡す
|
||
wchar_t argvs[32768];
|
||
STARTUPINFO si;
|
||
PROCESS_INFORMATION pi;
|
||
|
||
// コマンドラインを作る(先頭の空白は実行ファイルのパスの代わり)
|
||
wcscpy(argvs, L" /create");
|
||
|
||
// ファイルごとに記録する
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の取得
|
||
if (DragQueryFile(hDrop, i, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// コマンドラインに追加していく
|
||
if (wcschr(buf, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscat(argvs, L" \"");
|
||
wcscat(argvs, buf);
|
||
wcscat(argvs, L"\"");
|
||
} else {
|
||
wcscat(argvs, L" ");
|
||
wcscat(argvs, buf);
|
||
}
|
||
}
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
|
||
//len = wcslen(argvs);
|
||
//wsprintf(buf, L"length = %d", len);
|
||
//MessageBox(NULL, buf, L"param length", MB_OK);
|
||
//MessageBox(NULL, argvs, L"param command", MB_OK);
|
||
// Windows 2000 だと ShellExecute のコマンドラインは 2000文字ぐらいまでなので、パスが長いと無理
|
||
// ShellExecute(NULL, L"open", L"MultiPar.exe", argvs, path, SW_SHOWNORMAL);
|
||
// CreateProcess の引数は 32768文字ぐらいまでいける。
|
||
wcscat(path, L"\\MultiPar.exe");
|
||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||
si.cb = sizeof(STARTUPINFO);
|
||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||
si.wShowWindow = SW_SHOWNORMAL;
|
||
if (CreateProcess(path, argvs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){
|
||
// スレッドハンドルとプロセスハンドルの解放
|
||
CloseHandle(pi.hThread);
|
||
CloseHandle(pi.hProcess);
|
||
}
|
||
}
|
||
|
||
return NOERROR;
|
||
}
|
||
|
||
int CShellExtension::DoCommand7zip(void){
|
||
wchar_t path[MAX_PATH], buf[MAX_PATH], argvs[32768];
|
||
unsigned int i, len, max, uNumFiles;
|
||
STARTUPINFO si;
|
||
PROCESS_INFORMATION pi;
|
||
|
||
// DLL が存在するディレクトリ
|
||
// DLL と呼び出す EXE ファイルは同じディレクトリに置くこと
|
||
len = GetModuleFileName(g_inst, path, MAX_PATH);
|
||
if ((len == 0) || (len >= MAX_PATH))
|
||
return E_INVALIDARG;
|
||
for (i = len - 1; i > 0; i--){
|
||
if (path[i] == '\\'){
|
||
path[i] = 0;
|
||
break;
|
||
}
|
||
}
|
||
//MessageBox(NULL, path , L"DLL path", MB_OK);
|
||
|
||
get_path_7zip(buf); // 7-Zip のディレクトリを取得する
|
||
if (buf[0] == 0){
|
||
menu_behavior |= 32; // 次からメニューを表示しない
|
||
//MessageBoxA(GetDesktopWindow(), "Cannot find 7-Zip." , "MultiPar shell extension", MB_OK | MB_ICONERROR);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// 以下のコードでまず、HDROPを得る
|
||
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||
STGMEDIUM stg = { TYMED_HGLOBAL };
|
||
HDROP hDrop;
|
||
|
||
if (FAILED(m_pDataObj->GetData(&fmt, &stg)))
|
||
return E_INVALIDARG;
|
||
|
||
// HDROPを取得
|
||
hDrop = (HDROP)GlobalLock(stg.hGlobal);
|
||
if (hDrop == NULL)
|
||
return E_INVALIDARG;
|
||
uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); // ファイル数
|
||
|
||
// コマンドラインを作る
|
||
if (wcschr(buf, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscpy(argvs, L"\"");
|
||
wcscat(argvs, buf);
|
||
wcscat(argvs, L"\"");
|
||
} else {
|
||
wcscpy(argvs, buf);
|
||
}
|
||
// 「-saa」を追加すると書庫ファイルを別の形式で圧縮できるようになる
|
||
// 「--」を追加するとファイル名の先頭が「-」でもオプション扱いにならない
|
||
//wcscat(argvs, L" a -ad -saa -- ");
|
||
wcscat(argvs, L" /archive:7zip "); // MultiPar 用のコマンドに置き換える
|
||
// 選択された最初のファイル名
|
||
if (DragQueryFile(hDrop, 0, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
len = (unsigned int)wcslen(buf);
|
||
if (uNumFiles > 1){ // ファイルが複数ならその親フォルダーの名前を書庫に付ける
|
||
for (i = len - 1; i > 0; i--){
|
||
if (buf[i] == '\\'){ // 親フォルダー名の末尾
|
||
max = i;
|
||
for (i = i - 1; i > 0; i--){
|
||
if (buf[i] == '\\'){ // 親フォルダー名の先頭
|
||
for (len = 1; i + len < max; len++)
|
||
buf[max + len] = buf[i + len];
|
||
buf[max + len] = 0;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// コマンドラインに書庫名を追加する
|
||
if (wcschr(buf, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscat(argvs, L"\"");
|
||
wcscat(argvs, buf);
|
||
wcscat(argvs, L"\"");
|
||
} else {
|
||
wcscat(argvs, buf);
|
||
}
|
||
|
||
// ファイル名の文字数チェック
|
||
len = 0;
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の文字数
|
||
len += DragQueryFile(hDrop, i, NULL, 0);
|
||
}
|
||
len += uNumFiles * 3 + (unsigned int)wcslen(argvs); // コマンドと "" の分だけ余裕を見ておく
|
||
|
||
// 文字数が多いならファイル・リストを使う
|
||
if (len >= 32768){
|
||
char buf2[MAX_PATH * 3];
|
||
wchar_t list_path[MAX_PATH];
|
||
unsigned long rv;
|
||
HANDLE hFile;
|
||
|
||
// アプリケーション・データのディレクトリを決める
|
||
rv = 0;
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, list_path))){
|
||
//MessageBox(NULL, list_path, L"program files path", MB_OK);
|
||
// ProgramFiles の位置と比較する
|
||
max = 0;
|
||
while (list_path[max] != 0)
|
||
max++;
|
||
for (i = 0; i < max; i++){
|
||
if (list_path[i] != path[i])
|
||
break;
|
||
}
|
||
if (i == max){
|
||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, list_path))){
|
||
//MessageBox(NULL, list_path, L"application data path", MB_OK);
|
||
i = 0;
|
||
while (list_path[i] != 0)
|
||
i++;
|
||
if (i < MAX_PATH - 10 - 18){
|
||
wcscat(list_path, L"\\MultiPar");
|
||
rv = 2; // AppData を使う
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (rv != 2)
|
||
wcscpy(list_path, path); // DLL が存在するディレクトリを使う
|
||
wcscat(list_path, L"\\MultiPar_list.tmp");
|
||
//MessageBox(NULL, list_path, L"file-list path", MB_OK);
|
||
|
||
// テキスト・ファイルを開く
|
||
hFile = CreateFile(list_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (hFile == INVALID_HANDLE_VALUE){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// ファイルごとに記録する
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の取得
|
||
if (DragQueryFile(hDrop, i, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
CloseHandle(hFile);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// UTF-8 に変換する
|
||
len = WideCharToMultiByte(CP_UTF8, 0, buf, -1, buf2, MAX_PATH * 3, NULL, NULL);
|
||
if (len > 0){ // len は末尾の null 文字を含む
|
||
buf2[len - 1] = 0x0A; // 末尾に改行を追加する
|
||
if (!WriteFile(hFile, buf2, len, &rv, NULL)){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
CloseHandle(hFile);
|
||
}
|
||
}
|
||
}
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
|
||
// テキスト・ファイルを閉じる
|
||
CloseHandle(hFile);
|
||
|
||
// コマンドラインにファイル・リスト名を追加する
|
||
wcscat(argvs, L" @\"");
|
||
wcscat(argvs, list_path);
|
||
wcscat(argvs, L"\"");
|
||
|
||
} else { // ファイル数が少ないならコマンドラインで渡す
|
||
// ファイルごとに記録する
|
||
for (i = 0; i < uNumFiles; i++){
|
||
// 選択されたファイル名の取得
|
||
if (DragQueryFile(hDrop, i, buf, _countof(buf)) == 0){
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
return E_INVALIDARG;
|
||
}
|
||
|
||
// コマンドラインに追加していく
|
||
if (wcschr(buf, ' ') != NULL){ // スペースを含む場合は"で囲む
|
||
wcscat(argvs, L" \"");
|
||
wcscat(argvs, buf);
|
||
wcscat(argvs, L"\"");
|
||
} else {
|
||
wcscat(argvs, L" ");
|
||
wcscat(argvs, buf);
|
||
}
|
||
}
|
||
GlobalUnlock(stg.hGlobal);
|
||
ReleaseStgMedium(&stg);
|
||
}
|
||
|
||
// 書庫ファイルを MultiPar で開く
|
||
//MessageBox(NULL, argvs, L"command param", MB_OK);
|
||
wcscat(path, L"\\MultiPar.exe");
|
||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||
si.cb = sizeof(STARTUPINFO);
|
||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||
si.wShowWindow = SW_SHOWNORMAL;
|
||
if (CreateProcess(path, argvs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){
|
||
// スレッドハンドルとプロセスハンドルの解放
|
||
CloseHandle(pi.hThread);
|
||
CloseHandle(pi.hProcess);
|
||
}
|
||
|
||
return NOERROR;
|
||
}
|
||
|