Ponto V!

Home C/C++ Visual C++ Entendendo a Execução de Programas
Bruno Crivelari Sanches
Entendendo a Execução de ProgramasImprimir
Escrito por Bruno Crivelari Sanches

No artigo de hoje vamos entender melhor como funciona a execução de um programa e como o sistema operacional lida com isso, em especial, como isso afeta o uso de arquivos e biliotecas dinâmicas (DLLs) e por fim, vamos ver as funcionalidades do Visual C++ para execução dos programas que criamos, funcionalidades estas que nos ajudam a organizar melhor os assets do jogo, DLLs necessárias e outros detalhes.

Para quem não conhece o Visual C++, temos uma série que explica o básico desde a instalação até o uso, começando pelo artigo Como usar o Visual Studio C++ – Parte 1, para quem já usa o Visual, temos um artigo mostrando como usar o depurador: Como usar o Depurador do Visual C++. Se você já sabe depurar, talvez já queira usar novas bibliotecas com seus programas, para isso temos o artigo: Configurando o uso de Bibliotecas no Visual Studio 2010, além deste, se você pretende usar a Boost ou não conhece ela, não perca tempo e aprenda como instalar usando nosso guia Compilando a Boost com Visual C++ – Versão 2010.

Por fim, hoje vamos ver a melhor forma de configurar o Visual C++ para execução de seus programas, mas primeiro, vamos rever um pouco o básico:

Execução de um Programa

Para melhor controlar a execução de um programa, primeiro é preciso entender como funciona a execução de um programa. Não vou entrar em detalhes de como o sistema operacional gerencia o processo ou a execução deste (basta consultar um livro de sistemas operacionais para ver detalhes), mas vamos rever o que mais impacta a execução e o dia a dia do programador de jogos.

A execução óbviamente começa mesmo pelo arquivo executável do seu programa, no caso do Windows, os arquivos com extensão exe. Para executar algo, você solicita ao Windows que inicie a execução do seu programa, você pode fazer isso com um duplo clique no arquivo usando o explorer, via linha de comando, usando atalhos ou quando esta programando, pedir ao Visual que faça isso, através do menu Debug –> Start without Debugging ou simplesmente pressionando CTRL + F5 de dentro do Visual C++.

Não importa o meio que você usou, de alguma forma vai chegar um pedido ao sistema operacional para que um determinado arquivo seja executado, o Windows vai então carregar o seu arquivo executável na memória (caso isso ainda não tenha sido feito), criar uma região de memória para seu novo processo, criar um processo e jogar ele na fila de execução e eventualmente seu novo processo vai para a CPU e vai ser executado.

Nesse processo todo, existem duas coisas realmente relevantes para um desenvolvedor:

  • Diretório de trabalho
  • Bibliotecas Dinâmicas (DLLs)

Vamos ver detalhes de cada um a seguir.

Diretório de Trabalho

Todo programa ao ser executado, possui um diretório de trabalho, que para o programa é o ponto no sistema de arquivos de onde ele esta executando. Isso não necessariamente reflete o local onde esta o arquivo executável. Por exemplo, supondo que tenhamos um programa no diretório c:\temp\checkdir\checkdir.exe (este programa pode ser baixado no final do artigo).

Indo para o console do Windows e digitando os comandos:

Executando o programa a partir do seu diretório

Observe que o programa informou que seu diretório atual é “c:\temp\CheckDir”. Se ao invés dos comandos acima, usarmos:

Mudando o diretório do programa

Note que com os comandos acima, ao invés de executarmos o programa a partir do seu diretório, executamos ele a partir de outro diretório e o mais relevante, é que o diretório atual dele mudou, ou seja, para o programa, é como se ele estivesse rodando a partir de c:\ e de fato ele esta, no segundo caso.

Mas então vem a pergunta, e dai? A questão é que toda vez que seu programa tentar acessar um arquivo usando caminhos relativos, esses caminhos vão ser relativos ao diretório de execução do programa. Por não saberem, muitos iniciantes enviam questões como:

  • Levei o jogo que fiz para a casa do meu amigo e no computador dele não funciona pois fala que não achou as imagens.
  • Quando executo meu jogo fora da ide ele não funciona
  • etc

Por exemplo, supondo o código abaixo:

void CarregaImagens()
{
    imagem1 = fopen("sprite.bmp", "rb");
    imagem2 = fopen("fundos/fundo.bmp", "rb");
}

O pseudo código acima, tenta abrir dois arquivos, “sprite.bmp” e “fundo.bmp”. Esses nomes na verdade são caminhos relativos, pois não dizemos ao sistema operacional onde estes arquivos estão exatamente, apenas dizemos que queremos eles. Como o sistema opercional não é mágico e nem vem bom bola de cristal embutida, eles simplesmente usa o seu diretório de trabalho como ponto de partida. Se executarmos esse programa a partir de c:\games\MeuJogo\, o sistema vai então tentar abrir os arquivos “c:\games\MeuJogo\sprite.bmp” e “c:\games\MeuJogo\fundos\fundos.bmp”. Por isso saber exatamente onde seu programa esta executando é tão relevante.

Alguns podem dizer: “mas basta então, nas operações de arquivo, usar o caminho completo, mudando o código para (uma pessima solução):

void CarregaImagens()
{
    imagem1 = fopen("c:\\games\\MeuJogo\\sprite.bmp", "rb");
    imagem2 = fopen("c:\\games\\MeuJogo\\fundos\\fundo.bmp", "rb");
}

Pronto, agora seu jogo pode ser executado em qualquer lugar que vai sempre funcionar, desde que os arquivos estejam exatamente naqueles locais. Bom, para aqueles que usam essa solução, eu sinceramente desejo uma morte lenta e dolorosa, ops, melhor dizendo: essa não é uma atitude muito profissional, pois seus futuros usuários vão precisar sempre instalar o jogo no exato mesmo local e coitado daqueles que nem tiverem um drive c:\.

Então resumindo:

  • Sempre utilize caminhos relativos quando for lidar com arquivos em programas
  • Sempre tenha consciência de onde seu programa vai ser executado

Agora que já sabemos a importãncia do diretório de trabalho e como ele afeta nosso programa, vamos ver o que ocorre com as bibliotecas dinâmicas ou DLLs.

Bibliotecas Dinâmicas

Bibliotecas dinâmicas são as famosas DLLs que encontramos no Windows ou arquivos .so (Shared Object) que temos em sistemas Linux.

Bibliotecas dinâmicas são programas como qualquer outro, mas não possuem a capacidade de serem executadas por si só, precisam ser carregadas e executadas por um outro programa. Não cabe aqui discutir vantagens, desvantagens, o porque, etc de se usar bibliotecas dinâmicas, mas o fato é que muitas bibliotecas ou motores para quem programa em C/C++ hoje são compiladas em forma de DLLs, então é preciso ter um mínimo de conhecimento de como o sistema as utiliza, lembre-se, antes de programar, é preciso ser um bom usuário.

Já vimos aqui que quando pedimos ao sistema operacional que execute um programa, ele carrega o código executável deste na memória, faz uns ajustes aqui e ali e invoca a função principal do programa (para detalhes, consulte seu livro sobre sistemas operacionais favorito). Mas, antes de invocar a função principal de seu programa, o sistema precisa ter certeza que todo o código que ele precisa esta disponível, para isso, o compilador quando gera o executável inclui uma lista de todas as DLLs que este executável “usa” ou que foram ligadas de forma estática. Sim, a DLL é dinâmica, mas a ligação é estática, pois no executável já vem registrado quais DLLs o programa usa (mais a frente falaremos de ligação dinâmica).

Com a lista de DLLs o loader (componente do sistema operacional responsável por carregar os executáveis) vai então verificar se cada uma dessas DLLs já se encontram na memória, caso positivo, já esta quase tudo pronto e basta começar a execução (os detalhes sórdidos do “quase pronto”, cheque no livro de você sabe o que). Se alguma DLL estiver faltando, loader vai então procura-lá no sistema de arquivos e quando não encontra, temos a mensagem que é tão comum nos fóruns:

image

Isso ocorre porque o loader não achou a DLL, sem ela é impossível continuar a execução e esta é abortada. Você pode dizer: “mas como não, ela esta em c:\meusarquivos\minhaslibs\minhalibestranha\bin\MinhaDll.dll”. Novamente, o sistema não tem bola de cristal e ele não vai vasculhar todo o sistema de arquivos procurando uma DLL, não só porque isso demoraria muito, mas também porque ele em várias situações iria encontrar dlls com o mesmo nome e não saberia qual usar.

O fato é que o loader procura no sistema de arquivos pela DLL, mas apenas em alguns lugares e numa ordem especifica:

  1. O diretório onde o arquivo executável se encontra
  2. O diretório de execução
  3. O diretório System do Windows
  4. O diretório do Windows
  5. Os diretórios listados na variável de ambiente PATH.

Se sua DLL não estiver em nenhum desses locais, o loader não consegue encontra-la e aborta a execução

Por fim, no caso da ligação dinâmica, que é quando o programa durante a sua execução requisita o carregamento de alguma outra DLL (no caso do Windows, usando a função LoadLibrary) a processo é o mesmo e o loader procura pela DLL pelos exatos mesmos lugares, a não ser que o programador tenha fornecido um caminho completo.

Agora que já sabemos como tudo funciona, vamos ver como configurar o Visual C++ para ele nos ajudar com esses problemas.

Configurando o Diretório de Trabalho no Visual C++

Configurar o diretório de trabalho no Visual C++ é simples e facilita muito a vida. Alguns se perguntam porque simplesmente não colocamos os arquivos necessários onde o executável é gerado ou geramos o executável onde estes arquivos estão. Os principais motivos que vejo são:

  • Dependendo do tamanho do projeto, são gerados vários arquivos binários (múltiplos executáveis, dlls, etc), é muito conveniente poder simlesmente ir no diretório onde eles são gerados e apagar tudo indiscriminadamente, pois basta recompilar que teremos os arquivos de volta e nos livramos de arquivos obsoletos.
  • As vezes para cada tipo de compilação (versão debug e versão release) temos um conjunto de arquivos executáveis diferentes que não pode ficar no mesmo diretório, copiar os assets ou colocar tudo onde ficam os assets, vai certamente gerar conflitos
  • É comum os assets ficarem em um repositório próprio e é conveniente poder simplesmente copiar novas versões do sistema de controle de versões ou fazer atualizações sem se preocupar com os binários no meio do caminho.
  • ou seja, simplesmente uma questão de organização.

Para dizer ao Visual C++ o local que ele deve executar o seu programa, selecione seu projeto no Solution Explorer, clique nele com o botão direito e selecione Properties:

image

Na janela que se abrir, selecione Debugging e atente a caixa Working Directory. Neste ponto que você deve inserir o diretório onde seu programa vai executar. Pode inclusive ser um caminho relativo ao seu projeto, como no exemplo abaixo:

image

Pode-se inclusive utilizar váriaveis de ambiente ou variáveis do próprio Visual C++. Clicando-se no botão a esquerda e selecionando Edit abre-se uma janela que mostra todas as variáveis disponíveis e como usa-las.

Configurando o PATH para DLLs

O último tópico que vamos lidar neste artigo é como ajudar o loader a encontrar DLLs que nosso programa precisa. Temos vários motivos para não colocar as DLLs juntas com nossos executáveis, entre os quais podemos destacar:

  • Organização: ficar colocando várias DLLs espalhadas por todo sistema pode ser um verdadeiro problema para se gerenciar. É comum nesses casos atualizarmos uma biblioteca e começarem a surgir bugs estranhos porque alguém esqueceu uma DLL perdida em algum diretório.
  • O diretório onde seu programa é gerado, é o diretório onde seu programa é gerado, ficar colocando vários arquivos ali causa bagunça
  • Concentrar cada DLL onde estão os arquivos originais do código que a gerou facilita em muito a organização, atualização e tudo mais.

Para então dizer ao loader onde encontrar as DLLs, vamos alterar a variável de ambiente PATH durante a execução de nosso programa e colocar nela o diretóro onde as DLLs se encontram. Para isso, basta acessar a mesma tela anterior (selecione o projeto, clique com botão direito e depois clique em properties). Quando a tela se abrir, selecione novamente a opção Debugging e agora fique atento a caixa Environment:

image

No exemplo acima, vemos que foi inserido o código:

PATH=%PATH%;c:\libs\ogre\bin\;c:\libs\SDL\bin\

Este código é a sintaxe usada para se alterar uma variável de ambiente no Windows, nesse caso, temos:

  • PATH= : aqui estamos atribuindo um valor a variável PATH, mesma sintaxe que temos em C para alterar valores em váriaveis, seu valor no caso vai ser “%PATH%;c:\libs\ogre\bin\;c:\libs\SDL\bin\ “. Note que ela possui multiplos caminhos, separados por ;.
  • O primeiro caminho, é a própria variável PATH, no caso, usando %PATH%, que é a sintaxe do Windows para se expandir o valor de uma variável. Ou seja, o valor atual dela vai substituir o %PATH%. É a forma de dizer PATH += c:\libs\ogre\bin\;c:\libs\SDL\bin\ .
  • Por fim temos dois caminhos: c:\libs\ogre\bin\ e c:\libs\SDL\bin\

Dessa forma, toda vez que nosso programa for executado pelo Visual C++, a variável de ambiente PATH vai possuir além dos valores atuais os diretórios c:\libs\ogre\bin\ e c:\libs\SDL\bin\, possibilitando que o loader encontre as DLLs nesses diretórios também.

Considerações Finais

Este artigo explicou como funciona o ambiente de execução de um programa, os conceitos de diretório de trabalho e como bibliotecas dinâmicas são carregadas pelo sistema operacional. Por fim, vimos como configurar a ferramenta Visual Studio C++ para controlar esses aspectos da execução programa.

O artigo tem enfoque maior em desenvolvedores Windows, mas os conceitos aqui mostrados são praticamente os memos em outros ambientes e as mesmas configurações costumar ser suportadas em quase todas ferramentas de desenvolvimento.

Atenção que as configurações do Visual C++ só tem efeito quando o programa é executado de dentro do Visual C++, se você pretende copiar seu programa para um amigo ou usuário, tem que garantir no local onde eles esteja todas as DLLs e assets vão ser encontrados.

Por fim, quem tiver interesse abaixo temos o link para download da aplicação CheckDir.

download

Comentários (15)
  • Emerson Max
    avatar

    Sim uma morte lenta e dolorosa para aqueles que usam caminhos absolutos :evil:

  • MM
    avatar

    Muito legal, mesmo eu não trabalhando com DLL's ainda, já serve como uma boa introdução e para ter noção =}

    avatar

    "uma morte lenta e dolorosa para aqueles que usam caminhos absolutos"[3]

  • Anônimo
    avatar

    Obrigado. Dll nem todo mundo costuma usar, mas a questão do diretório de trabalho afeta a todos.

    T+

  • José F. Filho
    avatar

    Ótimo artigo Bruno.

    Já passei noites em claro tentando descobrir o porquê que um programa meu não funcionava em outro computador. Como o programa carregava suas configurações de um arquivo de texto localizado SEMPRE em "C:\\Data\\Plugin.dat", ocorria o erro de não encontrar o arquivo. Isso tudo por causa da letra do disco rígido que era D:\\.

  • Anônimo
    avatar

    Obrigado José, por isso é sempre bom conhecer o ambiente.

  • FILIPE FERREIRA
    avatar

    Ola Bruno! O site de vcs é maravilhoso, venho acompanhando de longe algum tempo rs. Sei q este topico nao tem nada a ver com a pergunta que farei, mas nao sei onde deveria perguntar. Vou começar o curso de tecnologo em desenvolvimento e analise de sistemas. Sera que esse curso tem ramificaçoes futuras para desenvolvimento de games, facilitaria a entrada nessa area, ou é melhor fazer algo especifico para games desde ja?
    Abraço.

  • Anônimo
    avatar

    Para quem quer ir para jogos eu sempre recomendo fazer ciência da computação, pois é o curso que foca mais em teoria e pesquisa, o que geralmente é preciso para games, além de uma bagagem matemática mais completa.

    Mas de qualquer forma, eu ainda acho melhor o Tecnólogo em análise de sistemas do que uma graduação em jogos. Não consigo imaginar um desenvolvedor de jogos sem toda a bagagem da ciência da computação, da mesma forma, não consigo imaginar que uma graduação de jogos va oferecer todo esse currículo e ainda material focado em jogos no mesmo tempo ou até menos do que uma graduação comum.

    Resumindo: termine sua graduação e depois faça uma especialização focada em jogos.

  • Anônimo
    avatar

    Muito obrigado pela resposta! Tentei achar algum lugar para me cadastrar no site, e uma maneira de fazer minha pergunta de forma mais isolada, mas não achei rs. Então vou tentar tirar minhas dúvidas aqui mesmo se for possivel, e se voce puder colaborar.O que acontece é que eu consegui uma bolsa do FIES que paga 100% do curso de analise, nao pagarei nada nem mesmo após o termino do curso. Mas ja me dasanimaram dizendo que o ensino é fraco e tal. O problema, é que nao to com essa grana para pagar 600, 800 reais ou mais, de parcela em curso, e tambem nao sou mais um garoto para esperar mais tempo. Por isso sinto que a hora é agora, talvez a ultima chance de fazer o que eu sonho desde pivete, que nao é só necessariamente jogos, mas tambem admiro muito programaçao no geral. Fiquei por muito tempo na area de mecânica, mas desanimei total, perdi o gosto rs. To pensando entrar nessa facul, mesmo o pessoal dizendo que é fraca, sei la, 2 anos e meio passa rapido, depois faço graduação em uma melhor, corro atrás... E além disso, posso ir acompanhando esse site, e ir me desenvolvendo aos poucos :P . Desculpa o chororô, mas me sinto numa encruzilhada que nunca passei antes kkk . Estou buscando todas as informações possiveis.
    Abraço.

  • Anônimo
    avatar

    Sobre o contato, no menu do canto esquerdo tem um link para contado com um form que vai direto para nós aqui do site.

    Sobre sua dúvida, é algo um tanto pessoal e é difícil opinar. Mas se você não tem os recursos / meios para algo melhor no momento, acho que não tem muitas escolhas não?

  • Lucas B. Luna
    avatar

    Ótimo artigo, como sempre Sanches! Me esclareceu várias coisas.

    Mas eu fiquei curioso sobre essa função LoadLibrary() do Windows. Eu tentei usá-la colocando:

    HMODULE SDL = LoadLibrary("SDL.dll";);

    Porém, o código não compilou. Até que eu incluí SDL.lib e SDLmain.lib em Properties > Linker > Input > Additional Dependencies. Aí funcionou.

    Porém, depois eu removí do código a parte que usava LoadLibrary() e o programa continuou compilando normalmente e funcionando.

    Eu pensei que eu poderia usar essa função para poder carregar DLL's sem ter que mexer em qualquer coisa no compilador. Dá pra explicar isso melhor pra mim?

  • Anônimo
    avatar

    Lucas, quando você usa a ligação dinâmica, justamente por ser dinâmico, o linker não sabe onde estão as funções da DLL, pois para o linker, LoadLibrary não tem nada de especial, é somente mais uma dll.

    Isso quer dizer que qualquer chamada a SDL no seu código, o linker precisa saber onde estão as rotinas durante a compilação, tipo, chamar SDL_Init, ele precisa encontrar a definição dessa função em algum lugar, por isso que você precisa incluir SDL.lib na configuração do seu projeto, pois isso diz ao linker para olhar também nesse arquivo e quando ele faz isso, descobre que precisa incluir SDL.dll na linkagem. Isso vai ficar no arquivo exe que você gera e quando você roda o programa, o Windows já sabe que tem que carregar a dll também.

    Agora quando você usa LoadLibrary, sem chance, o linker não sabe, então vocÊ não pode chamar SDL_Init e qualquer outra rotina da SDL diretamente no seu código, precisa primeiro pegar o endereço delas e para isso você precisa então chamar GetProcAddress.

    Resumindo: LoadLibrary não resolve isso :).

  • Anônimo
    avatar

    Correção:

    Lucas, quando você usa a ligação dinâmica, justamente por ser dinâmico, o linker não sabe onde estão as funções da DLL, pois para o linker, LoadLibrary não tem nada de especial, é somente mais uma função (não DLL). :)

  • Lucas B. Luna
    avatar

    Desculpa pela demora pra responder. Obrigado pela explicação cara, abraços!

  • William  - Capturando endereço de memória em programa ativo.
    avatar

    Bruno, como que seria capturar um endereço de memória dentro de um programa em execução ?

    Estou tentando capturar a tecla F9 dentro de um jogo e troca-la por outra.

    Sei que F9 = 0x78 em C++ ou Ca questão é como conseguir capturar isso dentro do jogo no momento que ela é pressionada. saber a posição do código. usei o IDA PRO e o Cheat Engine 3.5 para descobrir mais sem sucesso.


    Obrigado

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter