Introduction
We use RealVNC to remote control PCs in our network, VNC is a great product, but without remembering computer names it can be tedious to find the computer in the Network Neighbourhood, then copy the computer name into the VNC connection screen, so I developed this Shell Extension.
What is VNC?
From RealVNC Site:
"VNC stands for Virtual Network Computing. It is remote control software which allows you to view and interact with one computer (the "server") using a simple program (the "viewer") on another computer anywhere on the Internet. The two computers don't even have to be the same type, so for example you can use VNC to view an office Linux machine on your Windows PC at home. VNC is freely and publicly available and is in widespread active use by millions throughout industry, academia and privately."
RealVNC can be downloaded here.
Creating the Extension
I would first like to thank Michael Dunn for all his great efforts, especially with the tutorial: The Complete Idiot's Guide to Writing Shell Extensions - Part I. I used it as a starting point for my extension.
I followed his steps to create his extension demo, then changed his initialization interface code to handle Network Resources, instead of File Names.
CFSTR_NETRESOURCES
This format identifier is used when transferring network resources, such as a domain or server. The data is an STGMEDIUM
structure that contains a global memory object. The structure's hGlobal
member points to a NRESARRAY
structure. That structure's nr
member indicates a NETRESOURCE
structure whose lpRemoteName
member contains a null-terminated string identifying the network resource.
We have to use the RegisterClipboardFormat
API function, supplying CFSTR_NETRESOURCES
as the parameter, to get the shell registered clipboard format for Network Resources. We can then extract the name of the item selected from the first NETRESOURCE
structure in the nr
member of the NRESARRAY
structure.
We check the type of resource, as we are only interested in Servers. The dwDisplayType member contains the type of resource.
if(pNtary->nr[0].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
The string members in the NETRESOURCE
structure contain offsets instead of addresses!
So we get to the first name selected by casting the structure as a char
pointer, then we add the offset to the pointer, then cast it to a WCHAR
pointer.
WCHAR* pwchRemoteName = (WCHAR*)((char*)pNtary +
int(pNtary->nr[0].lpRemoteName));
We remove the \\
from the begining of the returned name, and store it in the m_szPCName
member variable for use later.
STDMETHODIMP CVNCShell::Initialize(LPCITEMIDLIST pIDFolder,
IDataObject *pDataObj,
HKEY hRegKey)
{
HRESULT hr;
if (pDataObj)
{
STGMEDIUM medium;
FORMATETC fe;
fe.cfFormat = RegisterClipboardFormat(CFSTR_NETRESOURCES);
fe.ptd = NULL;
fe.dwAspect = DVASPECT_CONTENT;
fe.lindex = -1;
fe.tymed = TYMED_HGLOBAL;
hr = pDataObj->GetData(&fe, &medium);
if(SUCCEEDED(hr))
{
LPVOID lpv = GlobalLock(medium.hGlobal);
if (lpv)
{
LPNRESARRAY pNtary = (NRESARRAY*)lpv;
m_bShowMenuItem = false;
if(pNtary->nr[0].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
{
m_bShowMenuItem = true;
WCHAR* pwchRemoteName = (WCHAR*)((char*)pNtary +
int(pNtary->nr[0].lpRemoteName));
lstrcpynW (m_szPCName, pwchRemoteName + 2,
lstrlenW(pwchRemoteName) * sizeof(TCHAR));
}
GlobalUnlock(medium.hGlobal);
}
else
hr = E_UNEXPECTED;
ReleaseStgMedium(&medium);
}
}
return hr;
}
I also changed his InvokeCommand
Interface to replace the code that displays a MessageBox
with code that executes the VNC program, this assumes the VNC program is installed in the fixed path supplied, in our case it is always installed there, we pass the m_szPCName
member variable containing the PCs name we stored earlier.
ShellExecute (pCmdInfo->hwnd, _T("open"),
_T("C:\\Program Files\\RealVNC\\vncviewer.exe"),
m_szPCName, NULL, SW_SHOWNORMAL);
return S_OK;
Registering the shell extension
The Shell defines additional objects under HKEY_CLASSES_ROOT
which can be extended in the same way as file types.
The only one we are interested in is the Network\Type\2 subkey:
HKEY_CLASSES_ROOT\Network\Type\2\ShellEx\ContextMenuHandlers\VNCShellExt
From MSDN:
"For Network\Type\# , "#" is a network provider type code in decimal. The network provider type code is the high word of a network type. The list of network types is given in the Winnetwk.h header file (WNNC_NET_* values)."
So I Changed the Registry Resource from text file to this:
NoRemove Network
{
NoRemove Type
{
NoRemove 2
{
NoRemove ShellEx
{
NoRemove ContextMenuHandlers
{
ForceRemove VNCShellExt = s '{B9442EFE-9815-4046-B6FF-4F3606291D8E}'
}
}
}
}
}
That is all, This is my first attempt at an article, and I have only been programming in C++ for a very short time, please be nice :)
References
I started programming in BASIC when I was 8 on an old Dick Smith VZ-200. The VZ-200 had a Z80A processor running at 3.58Mhz, and 6K of RAM!
I moved on to programming in C when I was 13 on an 8086 XT computer, which was my next computer. I wrote many little programs including Terminate and Stay Resident programs. I also wrote a great deal of code for manipulating the video memory directly for fast text updating.
I was taught Pascal in high school, and never really did much with it, I stuck to programming in C. After leaving school, I never really programmed anything until I worked for a local company, which no longer exists, I was thrown into the world of database programming.
I spent several months learning Microsoft Access and SQL, and my biggest project was a multi-user Point of Sale program that I developed in house on the customers site. The program took almost a year to complete single handed, it was developed in MS Access. The program replaced a very old UNIX based program that was causing them problems.
I currently work for a large local company, I was employed to do general IT&T support, but soon moved on to developing many in house applications, including a full Intranet site.
I taught myself C++ and use it to develop many of my applications, I also use Visual Basic 6.
Now using .NET to develop most apps.