Artigo: 238539 - Última revisão: terça-feira, 15 de Outubro de 2002 - Revisão: 1.1

CORRECÇÃO: Utilização CDynamicAccessor com Unicode colunas faz com que memória substitui e outras falhas

Dica do SistemaEste artigo aplica-se a um sistema operativo diferente do que está a utilizar. Foi desactivado o conteúdo do artigo, que pode não ser relevante para si.
Expandir tudo | Reduzir tudo

Sintomas

Quando utilizar a classe CDynamicAccessor para obter ou definir os valores de uma coluna da Unicode cadeia (por exemplo, um SQL Server 7.0 nchar campo), vários erros possíveis ou sintomas podem ocorrer, incluindo os seguintes:
  • DB_E_ERRORSOCCURED é devolvido quando chamada SetData .
  • A aplicação deixa de responder (não reage).
  • Recebe uma violação de acesso.

Causa

CDynamicAccessor não cria existe memória suficiente para conter a cadeia. CDynamicAccessor utiliza o membro ulColumnSize as informações de coluna para obter o comprimento necessário para a memória intermédia. No entanto, este método produz o número de caracteres para o campo, não o número de bytes. Este valor (o número de caracteres de campo) tem de ser multiplicado pelo 2 [ sizeof(WCHAR) ] para obter o número de bytes necessários para conter a cadeia Unicode.

Além disso, as seguintes funções não calcular o desvio correctamente para as respectivas partes (comprimento e estado):
  • GetLength
  • SetLength
  • SetStatus
  • GetStatus

Resolução

Para contornar este problema, deriva uma nova classe CDynamicAccessor e corrija as funções necessárias multiplicando ulColumnSize por 2 sempre que for encontrada uma coluna DBTYPE_WSTR .

O código de exemplo seguinte é um exemplo de como a classe pode aparecer:
class CDynAccessor: public CDynamicAccessor
{
public:

	bool GetStatus(ULONG nColumn, DBSTATUS* pStatus) const
	{
		ATLASSERT(pStatus != NULL);
		if (TranslateColumnNo(nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)), sizeof(ULONG)));
			else
				*pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG)));
			return true;
		}
		else
			return false;
	}
	
	bool GetStatus(TCHAR* pColumnName, DBSTATUS* pStatus) const
	{
		ATLASSERT(pColumnName != NULL);
		ATLASSERT(pStatus != NULL);
		ULONG nColumn;
		if (GetInternalColumnNo(pColumnName, &nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)), sizeof(ULONG)));
			else
				*pStatus = *(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG)));
			return true;
		}
		else
			return false;
	}
	
	bool SetStatus(ULONG nColumn, DBSTATUS status)
	{
		if (TranslateColumnNo(nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)), sizeof(ULONG))) = status;
			else
				*(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG))) = status;
			return true;
		}
		else
			return false;
	}

	bool SetStatus(TCHAR* pColumnName, DBSTATUS status)
	{
		ATLASSERT(pColumnName != NULL);
		ULONG nColumn;
		if (GetInternalColumnNo(pColumnName, &nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)), sizeof(ULONG))) = status;
			else
				*(ULONG*)(AddOffset(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize), sizeof(ULONG))) = status;
			return true;
		}
		else
			return false;
	}

	bool GetLength(ULONG nColumn, ULONG* pLength) const
	{
		ATLASSERT(pLength != NULL);
		if (TranslateColumnNo(nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)));
			else 
				*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
			return true;
		}
		else
			return false;
	}

	bool GetLength(TCHAR* pColumnName, ULONG* pLength) const
	{
		ATLASSERT(pColumnName != NULL);
		ATLASSERT(pLength != NULL);
		ULONG nColumn;
		if (GetInternalColumnNo(pColumnName, &nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR)));
			else 
				*pLength = *(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize));
			return true;
		}
		else
			return false;
	}

	bool SetLength(ULONG nColumn, ULONG nLength)
	{
		if (TranslateColumnNo(nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR))) = nLength;
			else
				*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
			return true;
		}
		else
			return false;
	}
	
	
	bool SetLength(TCHAR* pColumnName, ULONG nLength)
	{
		ATLASSERT(pColumnName != NULL);
		ULONG nColumn;
		if (GetInternalColumnNo(pColumnName, &nColumn))
		{
			if (DBTYPE_WSTR == m_pColumnInfo[nColumn].wType)
				*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize*sizeof(WCHAR))) = nLength;
			else
				*(ULONG*)(AddOffset((ULONG)_GetDataPtr(nColumn), m_pColumnInfo[nColumn].ulColumnSize)) = nLength;
			return true;
		}
		else
			return false;
	}
	


	HRESULT BindColumns(IUnknown* pUnk)
	{
		ATLASSERT(pUnk != NULL);
		CComPtr<IAccessor> spAccessor;
		HRESULT hr = pUnk->QueryInterface(&spAccessor);
		if (FAILED(hr))
			return hr;

		ULONG   i;
		ULONG   nOffset = 0, nLengthOffset, nStatusOffset;

		// If the user hasn't specifed the column information to bind by calling AddBindEntry then
		// we get it ourselves.
		if (m_pColumnInfo == NULL)
		{
			CComPtr<IColumnsInfo> spColumnsInfo;
			hr = pUnk->QueryInterface(&spColumnsInfo);
			if (FAILED(hr))
				return hr;

			hr = spColumnsInfo->GetColumnInfo(&m_nColumns, &m_pColumnInfo, &m_pStringsBuffer);
			if (FAILED(hr))
				return hr;

			m_bOverride = false;
		}
		else
			m_bOverride = true;

		DBBINDING* pBinding = NULL;
		ATLTRY(pBinding= new DBBINDING[m_nColumns]);
		if (pBinding == NULL)
			return E_OUTOFMEMORY;

		DBBINDING* pCurrent = pBinding;
		DBOBJECT*  pObject;
		for (i = 0; i < m_nColumns; i++)
		{
			// If it's a BLOB or the column size is large enough for us to treat it as
			// a BLOB then we also need to set up the DBOBJECT structure.
			if (m_pColumnInfo[i].ulColumnSize > 1024 || m_pColumnInfo[i].wType == DBTYPE_IUNKNOWN)
			{
				pObject = NULL;
				ATLTRY(pObject = new DBOBJECT);
				if (pObject == NULL)
					return E_OUTOFMEMORY;
				pObject->dwFlags = STGM_READ;
				pObject->iid     = IID_ISequentialStream;
				m_pColumnInfo[i].wType      = DBTYPE_IUNKNOWN;
				m_pColumnInfo[i].ulColumnSize   = sizeof(IUnknown*);
			}
			else
				pObject = NULL;

			// If column is of type STR or WSTR increase length by 1
			// to accommodate the NULL terminator.
			if (m_pColumnInfo[i].wType == DBTYPE_STR ||
				m_pColumnInfo[i].wType == DBTYPE_WSTR)
					m_pColumnInfo[i].ulColumnSize += 1;

			if (m_pColumnInfo[i].wType == DBTYPE_WSTR)
				nLengthOffset = AddOffset(nOffset, m_pColumnInfo[i].ulColumnSize*sizeof(WCHAR));
			else 
				nLengthOffset = AddOffset(nOffset, m_pColumnInfo[i].ulColumnSize);

			nStatusOffset = AddOffset(nLengthOffset, sizeof(ULONG));


			ULONG cbMaxLen;
			if (m_pColumnInfo[i].wType == DBTYPE_WSTR)
				cbMaxLen = m_pColumnInfo[i].ulColumnSize*sizeof(WCHAR);
			else 
				cbMaxLen = m_pColumnInfo[i].ulColumnSize;

			CAccessorBase::Bind(pCurrent, m_pColumnInfo[i].iOrdinal, m_pColumnInfo[i].wType,
				cbMaxLen, m_pColumnInfo[i].bPrecision, m_pColumnInfo[i].bScale,
				DBPARAMIO_NOTPARAM, nOffset,
				nLengthOffset, nStatusOffset, pObject);

			pCurrent++;

			// Note that, because we're not using this for anything else, we're using the
			// pTypeInfo element to store the offset to our data.
			m_pColumnInfo[i].pTypeInfo = (ITypeInfo*)nOffset;

			nOffset = AddOffset(nStatusOffset, sizeof(DBSTATUS));
		}
		// Allocate the accessor memory if we haven't done so yet.
		if (m_pAccessorInfo == NULL)
		{
			hr = AllocateAccessorMemory(1); // We only have one accessor.
			if (FAILED(hr))
			{
				delete [] pBinding;
				return hr;
			}
			m_pAccessorInfo->bAutoAccessor = TRUE;
		}

		// Allocate enough memory for the data buffer and tell the rowset.
		// Note that the rowset will free the memory in its destructor.
		m_pBuffer = NULL;
		ATLTRY(m_pBuffer = new BYTE[nOffset]);
		if (m_pBuffer == NULL)
		{
			delete [] pBinding;
			return E_OUTOFMEMORY;
		}
		hr = BindEntries(pBinding, m_nColumns, &m_pAccessorInfo->hAccessor,
				nOffset, spAccessor);
		delete [] pBinding;

		return hr;
	}
};

				

Ponto Da Situação

A Microsoft confirmou que este erro ocorre nos produtos da Microsoft listados no início deste artigo.

Este problema foi corrigido no Microsoft Visual C++. NET.

Mais Informação

Segue-se exemplos de código que falha ao inserir uma cadeia de Unicode 18 caracteres numa tabela SQL com uma coluna de texto Unicode 20 caracteres de nível (nvarchar):
	CDataSource ds;
	CSession sn;

	HRESULT		hr;
	CDBPropSet	dbinit(DBPROPSET_DBINIT);

	dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("sa"));
	dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("pubs"));
	dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("myserver"));
	dbinit.AddProperty(DBPROP_INIT_LCID, (long)1033);
	dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
	hr = ds.Open(_T("SQLOLEDB"), &dbinit);
	ATLASSERT(SUCCEEDED(hr)); //Is the Datasource name and the table name correct, above?	
	sn.Open(ds);
        CCommand< CDynamicAccessor> cmd;

// To fix the problem, remark the above line that uses CDynamicAccessor
// and uncomment the line below that uses the new CDynamicAccessor-derived class.
//	CCommand< CDynAccessor> cmd;  

	cmd.Create(sn, "Select * from TestTable");

	CDBPropSet	propset(DBPROPSET_ROWSET);
	propset.AddProperty(DBPROP_IRowsetChange, true);
	propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);

	
	hr = cmd.Open(&propset);

	hr = cmd.MoveNext();

	ULONG ul;
	DBSTATUS stat;
       	cmd.GetLength(1, &ul);	// This is probably not correct.
	cmd.GetStatus(1, &stat); // This is probably not correct.
		
	wchar_t * pszValue = (wchar_t *)cmd.GetValue(1);
	wcscpy(pszValue, L"12345678901234567");

	int len = wcslen(pszValue);
	cmd.SetLength(1, len*2 );
	cmd.SetStatus(1, DBSTATUS_S_OK);

	hr = cmd.Insert();

	cmd.GetStatus(1, &stat);
				

A informação contida neste artigo aplica-se a:
  • Microsoft OLE DB 2.7 nas seguintes plataformas
    • Microsoft Visual C++ 6.0 Enterprise Edition
    • Microsoft Visual C++ 6.0 Professional Edition
    • Microsoft Visual C++, 32-bit Learning Edition 6.0
Palavras-chave: 
kbmt kbbug kbconsumer kbdatabase kbdtl kbfix kbmdacnosweep kbnoupdate KB238539 KbMtpt
Tradução automáticaTradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine translation ou MT), não tendo sido portanto revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 238539  (http://support.microsoft.com/kb/238539/en-us/ )