Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Launch VNC Shell Extension

0.00/5 (No votes)
25 Apr 2004 1  
Launch VNC from Network Neighbourhood

Introduction

Sample image

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;

             // The string members in the structure 

             // contain offsets instead of addresses!

             WCHAR* pwchRemoteName =  (WCHAR*)((char*)pNtary +
                int(pNtary->nr[0].lpRemoteName)); 

             // Copy name, minus \\ at begining of name, to local variable

             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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here