Tom

Sep 112010

This function will return the system name for the computer you’re on.  It just fishes it out of the registry, but it is handy if you need that information for displaying in your UI or using for other purposes.  I use it to build job IDs that are unique since the system name can’t be duplicated on the same network.

//
//	Returns the system network name
//	looks like \\name
//
CString GetSystemName()
{
	long result;
	HKEY mainKey;
	result = RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,
		_T("SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName"),
		0,
		KEY_READ,
		&mainKey
	);
	TCHAR nameData[_MAX_PATH];
	DWORD nameType;
	DWORD nameDataSize;
	CString cs;
	if (result == ERROR_SUCCESS) {
		nameDataSize = sizeof nameData;
		LONG ret = RegQueryValueEx(mainKey, _T("ComputerName"), NULL, &nameType,
                    (unsigned char *)nameData, &nameDataSize);
		if (ret==ERROR_SUCCESS && nameType==REG_SZ) {
			cs = nameData;
		}
		RegCloseKey(mainKey);
	}
	return DOUBLE_SLASH + cs;
}
Aug 202010

This is a function I use to delete all files, subfolders, and the parent folder in a tree. It’s a simple process, but a useful function. It uses a simple recursion to work down through the folder tree and a loop to enumerate the files and subfolders.

//
//	Remove all folders.
//
void RemoveAllDirectories(CString strDir)
{
	CFileFind ff;
	CString path = strDir;
	
	path = AddPathAndFile(path,_T("*.*"));
	BOOL res = ff.FindFile(path);

	while(res) {
		res = ff.FindNextFile();
		if (!ff.IsDots()) {
			if(!ff.IsDirectory())
				DeleteFile(ff.GetFilePath());
			else {
				path = ff.GetFilePath();
				RemoveAllDirectories(path);
			}
		}
	}
	RemoveDirectory(strDir);
}
Mar 292010

Note to self: It never rains in Oregon, but don’t they warn ya, it pours, man it pours… IOW, keep an umbrella in the car dummy.

Mar 262010

This function is handy for getting and formatting the string that is associated with critical errors.  You just pass in the errno returned from a Win32 call.  This information is available elsewhere, but I’ve found this encapsulated function to be handy.

CString GetCriticalErrorString(DWORD nError) {
  LPVOID lpMsgBuf;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
          NULL, nError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
          (LPTSTR) &lpMsgBuf, 0, NULL );
  CString cs = (LPCTSTR)lpMsgBuf;
  LocalFree(lpMsgBuf);
  return cs;
}
Feb 232010

Note to self:  Be more careful when walking on the treadmill.  When walking on the ground, it stops when you do.  The treadmill, not so much.

Feb 232010

Note to self: Even though they call at 8:00a to tell you your package will arrive sometime between 8a and 7p that day and you have to be home to sign for it … staring out the window waiting for the UPS truck to arrive with your new computer does not make it arrive any faster.  You’d be more productive watching a pot of water boil.

Feb 232010

I don’t really consider myself a C# programmer yet, but I recently had a need to figure out how to do this in C#.  As it turns out I couldn’t find a handy built in function so I Binged for a solution.  Unfortunately, I only found parts of solutions in different places.  So, I compiled all of them in to the following code.  Maybe some of this will help someone else with the same need.  This code is similar to what I do in C++ and I think it is pretty safe, but make sure you test it in your application.

using System.Runtime.InteropServices;
using System.Management;

        [DllImport("mpr.dll")]
        [return: MarshalAs(UnmanagedType.U4)]
        static extern int WNetGetUniversalName(
            string lpLocalPath,
            [MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
            IntPtr lpBuffer,
            [MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
        const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
        const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
        const int ERROR_MORE_DATA = 234;
        const int ERROR_NOT_CONNECTED = 2250;
        const int ERROR_NO_NETWORK = 1222;
        const int NOERROR = 0;

        static public String GetUniversalName(String _path)
        {
            // The return value.
            String UNCPath = null;

            // Already a UNC, no need to convert
            if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
                return _path;

            // WNetGetUniversalName does not allow a null buffer
            IntPtr buf = Marshal.AllocCoTaskMem(5);
            int size = 0;
            try
            {
                // First call to WNetGetUniversalName to get the buffer size
                int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
                Marshal.FreeCoTaskMem(buf);

                // Local Drive
                if (ret == ERROR_NOT_CONNECTED || ret == ERROR_NO_NETWORK)
                    return GetLocalUniversalName(_path);

                // If the return is not ERROR_MORE_DATA then something went wrong and the
                // conversion didn't work so return the original path
                if (ret != ERROR_MORE_DATA)
                    return _path;
            }
            catch (Exception  ex)
            {
                String msg = String.Format("{0}: {1}",ex.Message, _path);
                log.Error(msg);
                return _path; // Something blew so just return the original path
            }

            buf = Marshal.AllocCoTaskMem(size);
            try
            {
                // Look up the name of the share for the mapped drive
                int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);

                // If it didn't convert just return the original path
                if (ret != NOERROR)
                {
                    // Release the temp buffer
                    Marshal.FreeCoTaskMem(buf);
                    return _path;
                }

                // Now convert the result to a string we can actually use
                // It's all in the same buffer, but the pointer is first,
                // so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi.
                UNCPath = Marshal.PtrToStringAnsi(new IntPtr(buf.ToInt64() + IntPtr.Size), size);

                // Clean up stuff at the end of UNCPath
                int actualLength = UNCPath.IndexOf('\0');
                if (actualLength >= 0)
                {
                    UNCPath = UNCPath.Substring(0, actualLength);
                }

                // Release the temp buffer
                Marshal.FreeCoTaskMem(buf);
                return UNCPath;
            }
            catch (Exception ex)
            {
                String msg = String.Format("{0}: {1}", ex.Message, _path);
                log.Error(msg);
                return _path;
            }
        }

        // Get a UNC for a local share
        static public String GetLocalUniversalName(String _path)
        {
            // Already a UNC, no need to convert
            if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
                return _path;

            String UNCPath = _path;
            ManagementClass exportedShares = new ManagementClass("Win32_Share");
            ManagementObjectCollection shares = exportedShares.GetInstances();
            foreach (ManagementObject share in shares)
            {
                String name = share["Name"].ToString();
                String path = share["Path"].ToString();
                String caption = share["Caption"].ToString();
                if (!name.Contains("$") && _path.StartsWith(path))
                {
                    String rest = _path.Substring(path.Length);
                    UNCPath = String.Format(@"<a href="file://\\{0}\{1}\{2">\\{0}\{1}\{2</a>}", Environment.MachineName, name, rest);
                }
            }
            return UNCPath;
        }
Jan 282010

MFC tries to write application program informatin into the HKLM (Local Machine) which isn’t allowed for non admin applicaitons on Vista and Win7.  The following functions will cause your program to redirect that information to the HKCU (Current User) area in the registry instead.  The OS will happily use the information from there.

In your application code InitInstance add the lines around:

OverrideHKCR();
RegisterShellFileTypes(true);
RestoreHKCR();

bool OverrideHKCR()
{
    // If we can't write to HKEY_CLASSES_ROOT, we should try
    // HKEY_CURRENT_USER\Software\Classes instead.

    HKEY hkcr;
    long ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Classes"), 0,
                                     KEY_ALL_ACCESS, &hkcr);
    if(ret != ERROR_SUCCESS) { // Need to override
        HKEY hkcu;
        ret = RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Classes"), &hkcu);
        if(ret == ERROR_SUCCESS) {
            ret = RegOverridePredefKey(HKEY_CLASSES_ROOT, hkcu);
            RegCloseKey(hkcu);
            return ret == ERROR_SUCCESS;
        }
    }
    else {
        RegCloseKey(hkcr);
    }
    return false; // Didn't need to do this
}

void RestoreHKCU()
{
    RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL);
}
Jan 282010

This code will use the IExtractImage interface in the shell to get an HBITMAP of the shell representation of a thumbnail.  This is what shows up in the Explorer as a preview of a file.  It works for any file type that has a filter installed for the Explorer.  I cobbled this code together based on other articles that I read. I couldn’t find anything callable in C++/MFC so this might be useful to someone else.  The application that produces the preview in Explorer will need to be installed on the system where this code is called.

//
// Attempt to get a thumbnail from the Windows shell.  This is faster than getting the bitmap bits
// using DirectShow and also  gives us the same thing the user sees in the Explorer.  It will only
// work if there is an IExtractImage filter registered for the file type.
//
HRESULT CVideoInfo::CreateThumbnail(LPCTSTR szPath, LPCTSTR szFile, DWORD dwWidth,
    DWORD dwHeight, HBITMAP *pThumbnail)
{
    LPITEMIDLIST pidList = NULL;
    DWORD dwPriority = 0;
    DWORD dwFlags = IEIFLAG_ASPECT;
    HRESULT hr = E_NOINTERFACE;
    SIZE size;
    WCHAR szBuffer[MAX_PATH];
    IExtractImage *peiURL = NULL;
    IShellFolder  *psfWorkDir = NULL;
    IShellFolder  *psfDesktop = NULL;

    size.cx = dwWidth;
    size.cy = dwHeight;

    int nTries = 0;

    try {
     // We need to try more than once in case the video file returns a false error
        while(!SUCCEEDED(hr) &amp;&amp; nTries++ < NUM_VIDEO_TRIES) {
            CoInitialize(NULL);

            hr = SHGetDesktopFolder(&Desktop);
            if(SUCCEEDED(hr)) {
                hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPWSTR)szPath, NULL, &pidList, NULL);
                if(SUCCEEDED(hr)) {
                    hr = psfDesktop->BindToObject(pidList, NULL, IID_IShellFolder, (void **)&psfWorkDir);
                    if(SUCCEEDED(hr)) {
                        hr = psfWorkDir->ParseDisplayName(NULL, NULL, (LPWSTR)szFile, NULL, &pidList, NULL);
                        if(SUCCEEDED(hr)) {
                            LPCITEMIDLIST pidl = pidList;
                            hr = psfWorkDir->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractImage, NULL,
                                                             (void **)&peiURL);
                            if(SUCCEEDED(hr)) {
                                hr = peiURL->GetLocation(szBuffer, MAX_PATH, &dwPriority, &size, 16, &dwFlags);
                                if(SUCCEEDED(hr) || hr == E_PENDING) {
                                    hr = peiURL->Extract(pThumbnail);
                                }
                            }
                        }
                    }
                }
            }

            IMallocFree(pidList);
            if(peiURL != NULL)
                peiURL->Release();
            if(psfDesktop != NULL)
                psfDesktop->Release();
            if(psfWorkDir != NULL)
                psfWorkDir->Release();
            CoUninitialize();
        }
    }
    catch(...) {
       return E_ABORT;
    }

    return hr;
}
Jan 282010

Simple routine to copy a HBITMAP in memory.  This routine assumes the HBITMAP has been read previously using something like:

HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), csBitmapFile,
      IMAGE_BITMAP,0,0,LR_LOADFROMFILE);

This routine makes a copy of the bitmap without affecting the original.

// Make a copy of an existing BMP in  memory
void CMyApp::CopyBitmap(HBITMAP hBmp, CBitmap *oBmp)
{
    CBitmap *srcBmp;
    srcBmp = CBitmap::FromHandle(hBmp);

    BITMAP bmpInfo;
    srcBmp->GetBitmap(&amp;bmpInfo);

    CDC srcDC;
    srcDC.CreateCompatibleDC(NULL);

    CBitmap *pOldBmp1 = srcDC.SelectObject(srcBmp);
    oBmp->CreateCompatibleBitmap(&amp;srcDC,bmpInfo.bmWidth,bmpInfo.bmHeight);

    CDC destDC;
    destDC.CreateCompatibleDC(NULL);

    CBitmap *pOldBmp2 = destDC.SelectObject(oBmp);
    destDC.BitBlt(0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,&amp;srcDC,0,0,SRCCOPY);

    srcDC.SelectObject(pOldBmp1);
    destDC.SelectObject(pOldBmp2);
}

Call as:

CBitmap myBmp;

CopyBitmap(hBmp, &amp;myBmp);