title: Using GetPrivateProfileStringW with UTF-8 encoded files?
body: <p>Up until now my INI text files have been <strong>Unicode</strong> encoded and I have been doing things like this to read it:</p>
  
  <pre><code>::GetPrivateProfileStringW(
                  strUpdateSection,
                  strTalkKey,
                  _T(""),
                  strTalk.GetBuffer( _MAX_PATH ),
                  _MAX_PATH,
                  m_strPathINI );
  strTalk.ReleaseBuffer();
  </code></pre>
  
  <p>The file gets downloaded from the internet first and then accessed. But I was finding that for <strong>Arabic</strong> the text file was getting corrupted. Unless I change the encoding to <strong>UTF-8</strong>. When I do that, it downloads right. But then it doesn't read the data right from the INI.</p>
  
  <p>So does a INI file have to be Unicode encoded or should it also be OK to use UTF-8 with the function calls?</p>
  
  <p>I think it is about time I convert this part of my program to UTF-8 encoded XML files instead! Way to go.</p>
  
  <p>But wanted to ask the question first.</p>
  
  <p>I should clarify that the text file was initial saved as UTF-8 using NotePad.</p>
  
  <h2>Update</h2>
  
  <p>This is what it looks like when I read the file:</p>
  
  <p><a href="https://i.stack.imgur.com/uHBIj.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/uHBIj.png" alt="Arabic results"></a></p>
  
  <p>This is how I download the file:</p>
  
  <pre><code>BOOL CUpdatePublicTalksDlg::DownloadINI()
  {
      CInternetSession    iSession;
      CHttpFile           *pWebFile = NULL;
      CWaitCursor         wait;
      CFile               fileLocal;
      TCHAR               szError[_MAX_PATH];
      int                 iRead = 0, iBytesRead = 0;
      char                szBuffer[4096];
      BOOL                bOK = FALSE;
      DWORD               dwStatusCode;
      CString             strError;
  
      // ask user to go online
      if( InternetGoOnline( (LPTSTR)(LPCTSTR)m_strURLPathINI, GetSafeHwnd(), 0 ) )
      {
          TRY
          {
              // our session should already be open
              // try to open up internet session to my URL
              // AJT V10.4.0 Use flag INTERNET_FLAG_RELOAD
              pWebFile = (CHttpFile*)iSession.OpenURL( m_strURLPathINI, 1,
                  INTERNET_FLAG_TRANSFER_BINARY |
                  INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD);
  
              if(pWebFile != NULL)
              {
                  if( pWebFile-&gt;QueryInfoStatusCode( dwStatusCode ) )
                  {
                      // 20x codes mean success
                      if( (dwStatusCode / 100) == 2 )
                      {
                          // Our downloaded file is only temporary
                          m_strPathINI = theApp.GetTemporaryFilename();
  
                          if( fileLocal.Open( m_strPathINI,
                              CFile::modeCreate|CFile::modeWrite|CFile::typeBinary ) )
                          {
                              iRead = pWebFile-&gt;Read( szBuffer, 4096 );
                              while( iRead &gt; 0 )
                              {   
                                  iBytesRead += iRead;
                                  fileLocal.Write( szBuffer, iRead );
                                  iRead = pWebFile-&gt;Read( szBuffer, 4096 );
                              }
                              fileLocal.Close();
  
                              bOK = TRUE;
                          }
                      }
                      else
                      {
                          // There was a problem!
                          strError.Format( IDS_TPL_INVALID_URL, dwStatusCode );
                          AfxMessageBox( strError,
                                         MB_OK|MB_ICONERROR );
                      }
                  }
              }
              else
              {
                  AfxMessageBox( ID_STR_UPDATE_CHECK_ERR, MB_OK|MB_ICONERROR );
              }
          }
          CATCH( CException, e )
          {
              e-&gt;GetErrorMessage( szError, _MAX_PATH );
              AfxMessageBox( szError, MB_OK|MB_ICONERROR );
          }
          END_CATCH
  
          // Tidy up
          if( pWebFile != NULL )
          {
              pWebFile-&gt;Close();
              delete pWebFile;
          }
  
          iSession.Close();
      }
  
      return bOK;
  }
  </code></pre>
  
  <p>This is how I read the file:</p>
  
  <pre><code>int CUpdatePublicTalksDlg::ReadTalkUpdateINI()
  {
      int         iLastUpdate = 0, iUpdate;
      int         iNumTalks, iTalk;
      NEW_TALK_S  sTalk;
      CString     strUpdateSection, strTalkKey, strTalk;
  
      // How many possible updates are there?
      m_iNumUpdates = ::GetPrivateProfileIntW(
                          _T("TalkUpdates"),
                          _T("NumberUpdates"),
                          0,
                          m_strPathINI );
  
      // What what the last talk update count?
      iLastUpdate = theApp.GetLastTalkUpdateCount();
  
      // Loop available updates
      for( iUpdate = iLastUpdate + 1; iUpdate &lt;= m_iNumUpdates; iUpdate++ )
      {
          // Build section key
          strUpdateSection.Format( _T("Update%d"), iUpdate );
  
          // How many talks are there?
          iNumTalks = ::GetPrivateProfileIntW(
                              strUpdateSection,
                              _T("NumberTalks"),
                              0,
                              m_strPathINI );
          // Loop talks
          for( iTalk = 1; iTalk &lt;= iNumTalks; iTalk++ )
          {
              // Build key
              strTalkKey.Format( _T("Talk%d"), iTalk );
  
              // Get talk information
              ::GetPrivateProfileStringW(
                              strUpdateSection,
                              strTalkKey,
                              _T(""),
                              strTalk.GetBuffer( _MAX_PATH ),
                              _MAX_PATH,
                              m_strPathINI );
              strTalk.ReleaseBuffer();
  
              // Decode talk information
              DecodeNewTalk( strTalk, sTalk );
  
              // Does it already exists in the database?
              // AJT v11.2.0 Bug fix - we want *all* new talks to show
              //if( !IsExistingTalk( sTalk.iTalkNumber ) )
              //{
              //if(!LocateExistingTheme(sTalk, false))
              AddNewTalkToListBox( sTalk );
              //}
          }
      }
  
      // Return the actual amount of updates possible
      return m_iNumUpdates - iLastUpdate;
  }
  </code></pre>
  
  <p>This is the file being downloaded:</p>
  
  <p><a href="http://publictalksoftware.co.uk/TalkUpdate_Arabic2.ini" rel="nofollow noreferrer">http://publictalksoftware.co.uk/TalkUpdate_Arabic2.ini</a></p>
  
  <h2>Update</h2>
  
  <p>It seems that the file is corrupt at the point of being downloaded:</p>
  
  <p><a href="https://i.stack.imgur.com/4Qw4P.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4Qw4P.png" alt="Corrupt"></a></p>
  
  <p>Please see updated Watch:</p>
  
  <p><a href="https://i.stack.imgur.com/a3r8W.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/a3r8W.png" alt="Watch"></a></p>
  
  <p>Making progress, I now confirm the data OK at this point:</p>
  
  <p><a href="https://i.stack.imgur.com/9XMCl.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/9XMCl.png" alt="Watch results with s8 format specifier"></a></p>