TN059: Usando Macros de conversão MFC MBCS/Unicode
Observação |
---|
A seguinte nota técnica não foi atualizada desde que foi incluída pela primeira vez na documentação online.Como resultado, alguns procedimentos e tópicos podem estar desatualizado ou incorreto.As informações mais recentes, é recomendável que você procure o tópico de interesse no índice de documentação on-line. |
Esta nota descreve como usar macros para conversão Unicode/MBCS, que são definidos no AFXPRIV.H.Essas macros são mais úteis se seu aplicativo de negócios diretamente com a API OLE ou por algum motivo, geralmente precisa converter entre Unicode e MBCS.
Visão Geral
No MFC 3. x, uma DLL especial foi usada (MFCANS32.DLL) para converter automaticamente entre Unicode e MBCS quando interfaces OLE foram chamados.Essa DLL foi uma camada quase transparente que aplicativos OLE a ser escrito como se as interfaces e APIs OLE foram MBCS, mesmo que eles são sempre Unicode permitidos (exceto no Macintosh).Enquanto essa camada foi conveniente e aplicativos rapidamente ser portados de Win16 Win32 permitidos (MFC, o Microsoft Word, Microsoft Excel e VBA, são apenas alguns dos aplicativos Microsoft que usou essa tecnologia), tinha um desempenho significativo às vezes visitas.Por esse motivo, o MFC 4. x não usa essa DLL e em vez disso, conversa diretamente com as interfaces OLE do Unicode.Para fazer isso, MFC precisa converter para Unicode em MBCS ao fazer uma chamada para uma interface OLE e geralmente precisa converter MBCS de Unicode ao implementar uma interface OLE.Para lidar com isso com eficiência e facilidade, um número de macros foram criado para facilitar essa conversão.
Um dos maiores obstáculos da criação como um conjunto de macros é a alocação de memória.Porque as seqüências de caracteres não podem ser convertidas no lugar, a nova memória para armazenar os resultados convertidos deve ser alocada.Poderia ter sido feito com o código semelhante ao seguinte:
// we want to convert an MBCS string in lpszA
int nLen = MultiByteToWideChar(CP_ACP, 0,lpszA, -1, NULL, NULL);
LPWSTR lpszW = new WCHAR[nLen];
MultiByteToWideChar(CP_ACP, 0,
lpszA, -1, lpszW, nLen);
// use it to call OLE here
pI->SomeFunctionThatNeedsUnicode(lpszW);
// free the string
delete[] lpszW;
Essa abordagem como um número de problemas.O principal problema é que ele é muito código para escrever, testar e depurar.Algo que foi chamada uma função simples, é muito mais complexa.Além disso, há um tempo de execução significativo sobrecarga ao fazer isso.Tem memória alocada no heap e liberado toda vez que uma conversão é feita.Finalmente, o código acima precisaria ter apropriado #ifdefs adicionado para versões Unicode e Macintosh (que não necessitam desta conversão ocorra).
A solução que criamos é criar algumas macros que máscara 1) a diferença entre várias plataformas e 2) usar um esquema de alocação de memória eficiente e 3) são fácil de inserir o código-fonte.Aqui está um exemplo de uma das definições:
#define A2W(lpa) (\
((LPCSTR)lpa == NULL) ? NULL : (\
_convert = (strnlen(lpa)+1),\
AfxA2WHelper((LPWSTR) alloca(_convert*2),
lpa, _convert)\
)\
)
Usando esta macro em vez de código acima e as coisas são muito mais simples:
// use it to call OLE here
USES_CONVERSION;
pI->SomeFunctionThatNeedsUnicode(T2OLE(lpszA));
Existem chamadas extras onde conversão é necessária, mas usar as macros é simples e eficaz.
A implementação de cada macro usa a função _alloca() para alocar memória de pilha em vez da pilha.Alocação de memória da pilha é muito mais rápido que a alocação de memória no heap e a memória é liberada automaticamente quando a função é finalizada.Além disso, as macros evitar chamada MultiByteToWideChar (ou WideCharToMultiByte) mais de uma vez.Isso é feito por alocar um pouco mais memória do que é necessário.Sabemos que um MBC converterá no máximo uma WCHAR e que para cada WCHAR temos um máximo de dois bytes MBC.Alocando um pouco mais que necessário, mas sempre suficiente para manipular a conversão a segunda chamada segunda chamada para a função de conversão é evitada.A chamada para a função auxiliar AfxA2Whelper reduz o número de verificações extras de argumento deve ser feito para executar a conversão (isso resulta em código de menor, que se chamado MultiByteToWideChar diretamente).
Em ordem para as macros tem espaço para armazenar o comprimento de temporário, é necessário declarar uma variável local chamada _convert que faz isso em cada função que usa as macros de conversão.Isso é feito chamando o USES_CONVERSION como visto acima, o exemplo de macro.
Existem macros genérico de conversão e macros específicas de OLE.Esses dois conjuntos diferentes de macro são discutidos abaixo.Todas as macros residem em AFXPRIV.H.
Macros de conversão genérico
As macros de conversão genérico formam o mecanismo subjacente.O exemplo de macro e a implementação mostrada na seção anterior, A2W, é um tal macro "genérica".Ele não está relacionado ao OLE especificamente.O conjunto de macros genéricos abaixo:
A2CW (LPCSTR) -> (LPCWSTR)
A2W (LPCSTR) -> (LPWSTR)
W2CA (LPCWSTR) -> (LPCSTR)
W2A (LPCWSTR) -> (LPSTR)
Além de fazer conversões de texto, também existem macros e funções auxiliares para conversão de TEXTMETRIC, DEVMODE, BSTRe alocados seqüências OLE.Essas macros estão além do escopo desta discussão – consulte AFXPRIV.H para obter mais informações sobre essas macros.
Macros de conversão de OLE
As macros de conversão de OLE são projetadas especificamente para lidar com funções que esperam OLESTR caracteres.Se você examinar os cabeçalhos OLE, você verá muitas referências a LPCOLESTR e OLECHAR.Esses tipos são usados para se referir ao tipo de caracteres usados em interfaces OLE de uma forma que não é específico para a plataforma.OLECHAR mapeia para char em plataformas Win16 e Macintosh e WCHAR no Win32.
Para manter o número de # ifdef diretivas no MFC código mínimo temos uma macro similar para cada conversão que onde as seqüências OLE estão envolvidas.As seguintes macros são as mais usadas:
T2COLE (LPCTSTR) -> (LPCOLESTR)
T2OLE (LPCTSTR) -> (LPOLESTR)
OLE2CT (LPCOLESTR) -> (LPCTSTR)
OLE2T (LPCOLESTR) -> (LPCSTR)
Novamente, existem macros similares para fazer TEXTMETRIC, DEVMODE, BSTRe alocados seqüências OLE.Consulte AFXPRIV.H para obter mais informações.
Outras Considerações
Não use as macros em um loop estreito.Por exemplo, você não deseja gravar o seguinte tipo de código:
void BadIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
for (int ii = 0; ii < 10000; ii++)
pI->SomeMethod(ii, T2COLE(lpsz));
}
O código acima pode resultar na alocação megabytes de memória na pilha dependendo do que o conteúdo da seqüência de caracteres lpsz é!Também leva tempo para converter a cadeia de caracteres para cada iteração do loop.Em vez disso, mova tais conversões constantes do loop:
void MuchBetterIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
LPCOLESTR lpszT = T2COLE(lpsz);
for (int ii = 0; ii < 10000; ii++)
pI->SomeMethod(ii, lpszT);
}
Se a seqüência não é constante, encapsule a chamada de método em uma função.Isso permitirá que o buffer de conversão para ser liberada a cada vez.Por exemplo:
void CallSomeMethod(int ii, LPCTSTR lpsz)
{
USES_CONVERSION;
pI->SomeMethod(ii, T2COLE(lpsz));
}
void MuchBetterIterateCode2(LPCTSTR* lpszArray)
{
for (int ii = 0; ii < 10000; ii++)
CallSomeMethod(ii, lpszArray[ii]);
}
Nunca retorne o resultado de uma das macros, a menos que o valor de retorno implica fazer uma cópia dos dados antes do retorno.Por exemplo, esse código é ruim:
LPTSTR BadConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // bad! returning alloca memory
}
O código acima pode ser corrigido alterando o valor de retorno para algo que copia o valor:
CString BetterConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // CString makes copy
}
As macros são fáceis de usar e fácil de inserir em seu código, mas como você pode dizer sobre as advertências acima, você precisa ter cuidado ao usá-los.