Você está na página 1de 12

Aplica-se a:

ADO.NET .NET Language Integrated Query (LINQ) SQL Server

Resumo: Eliminar a diferena de impedncia entre aplicaes e servios de dados como emisso de relatrios, anlise e replicao oferecidos como parte do produto SQL Server aumentando a abstrao do nvel lgico (relacional) para o nvel conceitual (entidade). As pessoas que apenas utilizam o software no reconhecem a dificuldade que escrev-lo. O design e o desenvolvimento so as partes divertidas. A parte difcil fazer com que ele seja executado corretamente (e com a rapidez necessria). Para o programador como se uma refeio comeasse com um delicioso cheesecake duplo de caf com creme e terminasse com creme de espinafre. O motivo pelo qual dizemos que a programao uma arte, e no uma cincia ou disciplina da engenharia, porque ainda no conseguimos dividi-la em suas etapas constituintes e mecaniz-la. Aps fazermos isso com xito, uma nova escala de possibilidades surge: programas que escrevem programas de linguagens de projeto orientadas a pessoas (PODL), programas para provar a correo do programa e para analisar e suportar a consulta semntica. At esse ponto, porm, a programao continua sendo uma arte ou pelo menos isso o que a sabedoria convencional diz. Mas assim como acontece com grande parte da sabedoria convencional, essa analogia no resiste a um exame mais detalhado. A criao de um programa (e j fazemos isso h mais de 20 anos) no , na verdade, nada parecido com arte pelo menos no a arte de escrever fico (e tambm j fazemos isso h mais de 20 anos). Vou ilustrar essa questo com estes dois fragmentos de programa e prosa. Este primeiro exemplo parte de um analisador lxico para analisar o Visual C++ 7.1. Ele assim: if ( index >= TokenID: : l a s tKeyword() ) { str i ng token = tokenStr ing ( index ) ; if (( isd ig i t ( token[ 0 ] )) | | ( token[0] == - )) {

#ifdef TELL_ME disp lay( "! ! TokenSet: : tokenIdent i f y : "a l i t e r a l #endif return TokenID: :TK_L i te ra l ; } } Este segundo exemplo a abertura de um conto que escrevi h cerca de 27 anos, quando morava em Tucson, no Arizona, lecionava e escrevia prosa: We were her pride of 10, Miss Steward named us: the Prodigal, Phoenix, dig i t : ",

" , token ) ;

Benjamin, and persp icac ious , paci f i c Suzanne. Hush, chi ld . st i l l , Miss Steward commanded him gent ly . are never just. People

Benjamin, be

Ambos os exemplos tiveram uma criao intensiva, ou seja, eu passei muito tempo antes criando a arquitetura geral do trabalho, e os fragmentos refletem esse esforo de criao. O programa tem uma falha de desempenho eu recupero a entrada de cadeia de caracteres na tabela de cadeia de caracteres associada por valor e no por ponteiro. Se o cdigo precisasse ser produzido, ele exigiria uma reviso bvia. Isso no tem nada a ver com a correo, mas apenas com o desempenho. Para um programa com cdigo fonte pequeno, a diferena de velocidade no mensurvel e, assim, eu preferi a sintaxe de objeto mais limpa e lenta, uma vez que no gosto dos tokens de manipulao de ponteiros da C/C++. Embora minha prosa no tenha atrado milhes de leitores, aqueles que a leram apreciaram a obra com sua trama emocional sob uma rigorosa estrutura simblica. Aqueles que no apreciam a prosa tradicional acharam que ela era enganosa, porque no h nada de funcional nela. A finalidade da arte projetar sentimentos e a realidade de uma vida privada subjetiva em meio aos eventos pblicos em um meio plstico em particular no caso da prosa, as palavras. Obviamente, no h valor nisso, no como o valor intrnseco de um programa. Eu gosto do processo de criao de um programa, e gosto de experimentar o modo como as diferentes partes interagem. Eu gosto de definir abstraes ou famlias de abstraes. Eu as considero meu elenco de personagens. Gosto tambm de criar interfaces, porque representam as coisas que meus personagens fazem. E assim como eu realmente escrevo os programas. Eu no gosto de escrever expresses e declaraes. Eu gostaria que tivssemos uma linguagem de programao simblica na qual eu pensasse melhor e que pudesse memorizar. Honestamente, eu no consigo manter a clareza de expresses e aninhamentos intercalados. Embora eu consiga manter oito sees separadas de texto em minha cabea e as compare e as ecoe, eu no consigo dar sentido a um programa alm das classes que crio e suas interfaces. No excelente artigo de John Markoff sobre a indstria do PC, What the Dormouse Said, ele declara que o objetivo dos visionrios iniciais - colocar um computador disposio de cada pessoa - foi concretizado. Mas ele est errado. Esse objetivo foi concretizado apenas pela metade. O que h de bom em todo esse poder da computao se uma pessoa no pode program-la? Ainda no realizamos seu sonho porque ainda no descobrimos como tornar a programao uma arte para que todos possam compartilh-la. At l, a programao continua no sendo uma disciplina da cincia, nem um domnio da arte. Por enquanto, a programao pouco mais do que uma nova alquimia. Ningum realmente a entende e ningum ainda transformou uma especificao em ouro verdadeiro. Stanley B. Lippman comeou a trabalhar na linguagem C++ junto com seu criador, Bjarne Stroustrup, em1984, na Bell Laboratories. Mais tarde, Stan trabalhou em animao de filmes na Disney e na DreamWorks e atuou como diretor tcnico de software no filme Fantasia 2000. Desde ento, tem trabalhado como consultor condecorado da JPL e arquiteto na equipe do Visual C++ da Microsoft. P Quando gero uma DLL com suporte de automao usando o Visual C++ 6.0, so geradas algumas funes de registro, mas no para cancelar o registro da minha DLL. Escrevi um DllUnregisterServer que tem esta aparncia: STDAPI DllUnregisterServer(void) {

AFX_MANAGE_STATE(AfxGetStat i cModuleState( ) ) ; if (!COleObjectFactory : :Unreg is te rA l l ( ) ) return ResultFromScode(SELFREG_E_CLASS); return NOERROR; } COleObjectFactory::UnregisterAll retorna TRUE, mas minha DLL ainda est registrada. Como devo escrever o cdigo? Ivan Pavlik R Bem, voc esbarrou em uma pequena fenda especial no universo MFC. UnregisterAll parece mesmo ser a funo a ser chamada para cancelar o registro de sua DLL. Se olhar no olefact.cpp, onde o UnregisterAll implementado, voc ver que mais ou menos assim: for (/* pFactory = all factories */) { pFactory->Unregister(); } Isto , ele faz um loop sobre todas as fbricas de seu mdulo e chama o Unregister para cada uma delas. At aqui, tudo bem. O que o COleObjectFactory::Unregister faz? O cdigo revela a histria: BOOL COleObjectFactory::Unregister() { return TRUE; } Opa! Como voc pode ver, o COleObjectFactory::Unregister no faz nada! Ele simplesmente retorna TRUE. Isso muito peculiar, pois o COleObjectFactory::Register registra realmente sua classe COM chamando ::CoRegisterClassObject. Por outro lado, h uma outra funo, COleObjectFactory::Revoke, que chama ::CoRevokeClassObject para cancelar o registro de sua classe COM. MFC chama Revoke automaticamente a partir de seu destruidor de COleObjectFactory. Ento, o que que est acontecendo aqui? O problema uma confuso infeliz na terminologia, que tem origem nos diferentes modos de registrar as classes COM para DLLs e EXEs. Para DLLs, voc registra sua classe adicionando chaves ao registro do Windows (CLSID, ProgID e assim por diante). Para EXEs, voc precisa chamar CoRegisterClassObject para registrar sua classe no sistema COM no tempo de execuo. A questo fica ainda mais confusa pelo fato de que para os EXEs, o oposto de registrar no cancelar o registro, mas revogar (CoRevokeClassObject). Quando as pessoas de Redmond adicionaram o suporte COM ao MFC, elas fizeram o que puderam para facilitar ao mximo a vida das pessoas, mas nem sempre tiveram muito sucesso. COleObjectFactory::Register tem esta aparncia: // in olefact.cpp BOOL COleObjectFactory::Register() {

if

(!a fxContext IsDLL) { : :CoRegis te rC lassObject ( . . . , CLSCTX_LOCAL_SERVER, . . ) ;

} } Voc pode ver de imediato que no faz nada para DLLs; ele s registra EXEs usando CLSCTX_LOCAL_SERVER (contexto = servidor local, EXE em execuo na mquina local). Seguindo a C API bsica, as pessoas de Redmond usaram o mesmo nome Revoke para a funo que cancela o registro do EXE: COleObjectFactory::Revoke (que chamado automaticamente pelo seu destruidor de fbrica de classes). Ento, para o que serve COleObjectFactory::Unregister a funo que no faz nada? Talvez as pessoas de Redmond pretendessem fazer com que um dia Register/Unregister funcionassem tambm para DLLs. Mas, do jeito que est agora, no funcionam. Para registrar seu DLL, voc precisa de uma outra funo completamente diferente: COleObjectFactory::UpdateRegistry. Essa funo usa um argumento booleano que diz ao MFC se voc quer registrar ou cancelar o registro de sua classe COM. H tambm UpdateRegistryAll, que executa um loop sobre todas as fbricas de classes, chamando UpdateRegistry para cada uma. Ento, aqui est o modo adequado para implementar seu DllUnregisterServer:: STDAPI Dl lUnregis te rServer (vo id ) { AFX_MANAGE_STATE(AfxGetStat i cModuleState( ) ) ; return COleObjectFactory : :UpdateRegis t ryA l l ( FALSE) ? S_OK : SELFREG_E_CLASS; } DllRegisterServer deve parecer igual, mas voc deve passar TRUE em vez de FALSE para UpdateRegistryAll. A implementao padro para UpdateRegistry faz o que pode para adicionar ou remover as chaves de registro apropriadas em HKCR/CLSIDInprocServer32, ProgID, Insertable, ThreadingModel e assim por diante, mas, s vezes, voc precisar de outras chaves especficas para sua classe COM. Por exemplo, pode ser preciso registrar categorias, que so um tipo de extenso da antiga chave "Insertable" (o que significa que sua classe COM pode ser ignorada em uma forma no modo de design). Nesse caso, voc precisa ignorar o UpdateRegistry para adicionar ou remover suas prprias chaves. H tempos, nos exemplares de novembro e dezembro de 1999 do Microsoft Systems Journal (agora chamados de MSDNMagazine), mostrei como construir um Band Object para Internet Explorer usando a interface Active Template Library (ATL) IRegistrar. (Band Objects precisam ser registrados como uma categoria especial CATID_DeskBand.) IRegistrar uma ferramenta muito boa que permite que voc escreva um script de registro (arquivo .RGS) para adicionar suas entradas ao registro, em vez de chamar funes de registro como RegOpenKey, RegSetValue e outras mais. A Figura 1 mostra um script tpico.

Como voc pode ver, IRegistrar permite que voc defina variveis como %ClassName% e %ThreadingModel% e depois as substitua com valores reais no tempo de execuo. Com IRegistrar, voc nunca mais ter de chamar a API de registro. Voc pode escrever um script que se parea mais com as entradas reais do registro e pode, inclusive, usar o mesmo script para registrar ou cancelar o registro de sua DLL. Isso mesmo, o IRegistrar inteligente o bastante para cancelar o registro. O IRegistrar no est documentado oficialmente em lugar algum que eu pudesse encontrar, mas o cdigo est aqui (encontre-o em atliface.h). Se ainda no est usando o IRegistrar para registrar e cancelar o registro de suas COM DLLs, voc deveria mesmo experimentar isso vai poupar-lhe muito trabalho. Para obter detalhes consulte meu artigo no exemplar de novembro de 1999 . P Meu aplicativo principal tem um menu normal com um submenu de comandos Editar (Recortar, Copiar, Colar etc.). Gostaria de exibir o submenu Editar como um menu de contexto quando o usurio clica com o boto direito na janela principal. O problema como obter esse submenu a partir do menu principal. Parece que no existe nenhuma ID de comando associada a um submenu. Posso usar o ndice comeando em zero, mas devido personalizao o menu Editar nem sempre ser o segundo menu. No posso procurar o texto "Editar" porque damos suporte a mltiplos idiomas e a palavra verdadeira pode mudar. Como posso localizar o submenu Editar em todos esses casos? Brian Manlin R Bem, deixe-me salientar que o menu Editar deveria ser o segundo submenu, se voc tiver um. As diretrizes de GUI do Official Windows necessitam que os trs primeiros itens de menu sejam Arquivo, Editar, Exibir, nessa ordem para receberem suporte. Consulte Windows Interface Guidelines for Software Design (em ingls) (Microsoft Press, 1995). Dito isso, posso ajud-lo a encontrar seu submenu Editar ou qualquer outro submenu desejado. Voc est correto: no existe ID de comando para um submenu. Isso acontece porque internamente o Windows usa o campo de ID de comando para armazenar o HMENU de submenu, se o item de menu um submenu. No se preocupe, supondo que o submenu sempre mantenha um comando especfico (por exemplo Editar | Recortar) e supondo que o comando sempre tenha a mesma ID (por exemplo, ID_EDIT_CUT), bem fcil escrever uma funo que localize o submenu que contm um determinado comando. A Figura 2 mostra o cdigo. CSubmenuFinder atua de fato como um namespace para armazenar a funo esttica FindCommandID, que chama a si mesma recursivamente para uma busca detalhada no menu e em todos os submenus por um item cuja ID de comando corresponda procura. Esse segmento busca no menu principal um submenu que contenha um item de menu cuja ID de comando seja ID_EDIT_CUT e retorna o submenu, se localizado. Escrevi um pequeno programa denominado EdMenu para testar o CSubmenuFinder. O EdMenu usa o cdigo anterior para manipular WM_CONTEXTMENU de modo a exibir um submenu editar exatamente igual ao do menu principal, quando o usurio clica com o boto direito na janela principal. Acredite em mim quando digo que o CSubmenuFinder localiza o menu editar onde quer que ele esteja dentro do menu principal. Na verdade, eu o testei quando mudei temporariamente o menu Editar para adiante do menu Exibir em uma de minhas criaes. Experimente voc mesmo, faa download da fonte. P Como converto MFC CString em String em C++ gerenciado? Por exemplo, tenho o seguinte cdigo em C++: void GetStr ing (CStr ing& msg) { msg = / / bui ld a str ing

} Como posso reescrever essa funo usando C++ gerenciado e substituindo o parmetro CString por uma String gerenciada? O problema que GetString muda a CString do chamador e eu quero fazer a mesma coisa usando uma String gerenciada. Sumit Prakash R Bem, a resposta depende de voc estar usando a nova sintaxe C++/CLI ou as antigas extenses gerenciadas. Os dois tipos de sintaxe podem ser confusos, mas no tenha medo, estou aqui para resolver sua confuso de cdigos. Vamos comear pela nova sintaxe, pois estamos em 2006. O truque para acertar a sintaxe lembrar de duas coisas. Primeiro, em C++/CLI, os objetos gerenciados usam chapus. Assim, CString torna-se String^. Segundo, lembre-se de que o equivalente gerenciado referncia (&) a referncia de rastreamento, que em C++/CLI %. Sei, voc mal pode enfiar ^ em seu crebro, mas, com alguma prtica, % tambm logo vai parecer normal. Ento, as novas funes ficam assim: / / using C++/CLI void GetStr ing (S t r i ng^% msg) { msg = / / bui ld a str ing } Isso faz sentido? Para mostrar que funciona de fato, escrevi um pequeno programa strnet.cpp (veja a Figura 3). Ele implementa duas classes, CFoo1 e CFoo2, cada uma com um membro GetName. A primeira usa CString& e a segunda usa String^%. Se voc compilar e executar esse programa (usando /clr claro), ver que nos dois casos o que usa CString e o que usa String o objeto passado (nativo ou gerenciado) modificado pela funo de membro. Por falar nisso, sempre use uma referncia de rastreamento (%) para objetos gerenciados, em vez de usar uma referncia nativa (&, que tambm far compilao) porque a referncia de rastreamento rastrear a referncia mesmo se o coletor de lixo mover o objeto dentro de sua pilha gerenciada. Se voc est usando a sintaxe no estilo antigo (/clr:oldSyntax), como pode ainda viver na Idade Mdia? Nada tema, voc pode usar uma referncia mesmo que seja antiga. Se voc est usando extenses gerenciadas, lembre-se apenas de que os objetos gerenciados so __gc pointers e assim CString transforma-se em String __gc *. E, como o compilador j sabe que String um tipo gerenciado, voc nem vai precisar do __gc; um ponteiro simples (*) bastar. A referncia a mesma. Ento, a converso da sintaxe de estilo antigo fica assim: / / __gc omitted void GetStr ing (S t r i ng*& msg) { msg = / / bui ld a str ing } Entendeu? Faz todo o sentido. Sempre que tiver dificuldade em descobrir a sintaxe C++, a soluo ser voltar ao bsico.

P Recentemente gerei um aplicativo de console CLR com o Visual Studio 2005. Reparei que o Visual Studio criou uma funo principal como esta: in t main(ar ray<System::St r ing ^> ^args) { ... return 0; } Isso parece ser uma mudana da antiga argc/argv com que estou familiarizado no C/C++. Quando tentei acessar args[0], pensando que seria o nome do arquivo (como em C/C++), descobri que args[0] no o nome do arquivo, mas o primeiro parmetro da linha de comando. O que aconteceu com o nome do arquivo? Pode me explicar a razo desta mudana? Jason Landrew R Ah, o mundo moderno em que nos divertimos! Nada igual ao que era, no verdade? Vivemos com argc/argv desde que a linguagem C foi desenvolvida em 1972 e agora as pessoas de Redmond vieram e mudaram tudo. Talvez eles quisessem ter certeza de que todos saberiam como usar a nova matriz de sintaxe. Uma razo para a nova sintaxe gerar um programa que compile com /clr:safe, que a ltima palavra em segurana de cdigo. como trancar seu programa dentro de uma sala limpa um daqueles ambientes estreis cheios de equipamentos de cromo, em que preciso entrar atravs de uma cmara de vcuo de presso negativa, usando capacete e botas especiais. Quando voc aciona o comutador /clr:safe, o compilador impe todos os tipos de restries trabalhosas. No possvel usar tipos nativos. No possvel usar globais. Voc no pode chamar funes no gerenciadas. Em resumo, voc no pode fazer nada divertido. E por que voc se sujeitaria a tanto sofrimento? Para ter certeza total e absoluta de que o cdigo est seguro. Em termos tcnicos, /clr:safe gera cdigo "verificvel", o que significa que o CLR (Common Language Runtime) pode verificar que seu cdigo no viola configuraes de segurana, tais como tentar acessar um arquivo quando as configuraes de segurana do usurio no permitem. Um dos muitos itens da lista proibida do /clr:safe ponteiros nativos ou seja, ponteiros. ("Nativo" redundante, pois no se pode ter um ponteiro de tipo gerenciado.) Como os ponteiros no so permitidos com o /clr:safe, a antiga declarao argv simplesmente no funciona. Assim, as pessoas de Redmond mudaram a declarao para usar uma matriz String^. E como as matrizes conhecem seu comprimento, no h necessidade de argc. A biblioteca de tempo de execuo proporciona o cdigo de inicializao apropriado para criar e inicializar a matriz de arg antes de chamar sua funo principal. Se voc no planeja usar /clr:safe, sempre possvel escrever um principal que use a antiga assinatura argc/argv . Isso explica o motivo de se usar uma matriz gerenciada, mas e o args[0], por que ele o parmetro do primeiro comando e no o nome do arquivo? No posso falar pelas pessoas de Redmond, mas talvez tenham achado que faria mais sentido omitir o nome do arquivo, talvez pensassem que voc no precisaria disso. De qualquer modo, no importa muito porque fcil obter o nome do arquivo. Tenho certeza de que existem muitos modos de fazer isso, mas o mais bvio e centrado em CLR (compatvel com /clr:safe) em que posso pensar usar a classe de System::Environment. Ela apresenta o seguinte mtodo: stat i c array<Str ing^>^ GetCommandLineArgs ()

Ao contrrio do parmetro args fornecido para o mtodo principal, a matriz retornada por Environment::GetCommandLineArgs na verdade comea com o nome do arquivo executvel. Finalmente, por que voc desejaria saber o nome de seu prprio arquivo EXE? Afinal de contas, se voc est escrevendo o programa, supostamente sabe como ele se chama. Um motivo seria gerar uma mensagem de ajuda que contenha o nome do programa, sem codific-lo. A mensagem de ajuda poderia ser mais ou menos assim: FooFi le - - Turns every word in your f i l e to "foo" usage: FooFi le [ /n :<number>] f i l e spec f i l e spec = the f i l e s you want to change <number> = change only the f i r s t <number> occurrences

Aqui, FooFile o nome do programa. Eu poderia simplesmente escrever "FooFile" em meu texto de ajuda, mas aprendi h muito tempo que muitas vezes renomeio programas ou copio cdigos de um programa para outro. Obtendo o nome do programa a partir do prprio programa (por meio de argv ou Environment::GetCommandLineArgs), minhas mensagens de ajuda sempre exibem o nome correto, mesmo que eu renomeie o arquivo ou copie meu cdigo em outro programa. Boa programao! Envie perguntas e comentrios para Paul no endereo: cppqa@microsoft.com. Paul DiLascia um consultor de software independente e um designer de interfaces de usurio e aplicativos Web. Ele o autor de Windows++: Writing Reusable Windows Code in C++ (AddisonWesley, 1992). Em seu tempo livre, Paul desenvolve a PixieLib, uma biblioteca de classes do MFC disponvel em seu site, http://www.dilascia.com/.

C++ foi a linguagem contemplada com a maior quantidade de modificaes no Visual Studio .NET 2003, a prxima verso do Visual Studio .NET. As linguagens mais recomendadas para o desenvolvimento na plataforma .NET so sem dvida o Visual Basic e o C#. Isto fica claro ao se constatar que tanto a documentao como os exemplos so sempre fornecidos nas das linguagens e, em muitos casos, apenas elas. Efetivamente, o C# e o VB so produtivos e fceis de aprender e usar. Como fica ento o C++? O C++ passa a ser oficialmente considerada uma linguagem mais de "baixo nvel" - se que voc j no achava isto antes. Dentro desta filosofia, o C++ do Visual Studio .NET possui alguns um recurso nico: a nica linguagem capaz de gerar cdigo "no-gerenciado", ou seja, cdigo Win32 como era desenvolvido antes da plataforma .NET. Isto continua sendo necessrio, notavelmente na criao de "device-drivers". Alm dito, possvel criar executveis contendo cdigo gerenciado e no-gerenciado, algo interessante na criao de programas que devem interagir com os dois mundos, como as prprias ferramentas de desenvolvimento. O Visual Studio 2003 traz algumas novidades a mais na linguagem C++: agora possvel criar aplicativos RAD e tambm gerar "cdigo verificvel". Aplicativos RAD

O paradigma de programao RAD foi introduzido pelo Visual Basic em 1991 e amplamente copiado por vrias outras ferramentas de desenvolvimento. Neste paradigma, colocamos "componentes" sobre formulrios, ajustamos propriedades dos componentes e interceptamos eventos dos componentes em nosso cdigo. Infelizmente o Visual Studio .NET original no trazia suporte a desenvolvimento RAD na linguagem C++. Com o Visual Studio .NET 2003 possvel criar aplicativos "RAD" em C++. Podemos criar tanto "Windows Forms Applications" como "Windows Control Libraries":

Veja a janela principal do projeto C++. Observe que podemos alterar propriedades na janela da direita:

Observe que os componentes possuem diversos eventos, como no caso do Button1 mostrado a seguir:

Os eventos mostrados acima so eventos dos prprios componentes da biblioteca .NET Framework. Veja o cdigo para processar o evento Click do boto:

Cdigo Verificvel Todo programa criado pelo compilador VB.NET e tambm a maioria dos programas criados pelo compilador C# dito "verificvel". Isto quer dizer que o compilador JIT pode, em tempo de execuo/compilao verificar e garantir que o programa no faa nenhuma operao que possa comprometer a segurana e integridade do sistema. Pode parecer estranho, mas existem instrues MSIL capazes de abrir brechas na segurana do sistema, como por exemplo, para manuseio direto de ponteiros ou manuseio da pilha da CPU. Estas instrues so necessrias em alguns casos, como por exemplo para que a prpria biblioteca chame a API do Windows. Programas que contem estas instrues so ditos "no-verificveis". Evidentemente necessrio um privilgio especial de segurana para rodar programas no-verificveis. O compilador C++ do Visual Studio 2002 sempre gera cdigo no-verificvel, o que sem dvida uma deficincia. J o Visual Studio 2003 pode gerar cdigo verificvel, desde que se faam alguns ajustes de compilao. Criar cdigo verificvel exige uma seqncia de ajustes, documentada sob o tpico "Producing Verifiable Components with Managed Extensions for C++":

Concluso O C++ foi significativamente melhorado no Visual Studio .NET 2003 e com certeza agradar os seus entusiastas, tanto com relao ao uso de recursos RAD como criao de cdigo verificvel.

Você também pode gostar