Rodrigo Strauss :: Blog
FAQ: Programação Win32 em C/C++
Continuando o meu projeto de projeto de FAQ C++, vou falar hoje sobre Win32.
O que é Win32 e para que serve?
Já expliquei isso em outro post.
Ouvi falar que Win32 é complicado, isso é verdade?
Isso tem lá seu fundo de verdade, alguns assuntos são extremamente complicados, os mais usuais nem tanto. De qualquer forma, essa complicação tem motivo: como a Win32 API é o acesso mais baixo nível que se pode ter ao sistema (considerando user-mode), ela deve possibilitar que você a use para fazer tudo que é possível no Windows. Por isso, muitas vezes, você chama uma função de 8 parâmetros mais só passa 2. Esses outros 6 parâmetros são usados em situações não muito comuns, mas que devem ser cobertas pela API. Além disso, como essa API é C (e não C++), não é possível fazer overload das funções com versões mais simples para os usos mais simples, como é feito muitas vezes nas classes do .NET Framework e em bibliotecas C++.
Vou exemplificar isso usando a famosa função CreateProcess como exemplo. Essa função pede 10 parâmetros, mas em 99% dos casos você só usa o LPTSTR lpCommandLine. Além dos parâmetros que você passa como NULL, existem alguns que você é obrigado a preencher, como o LPSTARTUPINFO lpStartupInfo, e o LPPROCESS_INFORMATION lpProcessInformation. Veja um exemplo adaptado da MSDN:
#include <windows.h>
#include <stdio.h>
void main()
{
STARTUPINFO si;
PROCESS_INFORMATION p;
//
// dica: muitas estruturas no Windows têm uma variável membro chamada
// cb ou cbSize. Esse membro deve ser preenchido com o sizeof()
// da estrutura ANTES de chamar a função. Se você não fizer isso a função
// retornará um erro. Esse é um erro comum de iniciantes (eu apanhei muito...)
//
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&p, sizeof(p));
//
// Abrindo o Bloco de Notas
//
if( !CreateProcess( NULL, // Sem módulo
"C:WINDOWS\\notepad.exe", // linha de comando para o executável
NULL,
NULL,
FALSE, // O processo criado não herdará os handles
0,
NULL,
NULL,
&si, // Ponteiro para STARTUPINFO
&p ) // Ponteiro para PROCESS_INFORMATION
)
{
printf( "Erro em CreateProcess: 0x%X", GetLastError() );
return;
}
//
// O CreateProcess preencheu as estruturas e colocou handles nelas
// como não vamos usar os handles, precisamos fechá-los...
//
CloseHandle(p.hProcess);
CloseHandle(p.hThread);
}
O que eu preciso saber antes de estudar Win32?
Você precisa ter prática em programação C/C++. A API Win32 é exportada como funções C, mas é usada a partir do C++ sem problemas.
Quais livros de Win32 você recomenda?
Primeiramente: eu indico que você leia os livros que vou indicar :-) Eu acho muito mais difícil estudar Win32 com tutoriais e lendo a MSDN. Não acho que exista muito matérial para iniciantes na Internet, e, como eu já disse, vale muito mais a pena comprar um livro e ler do que ficar procurando na Internet.
O primeiro livro que você deve ler é o
Programming Windows do Charles Petzold, considerado a Bíblia definitiva sobre o assunto. Ele já está na quinta edição, e é essencial para que você entenda como a GDI do Windows funciona. Ele é hardcore do meio pra frente (eu não tive paciência para ler a parte de toolbar), se você não tiver paciência leia até o capítulo 11 (página 566). Isso já será suficiente para entender como as coisas funcionam no Windows, e será muito útil MESMO QUE VOCÊ USE Windows Forms (afinal, WinForms nada mais é do que uma camada .NET sobre a GDI).
Nossa, Win32 é complicado mesmo... 100 linhas para fazer uma janelinha branca e feia? Existe algo mais fácil?
Existe. Hoje em dia, quase ninguém mais usa API diretamente ("à la Petzold" como é conhecido). A GDI é complexa pelo mesmo motivo que eu já expliquei, ela é muito flexível e é um dos motivos do sucesso do Windows. Poder e flexibilidade sempre têm um custo, que geralmente é a complexibilidade.
Para resolver isso, foram criadas, por diversas empresas, bibliotecas que encapsulam a Win32 API para facilitar o seu uso (o .NET Framework é uma delas). A biblioteca provida pelo Microsoft Visual C++ é a MFC (Microsoft Foundation Classes), que encapsula uma boa parte da Win32 API em objetos C++. Além disso, a IDE do Visual C++ possui diversos wizards que geram classes e código e facilitam muito a programação. Usar dialogs (forms) em MFC é quase tão fácil quanto VB6. Existem outras bibliotecas, como a VCL/CLX do Delphi e do C++ Builder e o WTL da Microsoft (que é a que eu mais gosto).
Leia algo sobre MFC depois de ler o Petzold. Pode ser qualquer livro, entendendo como funciona o Win32 e a GDI MFC não será um problema.
O livro do Petzold cobre extensivamente a GDI (gráficos). E o resto?
Depois de ler o Petzold e algo sobre MFC, leia o Advanced Windows do Jeffrey Richter, outro clássico. Pronto. Isso já é suficiente para você ser um programador Win32. Só 3 livros :-)
Meus amigos dizem que a API Win32 está ultrapassada e não vale a pena estudar isso. É verdade?
Não, não é. Você anda convivendo demais com Piranhas.NET. Explique para os seus amigos que TODOS os aplicativos para Windows hoje são feitos em Win32, já que ela é o Windows. No Windows Longhorn (previsto para 2006/2007) ALGUMAS NOVAS APIs serão disponibilidas como classes .NET. O Windows vai ficar cada vez mais .NET, mas isso deve levar no mínimo uns 5 anos. Não se esqueça que o .NET foi lançado em 2001 e a maioria dos aplicativos comerciais ainda são feitos em C e C++, usando a API Win32 (que por enquanto é a API do Windows)
Meus amigos disseram que existe mais material de estudos e livros falando de .NET do que de Win32. É verdade?
Não é verdade que existem mais livros sobre .NET. A API Win32 existe desde de 1993, e uma procura por livros na Amazon retornará vários livros. Procure "MFC" na Amazon, depois procure "Windows Forms" e veja a diferença. Além disso, no Code Project tem muito mais coisa útil em MFC do que em .NET.
Você me deixou em dúvida. Se o .NET é tão ruim assim, então eu devo abandoná-lo e fazer tudo em C++?
Primeiro: Eu nunca disse que o .NET é ruim. Na verdade eu acho ele muito bom para resolver os problemas que ele se propõe. Mas, tem seus problemas como qualquer API/Framework/Arquitetura. O grande problema é que no Brasil as pessoas acreditam no pessoal de marketing da Microsoft sem pensar se isso resolve o problema, e acham que .NET resolve todos os problemas. Nada resolve todos os problemas.
Como eu já disse, hoje tudo é C++ e Win32 e vai levar anos (talvez mais de uma década) para que o .NET seja realmente a plataforma de programação do Windows. Você acha mesmo que a Microsoft, Adobe/Macromedia e outros vão refazer todos os aplicativos em C#? Além disso, C++ ainda é a coisa mais portável que existe (em termos de código fonte), e esse é o motivo dele ter sido usado pela Mozilla Foundation (Firefox, Thunderbird, etc), pela Adobe (Photoshop), etc. E se isso conta, em Linux quase tudo é C e C++, o kernel (C), Gnome (C), KDE, (C++), etc. O fator multiplataforma do .NET ainda é incerto, e depende de implementações de terceiros (como o Mono).
Eu já escrevi um artigo sobre perspectivas para o NET a quase 1 ano atrás, e acho que ela continua a mesma. As duas arquiteturas são importantes, não troque o .NET pelo Win32, aprenda os dois e use o que for melhor para cada caso. Só não fique sentado esperando o .NET virar a plataforma oficial do Windows, você vai perder vários anos de mercado e da sua vida.Em 11/07/2005 14:37, por Rodrigo Strauss





Excelente artigo (assim como os outros) :)
Bom, para contribuir com a galera do C++ que está interessada em Win32 (ou vice versa) - "alguma coisa Win32 com cara/feeling de C++":
class Process
{
public:
struct CannotBeCreatedException
{
CannotBeCreatedException( const string& reason ) : reason(reason){}
string reason;
};
private:
struct ReasonFunctor
{
~ReasonFunctor(){ LocalFree( Buffer_ ); }
string operator()()
{
if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<char*>( &Buffer_ ),
0,
NULL )
)
{
return static_cast<char*>( Buffer_ );
}
else
{
return "Unknown";
}
}
private:
LPVOID Buffer_;
};
struct STARTUPINFO_ : public STARTUPINFO
{
STARTUPINFO_()
{
ZeroMemory(this, sizeof(STARTUPINFO_));
this->cb = sizeof(STARTUPINFO_);
}
LPSTARTUPINFO ptr()
{
return this;
}
};
struct PROCESS_INFORMATION_ : public PROCESS_INFORMATION
{
PROCESS_INFORMATION_()
{
ZeroMemory( this, sizeof(PROCESS_INFORMATION_) );
}
~PROCESS_INFORMATION_()
{
if( INVALID_HANDLE_VALUE != this->hProcess )
{
CloseHandle( this->hProcess );
if( INVALID_HANDLE_VALUE != this->hThread )
CloseHandle( this->hThread );
}
}
LPPROCESS_INFORMATION ptr()
{
return this;
}
};
public:
static void Start( const string& processName ) throw(...)
{
STARTUPINFO_ si;
PROCESS_INFORMATION_ p;
if( !CreateProcess( NULL,
const_cast<char*>( processName.c_str() ),
NULL,
NULL,
false,
0,
NULL,
NULL,
si.ptr(),
p.ptr() )
)
{
throw CannotBeCreatedException( ReasonFunctor()() );
}
}
};
para usar:
try
{
Process::Start( "C:\\Windows\\Notepad.exe" );
}
catch( Process::CannotBeCreatedException ex )
{
MessageBox( NULL, ex.reason.c_str(), "", 0 );
}
Falou ;)
Qual será o próximo tema? Definitivamente o melhor blog em português que temos no momento... ;)