Introduction
I did some Windows Script Host programming recently, and I was pleasantly surprised by its power, features, and flexibility. One thing that I couldn't accomplish was accessing the clipboard from WSH. Digging the Internet, I found some solutions like this one based on Internet Explorer Automation. There are several problems with this approach as you can read in my article about Internet Explorer Automation: What's wrong with Internet Explorer Automation?
Using the code
The solution for scripting the clipboard content in WSH is a regular COM object created with VC++ and ATL. To install the COM object, run register.bat.
Here is a simple example of using the component from WSH:
var clipboardHelper = null;
try
{
clipboardHelper = WScript.CreateObject("ClipboardHelper.ClipBoard");
}
catch (ex)
{
WScript.Echo(ex.message + "\n\nClipboardHelper library is not properly registered!");
WScript.Quit(1);
}
var msg = "Some text";
clipboardHelper.SetClipboardText(msg);
var text = clipboardHelper.GetClipboardText();
WScript.Echo(text);
Points of interest
The implementation is just regular ATL/COM code. Win32 API functions are used to get access to clipboard text (OpenClipboard, IsClipboardFormatAvailable, GetClipboardData, SetClipboardData, CloseClipboard, GlobalAlloc, GlobalLock, GlobalFree).
Here is the implementation of the method that retrieves the text from clipboard (CF_TEXT and CF_UNICODETEXT formats supported):
STDMETHODIMP CClipBoard::GetClipboardText(BSTR* pBstrClipboardText)
{
if (NULL == pBstrClipboardText)
{
return E_INVALIDARG;
}
CComBSTR bstrResult = L"";
if (::OpenClipboard(NULL))
{
if (::IsClipboardFormatAvailable(CF_TEXT) ||
::IsClipboardFormatAvailable(CF_UNICODETEXT))
{
BOOL bUnicode = TRUE;
HANDLE hClipboard = ::GetClipboardData(CF_UNICODETEXT);
if (NULL == hClipboard)
{
bUnicode = FALSE;
hClipboard = ::GetClipboardData(CF_TEXT);
}
if (hClipboard != NULL)
{
LPCSTR szClipboardData = (LPCSTR)::GlobalLock(hClipboard);
if (szClipboardData != NULL)
{
if (bUnicode)
{
LPCWSTR szClipboardWText = (LPCWSTR)szClipboardData;
bstrResult = szClipboardWText;
szClipboardWText = NULL;
}
else
{
LPCSTR szClipboardText = (LPCSTR)szClipboardData;
bstrResult = szClipboardText;
szClipboardText = NULL;
}
::GlobalUnlock(hClipboard);
*pBstrClipboardText = bstrResult.Detach();
hClipboard = NULL;
}
else
{
ATLTRACE("GlobalLock failed in CClipBoard::GetClipboardText\n");
}
}
else
{
ATLTRACE("GetClipboardData failed in CClipBoard::GetClipboardText\n");
}
hClipboard = NULL;
}
else
{
ATLTRACE("CF_TEXT NOT available in CClipBoard::GetClipboardText\n");
}
BOOL bRes = ::CloseClipboard();
ATLASSERT(bRes);
}
else
{
ATLTRACE("Can NOT OpenClipboard in CClipBoard::GetClipboardText\n");
}
return S_OK;
}