ID do artigo: 238539 - Última revisão: terça-feira, 15 de outubro de 2002 - Revisão: 1.1

CORRECÇÃO: Usando CDynamicAccessor com colunas Unicode causa memória sobrescreve 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 | Recolher tudo

Sintomas

Quando você usar a classe CDynamicAccessor para recuperar ou definir os valores de uma coluna de seqüência de caracteres de Unicode (por exemplo, um campo de nchar do SQL Server 7.0), vários erros possíveis ou sintomas podem ocorrer, incluindo o seguinte:
  • DB_E_ERRORSOCCURED é retornado quando você chamar SetData .
  • O aplicativo pára de responder (trava).
  • Você recebe uma violação de acesso.

Causa

CDynamicAccessor não irá criar armazenamento suficiente para armazenar a seqüência de caracteres. CDynamicAccessor usa o membro ulColumnSize as informações de coluna para obter o comprimento necessário para o buffer. No entanto, esse método apresenta o número de caracteres para o campo, não o número de bytes. Esse valor (o número de caracteres para o campo) deve ser multiplicado por 2 [ sizeof(WCHAR) ] para obter o número de bytes necessários para armazenar a seqüência de caracteres Unicode.

Além disso, as seguintes funções não calcular o deslocamento corretamente para suas partes (comprimento e status):
  • GetLength
  • SetLength
  • SetStatus
  • GetStatus

Resolução

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

O código de exemplo a seguir é 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;
	}
};

				

Situação

A Microsoft confirmou que esse é um problema nos produtos da Microsoft listados no começo deste artigo.

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

Mais Informações

O seguinte é código de exemplo que Falha ao inserir uma seqüência Unicode de 18 caracteres em uma tabela SQL com uma coluna de texto de Unicode de 20 caracteres toda (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 traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes 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/ )