diff --git a/_msbuild.py b/_msbuild.py
index 1ed48e2..9773db6 100644
--- a/_msbuild.py
+++ b/_msbuild.py
@@ -164,7 +164,7 @@ def launcher_exe(name, platform, windowed=False):
def pyshellext(ext='.exe', **props):
link_opts = ItemDefinition(
'Link',
- AdditionalDependencies=Prepend('RuntimeObject.lib;'),
+ AdditionalDependencies=Prepend('RuntimeObject.lib;pathcch.lib;'),
)
if ext != '.exe':
link_opts.options['ModuleDefinitionFile'] = '$(SourceRootDir)src\\pyshellext\\pyshellext.def'
diff --git a/_msbuild_test.py b/_msbuild_test.py
index 2b28a4b..de2cff9 100644
--- a/_msbuild_test.py
+++ b/_msbuild_test.py
@@ -63,7 +63,7 @@
PreprocessorDefinitions=Prepend("PYSHELLEXT_TEST=1;"),
LanguageStandard='stdcpp20',
),
- ItemDefinition('Link', AdditionalDependencies=Prepend("RuntimeObject.lib;")),
+ ItemDefinition('Link', AdditionalDependencies=Prepend("RuntimeObject.lib;pathcch.lib;")),
CSourceFile('pyshellext/shellext.cpp'),
CSourceFile('pyshellext/shellext_test.cpp'),
IncludeFile('pyshellext/shellext.h'),
@@ -74,6 +74,9 @@
CFunction('shellext_ReadAllIdleInstalls'),
CFunction('shellext_PassthroughTitle'),
CFunction('shellext_IdleCommand'),
+ CFunction('shellext_GetDropArgumentsW'),
+ CFunction('shellext_GetDropArgumentsA'),
+ CFunction('shellext_GetDropDescription'),
source='src',
)
)
diff --git a/src/pymanager/appxmanifest.xml b/src/pymanager/appxmanifest.xml
index 959faec..f178c51 100644
--- a/src/pymanager/appxmanifest.xml
+++ b/src/pymanager/appxmanifest.xml
@@ -272,6 +272,13 @@
+
+
+
+
+
+
+
diff --git a/src/pymanager/msi.wxs b/src/pymanager/msi.wxs
index c0f5617..0d02adb 100644
--- a/src/pymanager/msi.wxs
+++ b/src/pymanager/msi.wxs
@@ -63,12 +63,23 @@
Name="ExplorerCommandHandler"
Value="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}"
Type="string" />
+
+
+
+
@@ -111,6 +122,7 @@
Condition="WIX_NATIVE_MACHINE = 34404">
+
@@ -119,6 +131,7 @@
Condition="WIX_NATIVE_MACHINE = 43620">
+
diff --git a/src/pyshellext/shellext.cpp b/src/pyshellext/shellext.cpp
index 3d360e1..94f592f 100644
--- a/src/pyshellext/shellext.cpp
+++ b/src/pyshellext/shellext.cpp
@@ -1,18 +1,35 @@
+#include
+#include
+
#define _WIN32_WINNT _WIN32_WINNT_WIN10
#include
#define __WRL_CLASSIC_COM__
#include
+#include
+#include
+
using namespace Microsoft::WRL;
#include "shellext.h"
static HINSTANCE hModule;
+
#define CLSID_IDLE_COMMAND "{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}"
#define CLSID_LAUNCH_COMMAND "{F7209EE3-FC96-40F4-8C3F-4B7D3994370D}"
#define CLSID_COMMAND_ENUMERATOR "{F82C8CD5-A69C-45CC-ADC6-87FC5F4A7429}"
+#define CLSID_DRAGDROPSUPPORT "{EAF5E48F-F54A-4A03-824B-CA880772EE20}"
+
+#ifndef _DEBUG
+#undef OutputDebugString
+#define OutputDebugString(x)
+#endif
+
+
+static const WPARAM DDWM_UPDATEWINDOW = WM_USER + 3;
+static const LPCWSTR DRAG_MESSAGE = L"Open with %1";
LRESULT RegReadStr(HKEY key, LPCWSTR valueName, std::wstring& result)
@@ -251,9 +268,9 @@ class DECLSPEC_UUID(CLSID_LAUNCH_COMMAND) LaunchCommand
parameters.c_str(),
NULL
};
- OutputDebugStringW(L"IdleCommand::Invoke");
- OutputDebugStringW(exe.c_str());
- OutputDebugStringW(parameters.c_str());
+ OutputDebugString(L"IdleCommand::Invoke");
+ OutputDebugString(exe.c_str());
+ OutputDebugString(parameters.c_str());
ShellExecuteExW(&sei);
return S_OK;
}
@@ -402,7 +419,7 @@ class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
if (FAILED(hr)) {
wchar_t buffer[512];
swprintf_s(buffer, L"IdleCommand error 0x%08X", (DWORD)hr);
- OutputDebugStringW(buffer);
+ OutputDebugString(buffer);
idles.clear();
}
}
@@ -514,7 +531,388 @@ class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
};
+class DECLSPEC_UUID(CLSID_DRAGDROPSUPPORT) DragDropSupport : public RuntimeClass<
+ RuntimeClassFlags, IDropTarget, IPersistFile>
+{
+ std::wstring target, target_name, target_dir;
+ DWORD target_mode;
+
+ static CLIPFORMAT cfDropDescription;
+ static CLIPFORMAT cfDragWindow;
+
+ IDataObject *data_obj;
+
+public:
+ DragDropSupport() : data_obj(NULL) {
+ if (!cfDropDescription) {
+ cfDropDescription = RegisterClipboardFormat(CFSTR_DROPDESCRIPTION);
+ }
+ if (!cfDropDescription) {
+ OutputDebugString(L"PyShellExt::DragDropSupport - failed to get CFSTR_DROPDESCRIPTION format");
+ }
+ if (!cfDragWindow) {
+ cfDragWindow = RegisterClipboardFormat(L"DragWindow");
+ }
+ if (!cfDragWindow) {
+ OutputDebugString(L"PyShellExt::DragDropSupport - failed to get DragWindow format");
+ }
+ }
+
+ ~DragDropSupport() {
+ if (data_obj) {
+ data_obj->Release();
+ }
+ }
+
+ HRESULT UpdateDropDescription(DROPDESCRIPTION *drop_desc) {
+ StringCchCopy(drop_desc->szMessage, sizeof(drop_desc->szMessage) / sizeof(drop_desc->szMessage[0]), DRAG_MESSAGE);
+ StringCchCopy(drop_desc->szInsert, sizeof(drop_desc->szInsert) / sizeof(drop_desc->szInsert[0]), target_name.c_str());
+ drop_desc->type = DROPIMAGE_MOVE;
+ return S_OK;
+ }
+
+ HRESULT UpdateDropDescription(IDataObject *pDataObj) {
+ STGMEDIUM medium;
+ FORMATETC fmt = {
+ cfDropDescription,
+ NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL
+ };
+
+ auto hr = pDataObj->GetData(&fmt, &medium);
+ if (FAILED(hr)) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::UpdateDropDescription - failed to get DROPDESCRIPTION format");
+ return hr;
+ }
+ if (!medium.hGlobal) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::UpdateDropDescription - DROPDESCRIPTION format had NULL hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+ auto drop_desc = (DROPDESCRIPTION*)GlobalLock(medium.hGlobal);
+ if (!drop_desc) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::UpdateDropDescription - failed to lock DROPDESCRIPTION hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+ hr = UpdateDropDescription(drop_desc);
+
+ GlobalUnlock(medium.hGlobal);
+ ReleaseStgMedium(&medium);
+
+ return hr;
+ }
+
+ HRESULT GetDragWindow(IDataObject *pDataObj, HWND *phWnd) {
+ HRESULT hr;
+ HWND *pMem;
+ STGMEDIUM medium;
+ FORMATETC fmt = {
+ cfDragWindow,
+ NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL
+ };
+
+ hr = pDataObj->GetData(&fmt, &medium);
+ if (FAILED(hr)) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetDragWindow - failed to get DragWindow format");
+ return hr;
+ }
+ if (!medium.hGlobal) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetDragWindow - DragWindow format had NULL hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ pMem = (HWND*)GlobalLock(medium.hGlobal);
+ if (!pMem) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetDragWindow - failed to lock DragWindow hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ *phWnd = *pMem;
+
+ GlobalUnlock(medium.hGlobal);
+ ReleaseStgMedium(&medium);
+
+ return S_OK;
+ }
+
+ HRESULT GetArgumentsW(LPCWSTR files, LPCWSTR *pArguments) {
+ std::wstring arg_str;
+ while (arg_str.size() < 32767 && *files) {
+ std::wstring wfile(files);
+ files += wfile.size() + 1;
+
+ if (wfile.find(L' ') != wfile.npos) {
+ wfile.insert(wfile.begin(), L'"');
+ wfile.push_back(L'"');
+ }
+ if (arg_str.size()) {
+ arg_str.push_back(L' ');
+ }
+ arg_str += wfile;
+ }
+
+ LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (arg_str.size() + 1));
+ *pArguments = args;
+ if (!args) {
+ return E_OUTOFMEMORY;
+ }
+ wcscpy_s(args, arg_str.size() + 1, arg_str.c_str());
+
+ return S_OK;
+ }
+
+ HRESULT GetArgumentsA(LPCSTR files, LPCWSTR *pArguments) {
+ std::string arg_str;
+ while (arg_str.size() < 32767 && *files) {
+ std::string file(files);
+ files += file.size() + 1;
+
+ if (file.find(' ') != file.npos) {
+ file.insert(file.begin(), '"');
+ file.push_back('"');
+ }
+ if (arg_str.size()) {
+ arg_str.push_back(' ');
+ }
+ arg_str += file;
+ }
+
+ int wlen = MultiByteToWideChar(CP_ACP, 0, arg_str.data(), arg_str.size(), NULL, 0);
+ if (!wlen) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetArguments - failed to get length of wide-char path");
+ return E_FAIL;
+ }
+
+ LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (wlen + 1));
+ if (!args) {
+ return E_OUTOFMEMORY;
+ }
+ wlen = MultiByteToWideChar(CP_ACP, 0, arg_str.data(), arg_str.size(), args, wlen + 1);
+ if (!wlen) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetArguments - failed to convert multi-byte to wide-char path");
+ CoTaskMemFree(args);
+ return E_FAIL;
+ }
+ args[wlen] = '\0';
+ *pArguments = args;
+ return S_OK;
+ }
+
+ HRESULT GetArguments(IDataObject *pDataObj, LPCWSTR *pArguments) {
+ HRESULT hr;
+ DROPFILES *pdropfiles;
+
+ STGMEDIUM medium;
+ FORMATETC fmt = {
+ CF_HDROP,
+ NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL
+ };
+
+ hr = pDataObj->GetData(&fmt, &medium);
+ if (FAILED(hr)) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetArguments - failed to get CF_HDROP format");
+ return hr;
+ }
+ if (!medium.hGlobal) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetArguments - CF_HDROP format had NULL hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ pdropfiles = (DROPFILES*)GlobalLock(medium.hGlobal);
+ if (!pdropfiles) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::GetArguments - failed to lock CF_HDROP hGlobal");
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ if (pdropfiles->fWide) {
+ LPCWSTR files = (LPCWSTR)((char*)pdropfiles + pdropfiles->pFiles);
+ hr = GetArgumentsW(files, pArguments);
+ } else {
+ LPCSTR files = (LPCSTR)((char*)pdropfiles + pdropfiles->pFiles);
+ hr = GetArgumentsA(files, pArguments);
+ }
+
+ GlobalUnlock(medium.hGlobal);
+ ReleaseStgMedium(&medium);
+
+ return hr;
+ }
+
+ HRESULT NotifyDragWindow(HWND hwnd) {
+ LRESULT res;
+
+ if (!hwnd) {
+ return S_FALSE;
+ }
+
+ res = SendMessage(hwnd, DDWM_UPDATEWINDOW, 0, NULL);
+
+ if (res) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::NotifyDragWindow - failed to post DDWM_UPDATEWINDOW");
+ return E_FAIL;
+ }
+
+ return S_OK;
+ }
+
+ // IDropTarget implementation
+
+ STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ HWND hwnd = NULL;
+
+ OutputDebugString(L"PyShellExt::DragDropSupport::DragEnter");
+
+ if (data_obj) {
+ data_obj->Release();
+ }
+ pDataObj->AddRef();
+ data_obj = pDataObj;
+
+ *pdwEffect = DROPEFFECT_MOVE;
+
+ if (FAILED(UpdateDropDescription(data_obj))) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::DragEnter - failed to update drop description");
+ }
+ if (FAILED(GetDragWindow(data_obj, &hwnd))) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::DragEnter - failed to get drag window");
+ }
+ if (FAILED(NotifyDragWindow(hwnd))) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::DragEnter - failed to notify drag window");
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP DragLeave() {
+ if (data_obj) {
+ data_obj->Release();
+ data_obj = NULL;
+ }
+ return S_OK;
+ }
+
+ STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ *pdwEffect = DROPEFFECT_MOVE;
+ return S_OK;
+ }
+
+ STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ LPCWSTR args;
+
+ OutputDebugString(L"PyShellExt::DragDropSupport::Drop");
+ *pdwEffect = DROPEFFECT_NONE;
+
+ if (pDataObj != data_obj) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::Drop - unexpected data object");
+ return E_FAIL;
+ }
+
+ data_obj->Release();
+ data_obj = NULL;
+
+ if (SUCCEEDED(GetArguments(pDataObj, &args))) {
+ OutputDebugString(args);
+ ShellExecute(NULL, NULL, target.c_str(), args, target_dir.c_str(), SW_NORMAL);
+
+ CoTaskMemFree((LPVOID)args);
+ } else {
+ OutputDebugString(L"PyShellExt::DragDropSupport::Drop - failed to get launch arguments");
+ }
+
+ return S_OK;
+ }
+
+ // IPersistFile implementation
+
+ STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName) {
+ HRESULT hr;
+ size_t len = target.size();
+
+ if (!ppszFileName) {
+ return E_POINTER;
+ }
+
+ *ppszFileName = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
+ if (!*ppszFileName) {
+ return E_OUTOFMEMORY;
+ }
+
+ hr = StringCchCopy(*ppszFileName, len + 1, target.c_str());
+ if (FAILED(hr)) {
+ CoTaskMemFree(*ppszFileName);
+ *ppszFileName = NULL;
+ return E_FAIL;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP IsDirty() {
+ return S_FALSE;
+ }
+
+ STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode) {
+ OutputDebugString(L"PyShellExt::DragDropSupport::Load");
+ OutputDebugString(pszFileName);
+
+ target = pszFileName;
+ target_dir = pszFileName;
+ switch (PathCchRemoveFileSpec(target_dir.data(), target_dir.size())) {
+ case S_OK:
+ target_dir.resize(wcsnlen_s(target_dir.data(), target_dir.size()));
+ target_name = { target.begin() + target_dir.size(), target.end() };
+ while (!target_name.empty() && (target_name.front() == L'\\' || target_name.front() == L'/')) {
+ target_name.erase(0, 1);
+ }
+ break;
+ case S_FALSE:
+ target_name = L"script";
+ break;
+ default:
+ OutputDebugString(L"PyShellExt::DragDropSupport::Load - failed to remove filespec from target");
+ return E_FAIL;
+ }
+
+ OutputDebugString(target.c_str());
+ target_mode = dwMode;
+ OutputDebugString(L"PyShellExt::DragDropSupport::Load - S_OK");
+ return S_OK;
+ }
+
+ STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHODIMP GetClassID(CLSID *pClassID) {
+ *pClassID = __uuidof(DragDropSupport);
+ return S_OK;
+ }
+};
+
+CLIPFORMAT DragDropSupport::cfDropDescription = 0;
+CLIPFORMAT DragDropSupport::cfDragWindow = 0;
+
+
CoCreatableClass(IdleCommand);
+CoCreatableClass(DragDropSupport);
+
#ifdef PYSHELLEXT_TEST
@@ -530,6 +928,47 @@ IExplorerCommand *MakeIdleCommand(HKEY hive, LPCWSTR root)
return Make(hive, root).Detach();
}
+HRESULT GetDropArgumentsW(LPCWSTR args, std::wstring &parsed)
+{
+ LPCWSTR p;
+ auto o = Make();
+ HRESULT hr = o->GetArgumentsW(args, &p);
+ if (SUCCEEDED(hr)) {
+ parsed = p;
+ CoTaskMemFree((LPVOID)p);
+ }
+ return hr;
+}
+
+HRESULT GetDropArgumentsA(LPCSTR args, std::wstring &parsed)
+{
+ LPCWSTR p;
+ auto o = Make();
+ HRESULT hr = o->GetArgumentsA(args, &p);
+ if (SUCCEEDED(hr)) {
+ parsed = p;
+ CoTaskMemFree((LPVOID)p);
+ }
+ return hr;
+}
+
+HRESULT GetDropDescription(LPCOLESTR pszFileName, DWORD dwMode, std::wstring &message, std::wstring &insert)
+{
+ auto o = Make();
+ HRESULT hr = o->Load(pszFileName, dwMode);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ DROPDESCRIPTION drop_desc;
+ ZeroMemory(&drop_desc, sizeof(drop_desc));
+ hr = o->UpdateDropDescription(&drop_desc);
+ if (SUCCEEDED(hr)) {
+ message = drop_desc.szMessage;
+ insert = drop_desc.szInsert;
+ }
+ return hr;
+}
+
#elif defined(_WINDLL)
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
diff --git a/src/pyshellext/shellext.h b/src/pyshellext/shellext.h
index 12e1219..034be26 100644
--- a/src/pyshellext/shellext.h
+++ b/src/pyshellext/shellext.h
@@ -21,3 +21,6 @@ HRESULT ReadAllIdleInstalls(std::vector &idles, HKEY hive, LPCWSTR roo
IExplorerCommand *MakeIdleCommand(HKEY hive, LPCWSTR root);
IExplorerCommand *MakeLaunchCommand(std::wstring title, std::wstring exe, std::wstring idle);
+HRESULT GetDropArgumentsW(LPCWSTR args, std::wstring &parsed);
+HRESULT GetDropArgumentsA(LPCSTR args, std::wstring &parsed);
+HRESULT GetDropDescription(LPCOLESTR pszFileName, DWORD dwMode, std::wstring &message, std::wstring &insert);
diff --git a/src/pyshellext/shellext_test.cpp b/src/pyshellext/shellext_test.cpp
index a574f90..df03210 100644
--- a/src/pyshellext/shellext_test.cpp
+++ b/src/pyshellext/shellext_test.cpp
@@ -238,4 +238,63 @@ PyObject *shellext_IdleCommand(PyObject *, PyObject *args, PyObject *)
}
+PyObject *shellext_GetDropArgumentsW(PyObject *, PyObject *args, PyObject *)
+{
+ Py_buffer value;
+ if (!PyArg_ParseTuple(args, "y*", &value)) {
+ return NULL;
+ }
+ PyObject *r = NULL;
+ std::wstring parsed;
+ HRESULT hr = GetDropArgumentsW((wchar_t *)value.buf, parsed);
+ PyBuffer_Release(&value);
+ if (SUCCEEDED(hr)) {
+ r = PyUnicode_FromWideChar(parsed.data(), parsed.size());
+ } else {
+ PyErr_SetFromWindowsErr((int)hr);
+ }
+ return r;
+}
+
+PyObject *shellext_GetDropArgumentsA(PyObject *, PyObject *args, PyObject *)
+{
+ Py_buffer value;
+ if (!PyArg_ParseTuple(args, "y*", &value)) {
+ return NULL;
+ }
+ PyObject *r = NULL;
+ std::wstring parsed;
+ HRESULT hr = GetDropArgumentsA((char *)value.buf, parsed);
+ PyBuffer_Release(&value);
+ if (SUCCEEDED(hr)) {
+ r = PyUnicode_FromWideChar(parsed.data(), parsed.size());
+ } else {
+ PyErr_SetFromWindowsErr((int)hr);
+ }
+ return r;
+}
+
+PyObject *shellext_GetDropDescription(PyObject *, PyObject *args, PyObject *)
+{
+ wchar_t *value1;
+ Py_ssize_t value2;
+ if (!PyArg_ParseTuple(args, "O&n", as_utf16, &value1, &value2)) {
+ return NULL;
+ }
+ PyObject *r = NULL;
+ std::wstring parsed1, parsed2;
+ HRESULT hr = GetDropDescription(value1, value2, parsed1, parsed2);
+ if (SUCCEEDED(hr)) {
+ r = Py_BuildValue(
+ "u#u#",
+ parsed1.data(), (Py_ssize_t)parsed1.size(),
+ parsed2.data(), (Py_ssize_t)parsed2.size()
+ );
+ } else {
+ PyErr_SetFromWindowsErr((int)hr);
+ }
+ return r;
+}
+
+
}
diff --git a/tests/test_shellext.py b/tests/test_shellext.py
index d865d25..17a06cc 100644
--- a/tests/test_shellext.py
+++ b/tests/test_shellext.py
@@ -101,3 +101,19 @@ def test_IdleCommand(idle_reg):
f"{sys._base_executable},-4",
*(i[0] for i in reversed(idle_reg.all)),
]
+
+
+def test_DragDropDescription():
+ assert ("Open with %1", "test.exe") == SE.shellext_GetDropDescription(
+ r"C:\Fake\Path\test.exe", 0
+ )
+
+
+def test_GetDropArgumentsW():
+ actual = SE.shellext_GetDropArgumentsW("arg 1\0arg2\0arg 3\0\0".encode("utf-16-le"))
+ assert actual == '"arg 1" arg2 "arg 3"'
+
+
+def test_GetDropArgumentsA():
+ actual = SE.shellext_GetDropArgumentsA("arg 1\0arg2\0arg 3\0\0".encode("ascii"))
+ assert actual == '"arg 1" arg2 "arg 3"'