Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Changing Windows XP's Bootskin, Windows File Protection, and Themes programmatically

4.82/5 (28 votes)
29 Dec 2008CPOL13 min read 62.1K  
Vistra code walkthrough article, to explain a few of the interesting things about Windows.

Introduction

When I was searching something over the Internet, I got to see a software which couldn modify Windows XP's bootskin. I was surprised, and started exploring how it can be done by a third party software (not Microsoft's). This is how Vistra development started. This is a Vistra code walkthrough article, to explain a few of the interesting things about Windows. At the end of this article, you will know what is Windows File Protection, how to change Windows XP's bootskin, how to change Windows themes programmatically, and a few more.

What is Vistra?

The name Vistra is neither a trade mark nor copyrighted. Just to give a relative name to Vista, it is called Vistra (or VISual TRAnsformation). Vistra is a small software which can change your Windows XP theme to that of Vista, make your taskbar/Start menu transparent, change your bootskin with your own image, set a timer for your desktop wallpapers, and make your drive icons look like Vista drive icons.

Except the bootskin code, I think all others are familiar ones and you can get code for those easily over the Internet. I don’t have a team to test Vistra; I just developed it and posted it in my website. Please note, this software is not a competitor for any other existing software in the field.

Enough stories, let's get into it.

Before explaining how to change the bootskin or apply a theme programmatically, let’s look into a few of the functions which made Vistra work well.

Resource Security:

You cannot use Vistra if you have edited the Company name/Product Name/File Description/Legal Copyright or any other resource information of Vistra. This does not mean Vistra is 100% secured of corruption, but it is secured to some extent. Let’s see how:

C++
bool CVISTRADlg::IsProductValid()
{
      TCHAR szName[MAX_PATH+1];
      GetModuleFileName(NULL,szName,MAX_PATH);
      HANDLE hFile;
      if ((hFile = CreateFile(szName, GENERIC_READ, FILE_SHARE_READ, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
            NULL)) != INVALID_HANDLE_VALUE)     
      {
            DWORD dwSize=GetFileSize(hFile,0);
            CloseHandle(hFile);
            if(dwSize!=2154496 )          //2,154,496 bytes
            {
                  return false;
            }

            CString sInfo=GetFileInfo(_T("CompanyName"));
            sInfo=Crypt(sInfo,12);
            if(sInfo.Compare(_T("ZN`T^1`XYaWd]`[h"))!=0) return false;

            sInfo=GetFileInfo(_T("InternalName"));
            sInfo=Crypt(sInfo,13);
            if(sInfo.Compare(_T("cw‚dƒsAy{"))!=0) return false;

            sInfo=GetFileInfo(_T("ProductName"));
            sInfo=Crypt(sInfo,14);
            if(sInfo.Compare(_T("dxƒe„t"))!=0) return false;

            sInfo=GetFileInfo(_T("FileDescription"));
            sInfo=Crypt(sInfo,15);
            if(sInfo.Compare(_T("_‚€ˆ|xz‰7n‚}=r‡…Ž‡ODg•–œI–•›ZO¤£"¡§¥—") 
              _T("©§´\\±­_"¢µ®¦¦¸g©·®kŸÁ¯ÁÄqŸ¸ÂÊ‚w®ÂÍϽ}ÂÑÉ×ǃÍÈÕÕÛ—˜™"))!=0)
                        return false;

            sInfo=GetFileInfo(_T("LegalCopyright"));
            sInfo=Crypt(sInfo,16);
            if(sInfo.Compare(_T("BABK4=y@8g[mak>mefndqjmhuWJKm™šO¢š™›¨¨V©¬Ÿ­²¢¢m"))!=0) 
                        return false;
      }
      return true;
}

I believe the code above is pretty straightforward.

Checkpoint 1: Get the current module’s filename using GetModuleFileName(), get a handle of the file, and get the file size of the current module. If the size is different, i.e., not equal to the actual size of your software, then prompt it as an invalid product.

Checkpoint 2: Get the resource values of Company Name/Product Name/File Description/Legal Copyright etc., and compare with the encrypted actual values. If any of those are different, then prompt it as an invalid product.

Crypt() is a simple cryptography function to encrypt the resource values. The reason for encrypting the resource values in code is, when you open your product in a hex/bin editor, you can find the strings used in the product and you can edit all the strings straight away using that editor. For example, _T(“CompanyName”) is used in the above function. Open Vitsra in a hex/bin editor and search for CompanyName, you can find it and you can edit it. Also, you can find _T("ZN`T^1`XYaWd]`[h"), the actual company name, but encrypted. It is not understandable, so you cannot edit it. Even if you edit it to your own, it won't match with the encrypted comparison done in the code. So, the product will fail to launch.

Make your Crypt() function to return an integer value instead of a string value to make it more complex and to reduce the possibilities of corruption. E.g.,

C++
CString sInfo=GetFileInfo(_T("CompanyName"));
unsigned long nCryptValue=Crypt(sInfo,12);
if(nCryptValue!=6854266) return false;

The GetFileInfo() function is to retrieve information from your software’s resource like Company name/Product name/File description/Legal copyright etc.

C++
CString GetFileInfo(CString sInfo)
{
      TCHAR szName[MAX_PATH+1];
      GetModuleFileName(NULL,szName,MAX_PATH);
      CString strInfo(_T(""));
      TCHAR* pstrVerInfo = NULL;
      DWORD VerInfoLen, imsi;
      UINT imsi2;
      LPVOID pstrInfo;
      VerInfoLen = GetFileVersionInfoSize( szName, &imsi );
      pstrVerInfo = new TCHAR[VerInfoLen];
      if( ::GetFileVersionInfo( szName,(DWORD )0, VerInfoLen,(LPVOID )pstrVerInfo ) )
      {
            TCHAR szPath[MAX_PATH];
            _stprintf(szPath,_T("\\StringFileInfo\\040904e4\\%s"),sInfo);
            if( VerQueryValue( pstrVerInfo,szPath, (LPVOID*)&pstrInfo, &imsi2 ) )
                  strInfo = (TCHAR*)pstrInfo;
      }
      delete pstrVerInfo;
      pstrVerInfo = NULL;
      return strInfo;
}

040904e4 is your Block Header value. Open the Resource Tab->Version INFO of your project in Visual Studio (I am using 2005) to find the Block Header Value.

How to Set Window Transparency

To set the transparency of a window, use SetLayeredWindowAttributes of user32.dll – a well known API introduced in and after Windows 2000.

C++
void SetTransparency(HWND hwnd, UINT nAlpha)
{
      HMODULE hUser32 = GetModuleHandle(_T("USER32.DLL"));
      typedef BOOL (WINAPI *lpfn) (HWND hWnd, COLORREF cr,BYTE bAlpha, DWORD dwFlags);
      lpfn pSetLayeredWindowAttributes;   
      pSetLayeredWindowAttributes = 
        (lpfn)GetProcAddress(hUser32,"SetLayeredWindowAttributes");
      if(!pSetLayeredWindowAttributes) return;
      LONG nStyle=GetWindowLong(hwnd, GWL_EXSTYLE);
      if(( nStyle | 0x00080000)!=nStyle) 
            ::SetWindowLong(hwnd, GWL_EXSTYLE, nStyle ^ 0x00080000);
      pSetLayeredWindowAttributes(hwnd, 0,(BYTE) nAlpha, 0x00000002);
      ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
}

where hwnd must be a top level window and nAlpha can be 0 to 255.

The above function is safe, and you will not get the API or DLL not found error if you run your product in an OS older than Windows 2000. And, it is my advice to use GetModuleHandle() and GetProcAddress() to call a newly introduced API to support older Operating Systems.

How to Set the Desktop Wallpaper Programmatically

To set the wallpaper of your desktop programmatically, you have to create an instance of the IActiveDesktop interface. Using the created instance, you can access the various functions of the Active Desktop object. Refer MSDN for more information about the Active Desktop object.

C++
int SetWallpaper(LPCTSTR szPath)
{
      CoInitialize ( NULL );
      USES_CONVERSION;
      HRESULT hr;
      IActiveDesktop* pIAD;
      hr = CoCreateInstance ( CLSID_ActiveDesktop, NULL, 
             CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (void**) &pIAD );
      if ( SUCCEEDED(hr) )
      {
            hr = pIAD->SetWallpaper (T2W(szPath),0);
            hr = pIAD->ApplyChanges(AD_APPLY_ALL);
            pIAD->Release();
      }
      if ( FAILED(hr) ) return 0;
      CoUninitialize();
      return 1;
}

How to Restart your Computer Programmatically

Simply calling ExitWindowsEx() with EWX_REBOOT may fail to restart a computer due to access privileges provided to your process. The following function will enable access privileges to the current process to restart the computer.

C++
BOOL RestartComputer()
{
   HANDLE hToken; 
   TOKEN_PRIVILEGES tkp; 
   // Get a token for this process. 
   if (!OpenProcessToken(GetCurrentProcess(), 
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
      return( FALSE ); 
   // Get the LUID for the shutdown privilege. 
   LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); 
   tkp.PrivilegeCount = 1;  // one privilege to set    
   tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
   // Get the shutdown privilege for this process. 
   AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0); 
   if (GetLastError() != ERROR_SUCCESS) 
      return FALSE; 
   // Shut down the system and force all applications to close. 
   if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,0) )
      return FALSE; 
   return TRUE;
}

How to Get the Version of a File

Given below is a straightforward code, which uses GetFileVersionInfo to get the version information of a given file.

C++
bool GetFileVersion(LPCTSTR lpszFilePath, DWORD &dwMajor, 
     DWORD &dwMinor, DWORD &dwSubMinor, DWORD &dwBuild)
{
      DWORD dwDummy;
      bool bResult=false;
      DWORD dwFVISize = GetFileVersionInfoSize( lpszFilePath , &dwDummy ); 
      LPBYTE lpVersionInfo = new BYTE[dwFVISize]; 
      VS_FIXEDFILEINFO *lpFfi;
      bResult=GetFileVersionInfo( lpszFilePath , 0 , dwFVISize , 
                                  lpVersionInfo )?true:false; 
      if(!bResult){ delete []lpVersionInfo; return false;}
      UINT uLen; 
      bResult=VerQueryValue(lpVersionInfo , _T("\\") , 
                 (LPVOID *)&lpFfi , &uLen )?true:false; 
      DWORD dwFileVersionMS = lpFfi->dwFileVersionMS; 
      DWORD dwFileVersionLS = lpFfi->dwFileVersionLS; 
      delete [] lpVersionInfo; 
      dwMajor=HIWORD(dwFileVersionMS);
      dwMinor=LOWORD(dwFileVersionMS);
      dwSubMinor=HIWORD(dwFileVersionLS);
      dwBuild=LOWORD(dwFileVersionLS);
      return bResult;
}

Checking the Current Operating System:

Since Vistra should only run in Windows XP, I have the following function to check the current OS in which Vistra is running:

C++
bool IsWinXP()
{
      OSVERSIONINFO osver;
      memset(&osver, 0, sizeof(OSVERSIONINFO));
      osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
      if(!GetVersionEx( &osver ))
            return false;
      if(osver.dwPlatformId == VER_PLATFORM_WIN32_NT && 
         osver.dwMajorVersion == 5 && osver.dwMinorVersion>=1 )
            return true;
      return false;
}
Operating SystemVersion number
Windows Server 20086.0
Windows Vista6.0
Windows Server 2003 R25.2
Windows Server 20035.2
Windows XP5.1
Windows 20005.0

Changing Drive Icons

Create a Registry key named DriveIcons (if not exists) in "Software\Microsoft\Windows\CurrentVersion\Explorer".

C++
HKEY hKey;

DWORD dwError=RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons"),
0,0,0,KEY_ALL_ACCESS,NULL,&hKey,0);

if(dwError!=ERROR_SUCCESS) return false;

Create a subkey with the drive name for which you want to change the icon. For example, for C:, create a subkey named "C".

Create a subkey "DefaultIcon" for the drive key.

C++
dwError=RegCreateKeyEx(hKey,_T("C"),0,0,0, 
               KEY_ALL_ACCESS,0,&hKey,0);          // DriveIcons\C

if(dwError!=ERROR_SUCCESS) return false;

dwError=RegCreateKeyEx(hKey,_T("DefaultIcon"),0,0,0,
                  KEY_ALL_ACCESS,0,&hKey,0);      // DriveIcons\C\DefaultIcon

if(dwError!=ERROR_SUCCESS) return false;

Set an icon path in the default value of the "DefaultIcon" key. I have all the icons in Vistra itself, so I have set the Vistra EXE path and the icon index as the icon path.

C++
if(dwError==ERROR_SUCCESS)
{
    TCHAR szName[MAX_PATH+1];
    GetModuleFileName(NULL,szName,MAX_PATH);
    CString sFilename;
    sFilename.Format(_T("%s,%d"),szName,nIndex);
    RegSetValueEx(hDriveKey,_T(""),0,REG_EXPAND_SZ, 
         (BYTE *)sFilename.GetString(),sFilename.GetLength()*sizeof(TCHAR));
}

Use the GetDriveType() function to determine the type of a drive and to set the icon appropriately.

C++
int nType=::GetDriveType(dr); 
if (nType > DRIVE_NO_ROOT_DIR )
{
      // Set icon here
      // nType==DRIVE_CDROM
      // nType==DRIVE_FIXED
      // nType==DRIVE_REMOVABLE
      // etc.,
}

Warning: The code explained below will make changes to your Operating System files. All the code provided here are for knowledge purposes only. If you are trying any of the code below, make sure you know everything you are actually doing. I am not responsible for any damages that might happen to your OS. I recommend you install Microsoft Virtual PC and try everything in your Virtual PC.

If you have ever tried to search in the Internet for code for changing the Windows XP bootskin, then you should have come across the file named ntoskrnl.exe. As already warned, this is a system file (your OS kernel actually), and you are going to modify some of the bytes in this executable to get your own bootskin at startup. So, be careful while coding and testing it. Make sure you take a backup of the original ntoskrnl.exe. At any point, if you mess up something, use Windows System Restore Point to get back through Safe Mode.

Windows File Protection (WFP)

Here is what you will get to know in this topic:

  • Why should you have to know all these?
  • What is Windows File Protection?
  • What are sfc.dll and sfc_os.dll?
  • What is ordinal number?
  • How to disable WFP using SetSfcFileException()
  • How to disable WFP using SfcTerminateWatcherThread()
  • How to disable WFP through the Registry

Why Should You Have to Know all These?

Ntoskrnl.exe is a system file, and is protected by WFP. So, you cannot modify the executable when it is protected. Once the WFP watcher gets a file change notification on protected files, it will replace the altered file to original.

What is Windows File Protection?

Windows File Protection (WFP) prevents programs from replacing critical Windows system files. Programs must not overwrite these files because they are used by the Operating System and by other programs. Protecting these files prevents problems with programs and the Operating System.

For more information, see: Description of the Windows File Protection feature.

What are sfc.dll and sfc_os.dll?

SFC stands for System File Checker. SFC.exe is the application which will scan the Windows protected files for changes, and will replace the files to original, if altered. sfc.dll is a wrapper library which will redirect some of the function calls to sfc_os.dll. sfc.dll has some of the functions by its own. Sfc_os.dll is the library which has functions to protect and unprotect, enumerate protected files, etc.

Functions in sfc_os.dll:

  • SfcGetNextProtectedFile()
  • SfcIsFileProtected()
  • SfcWLEventLogoff()
  • SfcWLEventLogon()

Functions in sfc.dll (wrapper for all the above functions):

  • SRSetRestorePoint()
  • SRSetRestorePointA()
  • SRSetRestorePointW()
  • SfpVerifyFile()

There are almost 11 functions inside sfc_os.dll. Only four of those are named. Other functions have no information, and can only be referred by their ordinal numbers.

What is Ordinal Number?

Ordinal Number is nothing but a unique identifier for the functions within the DLL.

How to Disable WFP Using SetSfcFileException()

The function DisableWindowsFileProtection() explained below calls the SetSfcFileException() function in sfc_os.dll using its ordinal number 5. You will have one minute time to edit the file after the SetSfcFileException() call. The return value is 0 if success / 1 if an error or if the file is not protected.

C++
bool DisableWindowsFileProtection(LPCTSTR szFilename)
{
  USES_CONVERSION;
  TCHAR szSystemDir[MAX_PATH+1];
  int nSize=GetSystemDirectory(szSystemDir,MAX_PATH);
  szSystemDir[nSize]='\0';
  TCHAR szSFCOS[MAX_PATH+1];
  _tcscpy(szSFCOS,szSystemDir);
  _tcscat(szSFCOS,_T("\\sfc_os.dll"));
  HMODULE hSFSModule=::LoadLibrary(szSFCOS);
  if(!hSFSModule) return false;
  typedef DWORD (__stdcall *SETSFCFILEEXCEPTION) 
           (DWORD dwReserved, PWCHAR dwPath, DWORD dwParam2);
  SETSFCFILEEXCEPTION pFnSetSfcFileException;
  pFnSetSfcFileException= 
    (SETSFCFILEEXCEPTION) GetProcAddress(hSFSModule,(LPCSTR)5);
  if(pFnSetSfcFileException)
  {
        pFnSetSfcFileException(0,T2W(szFilename),-1);
  }
  else
  {
        ::FreeLibrary(hSFSModule);
        return false;
  }
  ::FreeLibrary(hSFSModule);
  return true;
}

How to Disable WFP Using SfcTerminateWatcherThread()

SfcTerminateWatcherThread() explains itself by its name. This function call will terminate the Watcher Thread, and there will be no WFP until the next reboot. The function has no parameters, and is so simple to call. But, there is a caveat on using this function; the function call should be made from the process which started the watcher thread, which is Winlogon.exe. Just explore how to inject your code into another process; I believe Robert Kuster's article will be helpful enough: Three Ways to Inject Your Code into Another Process.

How to Disable WFP Through the Registry

Create and set the following Registry value:

  • Key: HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\Windows File Protection
  • Name: SFCDisable
  • Value: 0xFFFFFF9D

More than enough I think, our goal is just to disable WFP for a few seconds. So, the SfcFileException() call is enough.

Do remember, disabling WFP is more dangerous. It should be enabled again, or else any virus/malwares may affect your system files.

Modifying the Default bootskin with Your Own Image

Why should I change my bootskin when it is already looking good? Why should I take this much risk in modifying a simple bootskin which will do nothing for me? Answer for both the above questions are simple. As a simple user, don’t try this, your question is correct. As a developer, try this, but with care, so that you will learn a few things you have never come across.

Copy your ntoskrnl.exe to the desktop or wherever you want, temporarily. Open your Visual Studio (I am using 2005), go to "File->Open->File…". In the file open dialog, choose "File of Type" as "*.dll or *.exe". Select the copied ntoskrnl from the location where you copied. Done? So, you should get the resource files of ntoskrnl.exe. Open "Bitmap" and choose Bitmap with id "1". This is your default bootskin image. Couldn’t believe it, right? How a full black image you are seeing here comes with a Windows logo and text on bootup? Open the "Bitmap" numbered "8". The same black image, but a smaller one, and this is the progress bar you can see on the bootskin. Where are those actual images? Are they hidden behind these black images? Partially, yes. It is a little tough to understand this may be, a little dry too, only if you are not already familiar with palettes. OK, before going further, open "Bitmap" numbered "5" once. Surprised? Is this the boot image? But, it is not. All the three bitmaps have beautiful relationships in between.

I don’t want to confuse you anymore. Simply, 1 and 8 are your boot skin and progress skin respectively and 5 is the image holding palette information for both 1 and 8. Since 1 and 8 have no palette information, you cannot able to see the image and those are black as well. To see both the images properly, you have to apply palette information of bitmap 5 to 1 and 8. But, still you will not get the actual image, but you can see the image in different color. Why so? This is because, the palette information in bitmap 5 is modified, and we can say it as encrypted, each color in the palette are shifted one level up. So when using this palette, we have to shift the entire colors one step down to get the original palette. If we did so, what will happen to the last color? Yes, we will be missing the last color. So there will be only 15 colors in the palette table, not 16.

Before proceeding, I want to tell you one important thing that, Windows XP’s bootskin image should be of resolution 640x480x16. ie., 16 colors bitmap of 640x480 size. So when you choose a bitmap file to replace the bootskin, make sure it is 640x480 and 16 colors. Vistra can handle this case, ie., it will convert the given bitmap to 640x480x16. But, I used some of the third party bitmap conversion codes over there and I am not providing those codes here.

I am giving the entire code here to change the bootskin of Windows XP. The following code will not work with palettes or any other image processing techniques. I have directly opened ntoskrnl.exe and searched for the palette pattern and replaced it with my own. The ReplaceBootSkin function will receive szFilename (should be ntoskrnl.exe), szSkinFile (your image file), and nSkinReplace (resource ID in ntoskrnl.exe) as parameters. SavePalette() will save the given palette's information and will erase the bootskin’s palette information. The RewritePalette() function will search for a pattern of bytes (palette bytes 00 00 00 00 15 1A 20 00) in the given szFilename (ntoskrnl.exe), and will replace all consecutive 64 bytes (palette size) with the saved palette information (from SavePalette). So, you don’t need to worry about shifting palettes, grabbing palette information from Bitmap 5 etc. I will appreciate it if you can show me a way to do the same in a better way using palettes.

Keep the above in mind, and hold on here. Have we missed anything? We disabled WFP, and we know how to replace a bitmap in place of the default bootskin bitmap in ntoskrnl.exe. What else should we need to know? How to update a resource bitmap in an executable with your own bitmap from a file? Have you ever done that before?

Yes. We have one more step to go. Here comes the BeginUpdateResource(), UpdateResource(), EndUpdateResource() APIs. Refer MSDN or even the code below to understand what these APIs will do. Simply, BeginUpdateResource() will get a handle to the resource for the update. UpdateResource() will update the resource associated with the handle with the given new resource data. EndUpdateResource() will close the resource handle.

There is a difference between a bitmap in a file and a bitmap in a resource. UpdateResource() needs only a bitmap handle from the resource. The difference is the bitmap file header; resource bitmaps have no bitmap file header information. So, you can note in the ReplaceBootSkin() function, the code will first remove the bitmap file header information.

C++
void SavePalette(LPBYTE pData, LPBYTE palette)
{
      BYTE byte[64];
      memcpy(byte,palette,64);
      memcpy(palette,pData+0x28,64);
      memcpy(pData+0x28,byte,64);
}


bool RewritePalette(CString sFilename, LPBYTE palette)
{
      BYTE find[8];
      find[0]=0x0;find[1]=0x0;find[2]=0x0;find[3]=0x0;
      find[4]=0x15;find[5]=0x1A;find[6]=0x20;find[7]=0x0;
      BYTE read[8];
      memset(read,0,8);
      BYTE pal[64];
      memcpy(pal,palette,64);
      CString sTmp=sFilename+_T(".tmp");
      FILE *fp=_tfopen(sFilename,_T("rb"));
      if(!fp) return false;
      FILE *ofp=_tfopen(sTmp,_T("wb"));
      if(!ofp) {fclose(fp);return false;}
      int i=0;
      while(!feof(fp))
      {
            read[i]=fgetc(fp);
            if(i==7)
            {
                  i=0;
                  if(memcmp(find,read,8)==0)
                  {
                        fwrite((void *)palette,sizeof(BYTE),64,ofp);
                        // 8 bytes already read using fgetc()
                        fread((void *)palette,sizeof(BYTE),64-8,fp); 
                  }
                  else
                  {
                        fwrite((void *)read,sizeof(BYTE),8,ofp);
                  }
            }
            else i++;
      }
      if(i>1)
      {
            fwrite((void *)read,sizeof(BYTE),i,ofp);
      }
      fclose(fp);
      fclose(ofp);
      ::DeleteFile(sFilename);
      ::rename(sTmp,sFilename);
      return true;
}

bool ReplaceBootSkin(LPCTSTR szFilename, LPCTSTR szSkinFile, UINT nSkinReplace)
{
      HANDLE hUpdateRes;  // update resource handle 
      DWORD dwSize=0;
      HANDLE hFile;
      if ((hFile = CreateFile(sFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
            NULL)) == INVALID_HANDLE_VALUE)     return false;
       DWORD dwBitmapSize = GetFileSize(hFile, NULL) - sizeof(BITMAPFILEHEADER);     
      BITMAPFILEHEADER    bmfHeader;
      DWORD dwRead;
      if (!ReadFile(hFile, (LPSTR)&bmfHeader, 
                 sizeof (BITMAPFILEHEADER),&dwRead, NULL)) 
      {
            CloseHandle(hFile);
            return false;
      }

      if (sizeof (BITMAPFILEHEADER) != dwRead || bmfHeader.bfType != 0x4d42) 
      {
            CloseHandle(hFile);
            return false;
      }
     
      HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,dwBitmapSize);
      if(!hGlobal) 
      {
            CloseHandle(hFile);
            return false;
      }

      LPVOID bmpData=GlobalLock(hGlobal);
      if(!ReadFile(hFile, bmpData, dwBitmapSize,&dwRead,NULL))
      {
            GlobalUnlock(hGlobal);
            GlobalFree(hGlobal);
            CloseHandle(hFile); 
            return false;
      }
      CloseHandle(hFile);

      BYTE palette[64];
      memset(palette,0,64);
      SavePalette((LPBYTE)bmpData, palette, false);

      hUpdateRes = BeginUpdateResource(szFilename, FALSE); 
      if (hUpdateRes == NULL) 
      {
            GlobalUnlock(hGlobal);
            GlobalFree(hGlobal);
            return false;
      }
 
      if(!UpdateResource
      (     
            hUpdateRes, 
            RT_BITMAP, 
            MAKEINTRESOURCE(nSkinReplace), 
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 
            bmpData, 
            dwBitmapSize
      )) 
      { 
            GlobalUnlock(hGlobal);
            GlobalFree(hGlobal);
            return false;
      } 

      if (!EndUpdateResource(hUpdateRes, FALSE)) 
      { 
            GlobalUnlock(hGlobal);
            GlobalFree(hGlobal);
            return false;
      } 
      GlobalUnlock(hGlobal);
      GlobalFree(hGlobal);
      RewritePalette(szFilename,palette);
      return true;
}

Setting Visual Style (Themes) Programmatically

Uxtheme.dll is your visual styler which is handling all your theme functionalities. Some of the uxtheme.dll functions have no name, and can be referred only through their ordinal numbers. The SetVisualStyle() function is one of those. Its ordinal number is 65. Use this function to set a theme programmatically.

C++
bool SetVisualStyle(LPCTSTR szVistraTheme)
{
      TCHAR szUxTheme[MAX_PATH+1];
      UINT nSize=::GetSystemDirectory(szUxTheme,MAX_PATH);
      szUxTheme[nSize]='\0';
      _tcscat(szUxTheme,_T("\\uxtheme.dll"));
      HMODULE hModule=::LoadLibrary(szUxTheme);
      if(!hModule) return false;
      typedef int (__stdcall *SETVISUALSTYLE) (LPCWSTR szTheme, 
                   LPCWSTR szScheme, LPCWSTR szFontType, int nReserved);
      USES_CONVERSION;  
      SETVISUALSTYLE pFnSetVisualStyle;
      pFnSetVisualStyle=(SETVISUALSTYLE) 
              GetProcAddress(hModule,MAKEINTRESOURCE(LOWORD(65)));
      if(pFnSetVisualStyle)
      {
            pFnSetVisualStyle(T2W(szVistraTheme), 
                  L"NormalColor",L"NormalSize",1|32);
      }
      ::FreeLibrary(hModule);
      return true;
}

Conclusion

I am not providing a sample project with this article. The reason is, I don’t want the reader of this article to just download and do trial and error, or commenting and uncommenting the code, since some of the code in this article is a little dangerous if handled without care. Again, I remind you, this article is only for knowledge purposes, and if you want to try experimenting the code, try it in a "MS Virtual PC" or "VMware". You can create a dialog application and copy-paste the code given in the article to see the output. I request you to understand the code before attempting to run it. Instead of calling it an article, I'd call it an experience I got during Vistra development, and I am sharing this with you all. Thank you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)