A shortcut to Name2Clip
tool is placed in the "Send To" menu of Windows Explorer:
Contents
Everybody knows how tedious it can be to copy full paths to files and folders from Windows Explorer to the Clipboard. The primary goal of Name2Clip
is to fix that problem. Name2Clip
is a tiny utility that sends its command line arguments to the system clipboard. Name2Clip
can be used with any number of strings as command line parameters. The utility doesn't care much about the content of the strings. It just copies strings to the system clipboard as text where each string occupies a new line. This article shows how to use "Send To" context menu of Windows Explorer together with Name2Clip
in order to copy full paths of selected files and folders to the Clipboard.
In order to keep things as small and as light as possible, Name2Clip
doesn't use any external framework or library. It doesn't even use C Run-time library (CRT). Name2Clip
is implemented in C programming language and calls strictly Windows API functions. Thanks to this, Name2Clip.exe is lightning fast and its size is about mere 7 KB.
Name2Clip
can run on any Windows system starting from Windows 2000 and above. No installation is required. Here's the usage summary:
Name2Clip [/c] [/s] [/n] params
/c - Coding friendly format: '\' replaced by '\\'.
/s - Paths are converted to MS-DOS 8.3 format.
/n - Only names rather than full paths are copied.
When launched without parameters, Name2Clip
shows its usage.
In addition to command line switches, Name2Clip
checks the keyboard state when launched. So, if a certain key is pressed, Name2Clip
detects it and behaves as if a corresponding command line switch was specified. Here is the list of supported keys:
- Ctrl key is pressed (equivalent to the
/c
switch) - double backslashes are inserted. Example: C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe - Shift key is pressed (equivalent to the
/s
switch) - MS-DOS 8.3 name is placed into the Clipboard. Example: C:\PROGRA~1\WINDOW~2\ACCESS~1\wordpad.exe - Ctrl+Shift keys are pressed - both of the above. Example: C:\\PROGRA~1\\WINDOW~2\\ACCESS~1\\wordpad.exe
Thanks to Nick Carruthers and his article Copy Path Context Menu Extension for the idea about these keys.
The "Send To" menu of Windows Explorer is created from the contents of special SendTo folder. The folder's location is as follows:
- Windows XP: %USERPROFILE%\SendTo, where
%USERPROFILE%
usually expands to: C:\Documents and Settings\{username} - Windows Vista and Windows 7: %APPDATA%\Microsoft\Windows\SendTo, where
%APPDATA%
is usually expands to: C:\Users\{username}\AppData\Roaming
When a link is placed in the SendTo folder, then link's name will be shown in the "Send To" context menu in the Windows Explorer. When user selects the link from "Send To" menu, then full paths to currently selected files and folders are passed as command line parameters to the program specified in the link. All we need to do is to place a link to Name2Clip
to SendTo folder.
Here is the detailed description of how to add an item to the "Send To" menu: Add an item to the Send To menu. The article relates to Windows XP, however the process is essentially the same for any Windows platform. Only the location of the SendTo folder is different.
So, why develop an application that doesn't use CRT? Here are a couple of reasons:
- Independency. An application that doesn't use CRT is almost completely independent of development environment. Any decent C compiler can be utilized. Alternatively, to achieve a similar level of independency, a program could be linked statically with the CRT. However, it will increase the program's size.
- Smallest possible size. By using exclusively Windows API functions, a program can achieve the smallest possible size on Windows platform. To make it even smaller, one probably would have been required to use assembler as a development tool. But let's be sane.
- It's fun. Most likely, you figured out already that the above points are moot. Nowadays nobody in their sane mind would start serious development without modern tools. It includes CRT support, of course. However,
Name2Clip
is so tiny and simple that making it without CRT might be fun. This is the true reason of the exercise.
Sometimes, there are legitimate reasons to make a program without CRT. Usually it is a device driver, a code that runs in embedded environment and other close to hardware applications. Regular desktop applications shouldn't be developed that way. Actually, modern desktop applications shouldn't be developed in a native language at all.
Here is the price Name2Clip
paid to be free from CRT:
So, the price is pretty high. Beware!
Since we don't use CRT, we need to specify our own entry point to the program. We can do it by using /ENTRY
linker switch. However, MSDN is wrong about the signature of the entry point function. From the "/ENTRY (Entry-Point Symbol)" article:
The function must be defined with the __stdcall
calling convention. The parameters and return value must be defined as documented in the Win32 API for WinMain
(for a .exe file) or DllEntryPoint
(for a DLL).
This is incorrect. The actual function signature of the program entry point must be: int __cdecl(*)(void)
or int __stdcall(*)(void)
. Calling convention is not important, since the function doesn't receive any arguments, so there is no need to clean the stack.
Here is the entire code of Name2Clip
:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define STRICT
#define _WIN32_WINNT 0x0500 // Win2K
#include <Windows.h>
#include <ShellApi.h>
#include <WindowsX.h>
#include "resource.h"
UINT CalcDataLength(
LPCWSTR* ppArgv,
int nArgc,
UINT nFlags);
LPWSTR FormatCopyData(
LPCWSTR* ppArgv,
int nArgc,
UINT nFlags,
LPWSTR pDst);
UINT GetFlags(
LPCWSTR* ppArgv,
int nArgc);
UINT GetKeyboardFlags();
UINT GetCmdLineFlags(
LPCWSTR* ppArgv,
int nArgc);
LPCWSTR FindFilename(
LPCWSTR pcwsz);
void ShowUsage(void);
#define N2C_CODEFRIENDLY 0x01 // /C switch
#define N2C_SHORTPATHS 0x02 // /S switch
#define N2C_NAMESONLY 0x04 // /N switch
int wN2ClipMain(void)
{
int nArgc = 0;
LPCWSTR* ppArgv = CommandLineToArgvW(GetCommandLineW(), &nArgc);
if(!ppArgv) return -1;
if(nArgc > 1)
{
UINT nFlags = GetFlags(ppArgv, nArgc);
UINT nDataLen = CalcDataLength(ppArgv, nArgc, nFlags);
LPWSTR pwszCopy = GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,
nDataLen * sizeof(WCHAR));
if(pwszCopy)
{
HWND hWndDesktop = GetDesktopWindow();
FormatCopyData(ppArgv, nArgc, nFlags, pwszCopy);
GlobalUnlockPtr(pwszCopy);
if(OpenClipboard(hWndDesktop))
{
if(EmptyClipboard())
SetClipboardData(CF_UNICODETEXT, GlobalPtrHandle(pwszCopy));
CloseClipboard();
}
}
GlobalFreePtr(ppArgv);
}
else ShowUsage();
return 0;
}
UINT CalcDataLength(
LPCWSTR* ppArgv,
int nArgc,
UINT nFlags)
{
UINT nLen = 0;
int i;
for(i = 1; i < nArgc; i++)
{
int nExtraLen = 0;
int nPathLen = 0;
LPCWSTR pPath = ppArgv[i];
if(*pPath == L'/') continue;
if(nFlags & N2C_NAMESONLY)
{
pPath = FindFilename(pPath);
}
else if(nFlags & N2C_CODEFRIENDLY)
{
LPCWSTR pEos = pPath;
while(*pEos)
{
if(*pEos == '\\') nExtraLen++;
pEos++;
}
}
if(nFlags & N2C_SHORTPATHS)
{
if(nFlags & N2C_NAMESONLY)
nPathLen = 12; else
nPathLen = GetShortPathNameW(pPath, NULL, 0);
}
else
{
nPathLen = lstrlenW(pPath);
}
nLen += nPathLen + nExtraLen + 2 ;
}
nLen = nLen + 1 ;
return nLen;
}
LPWSTR FormatCopyData(
LPCWSTR* ppArgv,
int nArgc,
UINT nFlags,
LPWSTR pDst)
{
LPWSTR pCopyData = pDst;
int i;
WCHAR wszPath[MAX_PATH + 1];
for(i = 1; i < nArgc; i++)
{
LPCWSTR pSrc = ppArgv[i];
if(*pSrc == L'/') continue;
if(nFlags & N2C_SHORTPATHS)
{
DWORD dwLen = GetShortPathNameW(pSrc, wszPath, MAX_PATH);
if(dwLen != ERROR_INVALID_PARAMETER)
pSrc = wszPath;
}
if(nFlags & N2C_NAMESONLY)
pSrc = FindFilename(pSrc);
while((*pDst = *pSrc) != 0)
{
if((nFlags & N2C_CODEFRIENDLY) && (*pSrc == L'\\'))
{
pDst++; *pDst = L'\\';
}
pDst++; pSrc++;
}
if(i < nArgc - 1)
{
*pDst++ = L'\r';
*pDst++ = L'\n';
}
}
return pCopyData;
}
UINT GetFlags(
LPCWSTR* ppArgv,
int nArgc)
{
UINT nKbdFlags = GetKeyboardFlags();
UINT nCmdFlags = GetCmdLineFlags(ppArgv, nArgc);
return (nKbdFlags | nCmdFlags);
}
UINT GetKeyboardFlags()
{
UINT nFlags = 0;
if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
nFlags |= N2C_CODEFRIENDLY;
if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
nFlags |= N2C_SHORTPATHS;
return nFlags;
}
UINT GetCmdLineFlags(
LPCWSTR* ppArgv,
int nArgc)
{
UINT nFlags = 0;
int i, j;
const struct Switches
{
LPCWSTR pcwszSwitch;
UINT nFlag;
} switches[] = {
{ L"/C", N2C_CODEFRIENDLY },
{ L"/S", N2C_SHORTPATHS },
{ L"/N", N2C_NAMESONLY }
};
for(i = 0; i < nArgc; ++i)
{
for(j = 0; j < ARRAYSIZE(switches); ++j)
{
if(lstrcmpiW(ppArgv[i], switches[j].pcwszSwitch) == 0)
{
nFlags |= switches[j].nFlag;
break;
}
}
}
return nFlags;
}
LPCWSTR FindFilename(
LPCWSTR pcwsz)
{
LPCWSTR pcwszStart = pcwsz;
while(*pcwsz++);
while(--pcwsz != pcwszStart && *pcwsz != L'\\' && *pcwsz != L'/');
if (*pcwsz == L'\\' || *pcwsz == L'/')
pcwsz++;
return pcwsz;
}
void ShowUsage(void)
{
MSGBOXPARAMS mbp = { 0 };
mbp.cbSize = sizeof(MSGBOXPARAMS);
mbp.hwndOwner = GetDesktopWindow();
mbp.hInstance = GetModuleHandle(NULL);
mbp.lpszText = MAKEINTRESOURCE(IDS_USAGE);
mbp.lpszCaption = MAKEINTRESOURCE(IDS_CAPTION);
mbp.dwStyle = MB_USERICON | MB_OK;
mbp.lpszIcon = MAKEINTRESOURCE(IDI_NAME2CLIP);
mbp.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
MessageBoxIndirect(&mbp);
}
According to MSDN, the maximum length of command line string for CreateProcess
function is 32,768 characters. Name2Clip
is started by Windows Explorer with selected filenames as command line parameters. So there is a limitation on the number of files that can be sent to the Clipboard using Name2Clip
. Even though 32K of characters is not a small buffer, sometimes it can be exceeded, too. When there are too many files selected, the system shows the following dialog box:
The workaround is to copy less filenames at a time.
- 29th August, 2009: Initial post
- 26th October, 2009: Article updated