title: how to update row data fetched using ATL OLE database in C++ from simple SQL server table
body: <p>I am experimenting with OLE database programming using Microsoft ATL and C++ with Visual Studio 2005.  I have created a sample Windows console program along with a simple database with a single table containing two columns, a 10 character (ANSI numeric digits) id and a signed 32 bit long count.  There are two rows in the table.  The database, table, and rows were created with SQL Server 2005 Server Management Studio Express.</p>
  
  <p>I need to know the method used with ATL to update the data in a row that has been fetched.</p>
  
  <p>I may be doing the fetch improperly which may be the problem.  During program execution I am seeing three ASSERTs which seem to involve a call to a method <code>GetInterfacePtr()</code> that is being invoked by the <code>myTable.Open ()</code> when executing the SQL queries for retrieving rows.  However the HRESULT value returned is S_OK and the SQL query appears to be working properly.</p>
  
  <p>The <code>HRESULT</code> from the <code>myTable.SetData()</code> has a value of <code>E_NOINTERFACE</code> according to the debugger which is an error return and the selected row is not being updated however I do not know what that means nor what changes are needed to update the row.</p>
  
  <p>The Table_1.h include file is generated by a wizard in Visual Studio 2005 however the class is derived from a CCommand type of template.</p>
  
  <pre><code>class CTable_1 : public CCommand&lt;CAccessor&lt;CTable_1Accessor&gt; &gt;
  </code></pre>
  
  <p>The main program source is:</p>
  
  <pre><code>#include "stdafx.h"
  #include &lt;string&gt;
  #include &lt;iostream&gt;
  
  // the VS 2005 generated include file for the table.
  #include "Table_1.h"
  
  int _tmain(int argc, _TCHAR* argv[])
  {
      HRESULT hrResult = OleInitialize(NULL);
  
      switch (hrResult)
      {
          case S_OK:
              break;
          default:
              std::cout &lt;&lt; "Ole Initialization Failed " &lt;&lt; hrResult &lt;&lt; std::endl;
              return FALSE;
      }
  
      CTable_1  myTable;
  
      HRESULT  hr = myTable.OpenAll ();
  
      std::string m_strQuery("select * from Table_1");
      hrResult = myTable.Open(myTable.m_session, m_strQuery.c_str());
      if (hrResult == S_OK) {
          int nItem = 0;
          while (myTable.MoveNext() == S_OK)
          {
              if (nItem &lt; 25)
              {
                  char szValueChar[40];
                  for (int i = 0; i &lt; 40; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
                  std::string sTemp (szValueChar);
                  std::cout &lt;&lt; nItem &lt;&lt; "  -&gt; " &lt;&lt; sTemp &lt;&lt; " : " &lt;&lt; myTable.m_Count &lt;&lt; std::endl;
                  nItem++;
              }
          }
      } else {
          std::cout &lt;&lt; "assessor open failed " &lt;&lt; hrResult &lt;&lt; " \"" &lt;&lt; m_strQuery &lt;&lt; "\"" &lt;&lt; std::endl;
      }
  
      // Update a specific row in the table by incrementing its count.
      m_strQuery = "select * from Table_1 where IdNumber='0000000001'";
      hrResult = myTable.Open(myTable.m_session, m_strQuery.c_str());
      if (hrResult == S_OK) {
          hrResult = myTable.MoveNext();
          char szValueChar[40];
          for (int i = 0; i &lt; 40; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
          std::string sTemp (szValueChar);
          std::cout &lt;&lt; "  selected item -&gt; " &lt;&lt; sTemp &lt;&lt; " : " &lt;&lt; myTable.m_Count &lt;&lt; std::endl;
          myTable.m_Count++;
              //  &lt;&lt;&lt;&lt;&lt;   Following SetData() returns HRESULT of E_NOINTERFACE
          hrResult = myTable.SetData ();
          if (hrResult != S_OK) std::cout &lt;&lt; "** update error.  hrResult = " &lt;&lt; hrResult &lt;&lt; std::endl;
      }
  
      std::cout &lt;&lt; std::endl &lt;&lt; " after update" &lt;&lt; std::endl;
  
      m_strQuery = "select * from Table_1";
      hrResult = myTable.Open(myTable.m_session, m_strQuery.c_str());
      if (hrResult == S_OK) {
          int nItem=0;
          while (myTable.MoveNext() == S_OK)
          {
              if (nItem &lt; 25)
              {
                  char szValueChar[40];
                  for (int i = 0; i &lt; 40; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
                  std::string sTemp (szValueChar);
                  std::cout &lt;&lt; nItem &lt;&lt; "  -&gt; " &lt;&lt; sTemp &lt;&lt; " : " &lt;&lt; myTable.m_Count &lt;&lt; std::endl;
                  nItem++;
              }
          }
      } else {
          std::cout &lt;&lt; "assessor open failed " &lt;&lt; hrResult &lt;&lt; " \"" &lt;&lt; m_strQuery &lt;&lt; "\"" &lt;&lt; std::endl;
      }
  
      myTable.CloseAll ();
  
      OleUninitialize ();
      return 0;
  }
  </code></pre>
  
  <p>The output of the console window looks like the following:</p>
  
  <pre><code>0  -&gt; 0000000001 : 2
  1  -&gt; 0000000002 : 3
    selected item -&gt; 0000000001 : 2
  ** update error.  hrResult = -2147467262
  
   after update
  0  -&gt; 0000000001 : 2
  1  -&gt; 0000000002 : 3
  </code></pre>
  
  <p><strong>Edit #1 - Table_1.h (wizard generated with suggested change)</strong></p>
  
  <pre><code>// Table_1.h : Declaration of the CTable_1
  
  #pragma once
  
  // code generated on Friday, April 25, 2014, 10:25 PM
  
  class CTable_1Accessor
  {
  public:
      TCHAR m_IdNumber[11];
      LONG m_Count;
  
      // The following wizard-generated data members contain status
      // values for the corresponding fields in the column map. You
      // can use these values to hold NULL values that the database
      // returns or to hold error information when the compiler returns
      // errors. See Field Status Data Members in Wizard-Generated
      // Accessors in the Visual C++ documentation for more information
      // on using these fields.
      // NOTE: You must initialize these fields before setting/inserting data!
  
      DBSTATUS m_dwIdNumberStatus;
      DBSTATUS m_dwCountStatus;
  
      // The following wizard-generated data members contain length
      // values for the corresponding fields in the column map.
      // NOTE: For variable-length columns, you must initialize these
      //       fields before setting/inserting data!
  
      DBLENGTH m_dwIdNumberLength;
      DBLENGTH m_dwCountLength;
  
  
      void GetRowsetProperties(CDBPropSet* pPropSet)
      {
          pPropSet-&gt;AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
          pPropSet-&gt;AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
          //&lt;&lt;  lines added from stackoverflow answer
          pPropSet-&gt;AddProperty(DBPROP_IRowsetChange, (bool) TRUE);
          pPropSet-&gt;AddProperty(DBPROP_IRowsetUpdate, (bool) TRUE);
          pPropSet-&gt;AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT);
      }
  
      HRESULT OpenDataSource()
      {
          CDataSource _db;
          HRESULT hr;
          hr = _db.OpenFromInitializationString(L"Provider=SQLNCLI.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestData;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=CIT-31204E1FF03;Use Encryption for Data=False;Tag with column collation when possible=False;MARS Connection=False;DataTypeCompatibility=0;Trust Server Certificate=False");
          if (FAILED(hr))
          {
  #ifdef _DEBUG
              AtlTraceErrorRecords(hr);
  #endif
              return hr;
          }
          return m_session.Open(_db);
      }
  
      void CloseDataSource()
      {
          m_session.Close();
      }
  
      operator const CSession&amp;()
      {
          return m_session;
      }
  
      CSession m_session;
  
      DEFINE_COMMAND_EX(CTable_1Accessor, L" \
      SELECT \
          IdNumber, \
          Count \
          FROM dbo.Table_1")
  
  
      // In order to fix several issues with some providers, the code below may bind
      // columns in a different order than reported by the provider
  
      BEGIN_COLUMN_MAP(CTable_1Accessor)
          COLUMN_ENTRY_LENGTH_STATUS(1, m_IdNumber, m_dwIdNumberLength, m_dwIdNumberStatus)
          COLUMN_ENTRY_LENGTH_STATUS(2, m_Count, m_dwCountLength, m_dwCountStatus)
      END_COLUMN_MAP()
  };
  
  class CTable_1 : public CCommand&lt;CAccessor&lt;CTable_1Accessor&gt; &gt;
  {
  public:
      HRESULT OpenAll()
      {
          HRESULT hr;
          hr = OpenDataSource();
          if (FAILED(hr))
              return hr;
          __if_exists(GetRowsetProperties)
          {
              CDBPropSet propset(DBPROPSET_ROWSET);
              __if_exists(HasBookmark)
              {
                  if( HasBookmark() )
                      propset.AddProperty(DBPROP_IRowsetLocate, true);
              }
              GetRowsetProperties(&amp;propset);
              return OpenRowset(&amp;propset);
          }
          __if_not_exists(GetRowsetProperties)
          {
              __if_exists(HasBookmark)
              {
                  if( HasBookmark() )
                  {
                      CDBPropSet propset(DBPROPSET_ROWSET);
                      propset.AddProperty(DBPROP_IRowsetLocate, true);
                      return OpenRowset(&amp;propset);
                  }
              }
          }
          return OpenRowset();
      }
  
      HRESULT OpenRowset(DBPROPSET *pPropSet = NULL)
      {
          HRESULT hr = Open(m_session, NULL, pPropSet);
  #ifdef _DEBUG
          if(FAILED(hr))
              AtlTraceErrorRecords(hr);
  #endif
          return hr;
      }
  
      void CloseAll()
      {
          Close();
          ReleaseCommand();
          CloseDataSource();
      }
  };
  </code></pre>