Microsoft DirectX 9.0

Adding Region-Change Support to an Application

The following functions are provided for developers who wish to integrate region-change support into their decoders or DVD applications.

// Forward declares, constants, typedefs.
const short MAX_DRIVE_NAME = 4;
struct DriveName
{
    TCHAR name[MAX_DRIVE_NAME];
};
BOOL DoesFileExist(LPTSTR pszFile);
BOOL GetDriveLetter(IDvdInfo2 *pDvd, DriveName& szDrive);

/////////////////////////////////////////////////////////////////////
// ChangeDVDRegion :  Function to change the DVD drive region.
// Parameters:
//     hWnd - Handle to the application window.
// Returns TRUE if the change is successful, or FALSE otherwise.
// (Failure usually means there was no drive with a valid DVD-V disc.)
/////////////////////////////////////////////////////////////////////
BOOL ChangeDVDRegion(HWND hWnd, IDvdInfo2 *pDvd)
{
    typedef BOOL (APIENTRY *DVDPPLAUNCHER) (HWND hWnd, CHAR DriveLetter);
    
    // First find out which drive is a DVD drive with a valid DVD-V disc.
    DriveName  szDVDDrive;

    if (! GetDriveLetter(pDvd, szDVDDrive) )
    {
        MessageBox(hWnd, TEXT("No DVD drive was found with DVD-V disc."),
            TEXT("Error"), MB_OK | MB_ICONERROR) ;
        return FALSE;
    }

    // Detect which OS we are running on. 
    
    OSVERSIONINFO   ver;
    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&ver);

    if (VER_PLATFORM_WIN32_NT  == ver.dwPlatformId)
    {
        // For Windows NT, use the storprop.dll,

        HINSTANCE      hInstDLL;
        DVDPPLAUNCHER  dvdPPLauncher;
        CHAR           szDVDDriveA[MAX_DRIVE_NAME];

        const TCHAR szDllName[] =  TEXT("\\storprop.dll");
        
        // Allocate a large enough string to hold the path + the name.
        TCHAR szCmdLine[MAX_PATH + (sizeof(szDllName)/sizeof(TCHAR)) + 1];

#ifdef UNICODE
        WideCharToMultiByte(CP_ACP, 0, szDVDDrive.name, -1,
            szDVDDriveA, sizeof(szDVDDriveA), NULL, NULL );
#else
        strncpy(szDVDDriveA, szDVDDrive.name, MAX_DRIVE_NAME);
#endif  
        

        UINT cb = GetSystemDirectory(szCmdLine, MAX_PATH + 1);
        if (cb == 0 || cb > MAX_PATH + 1)
        {
            return FALSE;
        }

        lstrcat(szCmdLine, szDllName);
        
        hInstDLL = LoadLibrary (szCmdLine);
        if (hInstDLL)
        {
            BOOL bResult = FALSE;

            dvdPPLauncher = (DVDPPLAUNCHER) GetProcAddress(hInstDLL,
                "DvdLauncher");
            if (dvdPPLauncher)
            {
                 bResult = dvdPPLauncher(hWnd, szDVDDriveA[0]);
            }
            FreeLibrary(hInstDLL);
            return bResult;
        }
    }  
    else  
    {
        // For Windows 9x use the DVDRgn.exe application.

        const TCHAR szUtilName[] = TEXT("\\DVDRgn.exe ");
        
        // Allocate a large enough string, including a one-character command-line argument.
        TCHAR szCmdLine[MAX_PATH + (sizeof(szUtilName)/sizeof(TCHAR)) + 2];

        // Get path of \windows\dvdrgn.exe for command line string
        UINT cb = GetWindowsDirectory(szCmdLine, MAX_PATH + 1);
        if (cb == 0 || cb > MAX_PATH + 1)
        {
            return FALSE;
        }

        lstrcat(szCmdLine, szUtilName);
        
        // Add only the drive letter as command line parameter
        _tcsnccat(szCmdLine, szDVDDrive.name, 1);
        
        // Prepare to execute DVDRgn.exe 
        STARTUPINFO               StartupInfo;
        PROCESS_INFORMATION       ProcessInfo;
        StartupInfo.cb            = sizeof(StartupInfo);
        StartupInfo.dwFlags       = STARTF_USESHOWWINDOW;
        StartupInfo.wShowWindow   = SW_SHOWNORMAL;
        StartupInfo.lpReserved    = NULL;
        StartupInfo.lpDesktop     = NULL;
        StartupInfo.lpTitle       = NULL;
        StartupInfo.cbReserved2   = 0;
        StartupInfo.lpReserved2   = NULL;
        if (CreateProcess(0, szCmdLine, 
            NULL, NULL, TRUE, 
            NORMAL_PRIORITY_CLASS,
            NULL, NULL, &StartupInfo, &ProcessInfo) )
        {
            // Wait until DVDRgn.exe finishes
            WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
            DWORD dwRet = 1;
            BOOL bRet = GetExitCodeProcess(ProcessInfo.hProcess,
                &dwRet);
            // If the user changed the drive region 
            // successfully, the exit code of DVDRgn.exe is 0.
            if (bRet && 0 == dwRet)  
            {
                return TRUE;
            }
        }
    }  

    return FALSE;
} 


/////////////////////////////////////////////////////////////////////
// GetDriveLetter: Returns the drive letter of the active DVD drive.
// Parameters:
//     pDvdC - IDvdControl2 interface of the DVD Navigator filter.
//     pszDrive - Receives the first DVD drive with a valid DVD-V disc.
// Returns TRUE if there is a DVD drive with a valid disc.
/////////////////////////////////////////////////////////////////////
BOOL GetDriveLetter(IDvdInfo2 *pDvd, DriveName& pszDrive) 
{
    WCHAR  szPathW[MAX_PATH];
    TCHAR  szPath[MAX_PATH];
    ULONG  ulActualSize = 0;

    pszDrive.name[0] = pszDrive.name[MAX_DRIVE_NAME - 1] = 0;
    
    // Get the current root directory
    HRESULT hr = pDvd->GetDVDDirectory(szPathW, MAX_PATH, &ulActualSize);

    if (SUCCEEDED(hr))
    {

#ifdef UNICODE
        lstrcpyn(pszDrive.name, szPathW, MAX_DRIVE_NAME);
#else
        WideCharToMultiByte(CP_ACP, 0, szPathW, ulActualSize, szPath, MAX_PATH, 0, 0);
        lstrcpyn(pszDrive.name, szPath, MAX_DRIVE_NAME);
#endif  
        
        if (DRIVE_CDROM == GetDriveType(pszDrive.name)) // could be a DVD drive
            return TRUE;
    }

  // Now loop through all the valid drives to detect which one
  // is a DVD drive with a valid DVD-V disc in it.

  // Get all valid drives
  DWORD dwLen = GetLogicalDriveStrings(MAX_PATH, szPath);

  if (dwLen > MAX_PATH)
  {
      // The function returns a larger value if the buffer is too small.
      return FALSE;
  }

  // Try all drives one by one
  for (DWORD dw = 0; dw < dwLen; ) 
  {
      
      TCHAR *szTmp = szPath + dw;
      
      // Look only for CD-ROM drives that has a disc with required (.ifo) files
      if (DRIVE_CDROM  == GetDriveType(szTmp)) 
      {
          TCHAR   szDVDPath1[MAX_PATH + 1];
          TCHAR   szDVDPath2[MAX_PATH + 1];
          
          lstrcpyn(szDVDPath1, szTmp, MAX_DRIVE_NAME);
          lstrcpyn(szDVDPath2, szTmp, MAX_DRIVE_NAME);
          lstrcat(szDVDPath1, TEXT("Video_ts\\Video_ts.ifo"));
          lstrcat(szDVDPath2, TEXT("Video_ts\\Vts_01_0.ifo"));
          
          // If the .ifo files exist on this drive then it has a valid DVD-V disc
          if (DoesFileExist(szDVDPath1) && DoesFileExist(szDVDPath2))    
          {
              lstrcpyn(pszDrive.name, szTmp, MAX_DRIVE_NAME);
              return TRUE;   // return the first drive that has a valid DVD-V disc
          }
      }
      dw += lstrlen(szTmp) + 1;
  }

  return FALSE ;   // didn't find any DVD drive with DVD-V content
} 

/////////////////////////////////////////////////////////////////////
// DoesFileExist : Determines whether a specified file exists
// Parameters:
//     pszFile - File name to check.
// Returns TRUE if the specified file is found, or FALSE otherwise.
/////////////////////////////////////////////////////////////////////
BOOL DoesFileExist(LPTSTR pszFile)
{
    if (pszFile == NULL)
    {
        return FALSE;
    }

    if (lstrlen(pszFile) > MAX_PATH)
    {
        return FALSE;
    }

    // We don't want any error message box to pop up when we try to test
    // if the required file is available to open for read.
    
    UINT uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
    
    HANDLE hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

    SetErrorMode(uErrorMode);  // restore error mode
    
    if (INVALID_HANDLE_VALUE  == hFile) 
    {
        return FALSE;    
    }
    
    CloseHandle(hFile);
    return TRUE;
}