TrabalhosGratuitos.com - Trabalhos, Monografias, Artigos, Exames, Resumos de livros, Dissertações
Pesquisar

Arquivos de Recursos no Windows

Por:   •  14/11/2018  •  Resenha  •  4.287 Palavras (18 Páginas)  •  246 Visualizações

Página 1 de 18

Arquivos de Recursos no Windows

INTRODUÇÃO

Estarei neste tutorial "tentando" explicar como utilizar os arquivos de recursos (resources files), digo "tentar" porque até mesmo eu não vejo sentido em algumas coisas que eles fazem, e também não sou expert nisso. Toda a explicação aqui está sendo dada do entendimento que eu consegui ter do livro "Tricks of the Windows Game Programming Gurus" de Andre Lamothe, então qualquer coisa xingue a ele ;-). Este tutorial presume que você já saiba programar em C e C++, e também já esteja começando a criar seus programas para windows.

O QUE SÃO?

Nos arquivos .EXE podemos encontrar mais do que somente código de maquina, podemos encontrar imagens, tabelas de string, sons, ícones e etc. ;-) a organização fica mais ou menos assim:

CODIGO DO PROGRAMA

RECURSOS

ICONES

BITMAPS

.WAV

CURSORES

TABELAS DE STRING

DIALOGOS

MENUS

...

Toda essa parafernália fica localizada ao fim do executável, e a única coisa que vejo de bom neles é que dificulta que seus sons, arte, e etc sejam roubados. Outra boa utilidade é que eles são carregados para memória juntamente com seu programa o que garante um acesso mais rápido a eles que em um arquivo no disco.

COMO CRIAR UM ARQUIVO DE RECURSOS

Na maioria dos pacotes de programação para windows vem inclusa uma ferramenta chamada "compilador de recursos". Este programa recebe como entrada um arquivo ASCII com extensão .RC. O Arquivo RC consiste em uma "linguagem" parecida com C, tudo o que ele faz é ler todas as entradas do arquivo, pegar todos os recursos e uni-los em um único arquivo de extensão .RES. O arquivo .RES contém a representação binária de todos os seus ícones, sons e etc. que você colocou no arquivo de recursos, depois esse arquivo é unido com as demais partes do programa, do qual e é formado o nosso executável.

[pic 1]

Para criar um arquivo de recurso, você pode tanto utilizar um compilador de recursos separado ou pode utilizar a IDE de sua preferência.

Ícones

UTILIZANDO ICONES

Para utilizar os recursos você necessita de dois arquivos, o .RC e possivelmente um .H que será utilizado para criar referencias simbólicas do conteúdo do arquivo RC. Um exemplo de como você carrega seus ícones:

Winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

Winclass.hIconSm = LoadIcon(NULL, IDI_APPLUCATION);

Isso carrega o ícone padrão de aplicações, porem você provavelmente vai querer seus próprios ícones nos seus programas e aí entram os recursos. Você deve ter um ícone para poder utilizar. Eu criei um ícone bem feio, mas será este que vamos utilizar no resto desta seção, um ícone pode variar em tamanho de 16x16 ate 64x64 com 256 cores, porém a maioria dos ícones é 32x32 com 16 cores. O ícone que criei foi esse:

[pic 2]

Agora que temos o ícone vamos colocar no RC. Lembre-se que as IDEs facilitam muito esse trabalho para nós. Você pode colocar o ícone no RC usando uma string que vai guardar a referencia por nome:

nome_do_icone ICON NOMEDOARQUIVO.ICO

EX:

IconeJanela ICON Janela.ico

IconeLegal  ICON iconeLegal.ico

Ou pode definir o ícone como um inteiro de referencia:

ID_do_ícone ICON NOMEDOARQUIVO.ICO

EX:

200 ICON Janela.iço

201 ICON iconeLegal.ico

OBS: 200 e 201 são simples números aleatórios poderia ser 1, 3, 50 etc.

Mas como se referenciar a eles no programa ? Bem, no primeiro caso você usa o nome dele mesmo, ou seja "IconeJanela". Lembre-se de colocar entre aspas, já que estamos falando de uma String. Já no segundo caso é onde entra o .H, já que seria bem complicado ficar decorando números de ícones, então você pode usar o .H para definir alguns nomes mais simpáticos para a referência no RC. Você pode fazer algo assim:

RESOURCE.H

#define ICON1 100

#define ICON2 101

#define ICON3 102

RESOURCE.RC

#include "RESOURCE.H"

ICON1 ICON icone1.ico

ICON2 ICON icone2.ico

ICON3 ICON icone3.ico

Logicamente o arquivo .ICO deve estar no mesmo diretório caso contrário o compilador de recursos não conseguirá localizar-lo. Agora no seu código é só referenciar ao ícone como "ICON1", "ICON2", "ICON3" etc., vou mostrar um exemplo bem simples de como carregar um ícone usando as técnicas mostradas:

.RC:

nome_do_seu_ícone ICON seu_icone.ico

no programa:

winclass.hIcon = LoadIcon(hinstance, "nome_do_seu_ícone");

winclass.hIconSm = LoadIcon(hinstance, "nome_do_seu_ícone");

ou para usar a constante definida, você deve incluir o arquivo com as definições:

.H

#define ICON1 100

#define ICON2 101

#define ICON3 102

no .RC:

ICON1 ICON icone1.ico

ICON2 ICON icone2.ico

ICON3 ICON icone3.ico

E depois no seu programa, fazer isso:

winclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICON1));

winclass.hIconSm LoadIcon(hInstance, MAKEINTRESOURCE(ICON1));

A macro MAKEINTRESOURCE() transforma o inteiro do .H em um ponteiro string, mais não se preocupe com o funcionamento da macro, somente lembre-se de como organizar as definições.

Cursores

USANDO CURSORES

O processo para utilizar um cursor no RC é praticamente idêntico ao processo utilizado nos ícones. Os cursores podem ser imagens de 32x32 com 16 cores ou ate 64x64 com 256 cores e também podem ser animados ;-). Crie o seguinte cursor para nosso exemplo:

[pic 3]

Para utilizar o cursor você deve primeiramente utilizar a palavra chave CURSOR ou seja:

meu_cursou CURSOR cursou.cur

EX:
Cursor_da_janela CURSOR crosshair.cur
Meu_cursou CURSOR meucursor.cur

Ou então pelo método de usar um ID para definir:

ID_do_cursor CURSOR meu_cursor.cur

EX:
100 CURSOR meu_cursor.cur
101 CURSOR cursor_da_janale.cur

todo o processo já explicado antes sobre os arquivos .H pode ser utilizado:

CURSOR.H
#define CURSOR1 100

CURSOR.RC
#include "CURSOR.ICO"

CURSOR1 CURSOR meu_cursor.cur

Para utilizar no programa você utiliza o mesmo método, porem lembre-se que você estará carregando um cursor não um ícone:

winclass.hCursor = LoadCursor(hInstance, "Cursor_da_janela");

ou pelo ID,

winclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(CURSOR1));

Mas agora imagine que você deseja criar duas janelas, e você deseja que o cursor mude de uma para outra então você vai usar a função SetCursor() que tem o seguinte protótipo:

HRESULT SetCursor(HCURSOR hCursor);

HCURSOR é um, handle para o cursor que foi carregado usando a função LoadCursor(). O único problema e que a função SetCursor() é bem limitada o que vai sobrar pra você ;-). Para implementar o exemplo da mudança de cursor você terá de detectar quando o mouse muda de janela e trocar você mesmo o ícone, mais nada de assustador, um exemplo de como trocar o cursor seria:

HCURSOR hCursor = LoadCursor (hInstance, "meu_cursor");

Depois no seu programa:

SetCursor (hcursor);

É simples não é mesmo ;-) lembre-se de que você deve manter seu arquivo RC bem organizado quando for misturar vários ICONS e CURSORS no mesmo arquivo, organize o máximo possível colocando ICON em uma parte só para ICON e assim por diante.

Tabelas de String

USANDO TABELAS DE STRING

O windows suporta também as tabelas de string nos arquivos de recursos, você só pode ter uma tabela de string e nela que você vai colocar todas as string que for utilizar no programa. Um detalhe é que essas tabelas são equivalentes a utilizar um arquivo de cabeçalho, mas quando a tabela de strings muda o programa não precisa ser compilado novamente, apenas passar pelo linker. Para ter strings mais independentes uma outra idéia seria usar arquivos externos.

STRINGTABLE

{

ID_STRING, "STRING 1"

ID_STRING, "STRING 2"

.

.

}

Note que ela utiliza o ID inteiro para referenciar a string que pode ter ate 255 caracteres de extensão. Você também pode colocar qualquer tipo de controle usado no printf() na string como %d, %f etc, menos as seqüências de escape como , mais pode utilizar seqüência octonais como 15 e etc.

Um exemplo de utilizar as tabelas esta aqui:

no .H

#define ID_STRING_STAR 16
#define ID_STRING_QUIT 17
#define ID_STRING_RESTART 18

no .RC

STRINGTABLE
{
ID_STRING_START, "START GAME"
ID_STRING_QUIT, "QUIT GAME"
ID_STRING_RESTART, "RESTART GAME"
}

Para carregar uma string na tabela você deve utilizar a função LoadString() que tem o seguinte protótipo:

int LoadString(HINSTANCE hInstance, // handle para o modulo que contem a tabela
                        UINT uID,                        // ID da string a carregar
                        LPSTR lpBuffer,              // endereço do buffer para armazenar
                        int nBufferMax);              // tamanho do buffer

a função retorna o numero de caracteres lidos ou 0 em caso de erro. Para usar a função você deve fazer algo como:

char  start[80], quit[80], restart[80];

if (!LoadString(hInstance, ID_STRING_START, start, 80))

{

   // ERRO

}

if (!LoadString(hInstance, ID_STRING_QUIR,quit, 80))

{

   // ERRO

}

if (!LoadString(hInstance, ID_STRING_RESTART, restart, 80))

{

   // ERRO

}

depois e só fazer o que quiser das strings. Como eu havia dito antes não vejo utilidade nisso mais, quem sabe você tem alguma utilidade para elas.

Arquivos de Som (WAV)

USANDO WAVES

Utilizar arquivos de sons é um tanto quanto mais complicado, porém não vai ser coisa do outro mundo, esses arquivos armazenam a forma digital de uma onda sonora, geralmente são usados waves com taxas de 11KHz, 22KHz e 44KHz (qualidade de CD). Para adicionar um wave a um resource, você deve utilizar a palavra WAVE seguindo o exemplo dos demais recursos, ou seja:

Meu_wave WAVE arquivo.wav

Ou também pelo ID:

100 WAVE arquivo.wav

101 WAVE arquivo2.wav

carregar esse tipo de arquivo é um pouco complicado e está fora do escopo deste tutorial, porém, vou demonstrar como tocar um wav, contido no arquivo de recursos, para fazer isso vamos utilizar a função PlaySound(), que tem o seguinte protótipo:

BOOL PlaySound(LPCSTR pszSound, // nome do som a tocar

               HMODULE hmod,  // Instancia da aplicação

               DWORD fwdSound); // parâmetro de como executar o som

pszSound -   este é o nome do arquivo em disco ou do nome no recurso, note que você pode

utilizar a macro MAKEINTRESOURCE() neste parâmetro.

hmod-  Instancia da qual o aplicativo será retirado o recurso, geralmente a instancia do

próprio programa.

fwdSound-  controla como o som será reproduzido.

Abaixo uma tabela com os possíveis valores para o parâmetro fwdSound da função PlaySound():

TABELA:

SND_FILENAME    -   pszSound é um arquivo em disco

SND_RESOURCE    -   pszSound é um identificador no resource

SND_MEMORY      -   o wav esta carregado na memória, lpzSound é um ponteiro pra ele

SND_SYNC        -   a função retorna assim que o som ja foi executado

SND_ASYNC       -   a função retorna antes de o som ter sido totalmente tocado, chame    

                    PlaySound com pszSound = NULL para voltar ao modo normal de              

                    execução.

SND_LOOP        -   toca sem parar o som, você deve definir o modo assíncrono para usar

                    esta opção.

SND_NODEDEFAULT -   não toca o som padrão caso o arquivo ou recurso não seja

                    encontrado.

SND_PURGE       -   todos os outros sons são interrompidos para executar o que foi

                    chamado.

SND_NOSTOP      -   o som e encoberto por qualquer outro som que for executado caso

                    não consiga tocar o som retorna FALSE imediatamente sem tocar o

                    som.

SND_NOWAIT      -   se o dispositivo estiver ocupado retorna sem tocar o som.

Você deve especificar os parâmetros que desejar, executando um or binário "|" nos parâmetros da seguinte forma:

RESOURCE.H

#define SOUND_ID_WIN 1

RESOURCE.RC

#include "RESOURCE.H"

SOUND_ID_WIN WAVE wingame.wav

No programa:

PlaySound(MAKEINTRESOURCE(SOUND_ID_WIN),

          hInstance,

          SND_ASYNC | SND_RESOUCE);

Lógico que usando este método você não terá nenhum tipo de controle sobre o som, mas se você somente deseja tocar o som e nada além, já é o bastante.

Bitmaps

USANDO BITMAPS

Os bitmaps não diferem muito de ícones quanto a forma de utilizar, no entanto lembre-se que um bitmap muito grande pode deixar seu executável gigante e creio que não é o que você esteja pretendendo. Mas pequenas imagens podem, sem problema, ser armazenadas nos arquivos de recursos. Para utilizar deve-se usar a palavra chave BITMAP como todos outros exemplos, o que resulta em algo mais ou menos assim:

MEU_BITMAP BITMAP arquivo.bmp

Ou pelo ID:

100 BITMAP arquivo2.bmp

para carregar um bitmap você ira utilizar a função LoadBitmap() que tem o seguinte protótipo:

HBITMAP LoadBitmap(HINSTANCE hInstance, LPSTR lpbitmapname)

Como sempre ;-) hInstance é a instancia para onde deve ser carregado o recurso, ou o aplicativo em si, e lpbitmapname é o nome do arquivo em disco ou a descrição no recurso. Para desenhar você irá utilizar a função BitBlt() da qual fará uma chamada mais ou menos assim:

BitBlt(hdc, X, Y, Width, Height, hdcMemory, 0, 0, SRCAND);

Onde hdc é o contexto onde vai ser desenhada a imagem.
X, Y posição na tela.
Height, Width largura e altura da imagem.
HdcMemory e uma variável que deve ser iniciada como uma espécie de subbitmap de hdc por meio da chamada a CreateCompatibleDC().
Bem, acho que não há muito para se explicar sobre este recurso já que utilizando DX você vai ter um controle bem mais "profissional".

Menus

USANDO MENUS

Calma esse tutorial já esta acabando ;-) quando você vai trabalhar com programas windows e bastante importante que seu programa possa ter um menu de opções com as quais o usuário possa melhor aproveitar seu programa, os menus em algumas IDE podem ser criados simplesmente com o recurso de arrastar e soltar porem não e bom você se tornar "escravo" de uma IDE, pois nem sempre você terá a oportunidade de ter uma na mão ;-) Menus são criados nos resources por meio da palavra MENU e segue a seguinte ordem de criação:

NOME_DO_MENU MENU DISCARDABLE

{

// definições de menu

}

note que ao invés de "{" e "}" pode-se usar BEGIN e END porem prefiro as chaves já que deixam mais parecido com uma sub-linguagem de C. mais pêra ae tem uma nova palavra na definição do menu que não foi utilizado nos outros casos, bem pense em DISCARDABLE como uma coisa nescesaria porem que você não precisa se preocupar ;-) Um menu pode ter vários sub-menus e cada sub-menu um outro menu e assim recursivamente porem não vou abordar menu de menus mais somente os menus principais da tela. De uma olhada na figura abaixo e olhe como seria a definição de menu para essa janela:

[pic 4]

Ta você deve estar pensando que eu copiei essa janela, mais na verdade eu fiz isso mesmo somente "hackeie" ;-) a janela do Notepad mais e um bom exemplo, bem para criar este menu usaríamos o seguinte código:

MenuPrincipal MENU DISCARDABLE

{

   POPUP "Arquivo"

   {

       MENUITEM "Novo",     MENU_NOVO_ID

       MENUITEM "Abrir",     MENU_ABRIR_ID

       MENUITEM "Salvar",    MENU_SALVAR_ID

       MENUITEM "Salvar Como", MENU_SAVARCOMO_ID

       MENUITEM "Sair",       MENU_SAIR_ID

   }

   POPUP "Ajuda"

   {

       MENUITEM "Sobre", MENU_SOBRE_ID

   }

}

 

Vamos analisar este menu, primeiramente vemos que o nome do menu e "MenuPrincipal", normalmente usa-se strings ao invés de ID neste caso, logo depois vemos a palavra POPUP que marca o inicio de um menu propriamente dito dentro da seção marcada no caso o primeiro seria "Arquivo", nele encontramos vários:

MENUITEM STRING, ID

Esse comando inicia os itens da seção logo depois vemos o popup "Ajuda" que tem o item sobre, mais e os ID´s bem eles poderiam ser inicializados utilizando um .H para definir as constantes assim mais ou menos:

// DEFINES PARA "ARQUIVO"

#define MENU_NOVO_ID 1000

#define MENU_ABRIR_ID 1001

#define MENU_SAVAR_ID 1002

#define MENU_SAVARCOMO_ID 1003

#define MENU_SAIR_ID 1004

// DEFINES PARA "AJUDA"

#define MENU_SOBRE_ID 2000

note que utilizei 1000 incrementando em 1 na parte arquivo e 2000 para a próxima isso e uma boa técnica e ajuda a manter organização no .RC. não coloquei um define para "MenuPrincipal" já que vou utilizar ele como string mais a seguinte definição:

#define MenuPrincipal 100

e depois MAKEINTRESOURCE(MenuPrincipal) ou MAKEINTRESOURCE(100) da na mesma. Você pode utilizar atalhos de teclado nos menus, para facilitar a utilização dos mesmos, como por exemplo:

MENUITEM "&SALVAR", MENU_SALVAR_ID

Isso faz com que o item salvar seja linkado pela combinação ALT+S do teclado, este método também funciona com a definição do POPUP, da seguinte forma:

POPUP "A&RQUIVO"

Isso faz com que ALT+R ative a parte ARQUIVO do menu. Lembre-se "&" deve vir antes do caractere que você deseja utilizar com atalho. Existem diversas formas de se colocar um menu em uma janela, uma delas e na definição da janela já colocar o menu la ;-) assim:

Winclass.lpszMenuName = "MenuPrincipal";

Ou

Winclass.lpszMenuName = MAKEINTRESOURCE(MenuPrincipal);

Nenhum problema com isso, mais o único contra neste método e que qualquer janela que você crie a partir dessa classe vai ter o mesmo menu, para contornar esse problema vamos utilizar a função LoadMenu() que tem o seguinte protótipo:

HMENU LoadMenu(HINSTANCE hInstance, // instancia de aonde vai carregar o menu

                                   LPCTSTR lpMenuName); // nome do menu ou ID do mesmo

Se a função tiver susceso em carregar o menu, ela retorna em HMENU, um handle para o menu que você poderá utilizar, você poderá utilizar esse meio na chamada a CreateWindow() como abaixo:

if (!(hwnd = CreateWindowsEX(NULL,                      // estilo estendido

                             WINDOW_CLASS_NAME,         // nome da classe

                             "TESTE DE MENU",           // titulo da janela

                             WS_OVERLAPPEDWINDOW | WS_VISIBLE, // estilo da janela

                             0, 0,                      // X, Y

                             500, 500,                  // Largura e altura

                             NULL,                      // handle para janela pai

                             LoadMenu(hInstance, "MenuPrincipal"), // menu

                             hInstance,                 // instancia do aplicativo

                             NULL)))                    // algumas opções extra

        return (0);

Note que se ao invés de string você estiver utilizando um inteiro você deve utilizar:

LoadMenu (hInstance, MAKEINTRESOURCE(MenuPrincipal)),

Mais e claro que você pode ter vários menus no seu arquivo RC, e vamos supor que você esteja querendo mudar o menu por outro em resposta a algum evento ocorrido, para fazer isso você ira utilizar a função SetMenu() que tem o seguinte protótipo:

SetMenu(HWND hWnd, // janela a modificar

                HMENU hMenu); // handle para o menu

Vamos supor que você tenha definido o campo lpszMenuName com NULL e depois deseja colocar nosso recém criado menu, então no código você deve criar, algo mais ou menos assim:

HMENU meuMenu = LoadMenu(hInstance, "MenuPrincipal");

SetMenu(hwnd, meuMenu);

Simples ne ;-), agora que já estamos com nosso menu criado e tudo mais, e a hora, de responder a eventos dos menus, pois não tem utilidade um menu que não serve pra nada não e mesmo.quando você clica em um item do menu e enviado um WM_COMMAND para o tratador de eventos do programa, ou seja WinProc() da janela onde o menu esta, quando msg = WM_COMMAND serra configurado da seguinte forma:

lparam = a janela que a mensagem foi enviada

wparam = o ID do item do menu que disparou o evento

você pode criar todo esse processamento de mensagens dentro de um switch como mostra o exemplo a seguir:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg,

                                                               WPARAM wparam,   LPARAM lparam)

{

  PAINTSTRUCT ps;

  HDC hdc;

switch (msg)

{

  case WM_CREATE:

  {

    // inicialize as coisas aqui

    return 0; // retorna OK

  }

  break;

  case WM_COMMAND:

    switch(LOWORD(wparam))

    {

      case MENU_NOVO_ID:

      {

        // trate o evento de novo

      }

      break;

      case MENU_ABRIR_ID:

      {

        // trate o evento de abrir

      }

      break;

      case MENU_SALVARCOMO_ID:

      {

        // trate o evento de salvar

      }

      break;

      case MENU_SALVAR_ID:

      {

        // trate o evento de salvar rapido

      }

      break;

      case MENU_SAIR_ID:

      {

        // trate o evento de sair

      }

      break;

      case MENU_SOBRE_ID:

      {

        // trate o evento de sobre

      }

      break;

      default:

      break;

    }

  }

  break;

  case WM_PAINT:

  {

    hdc = BeginPaint(hwnd, &ps);

    // desenho tudo o que tiver de desenha aqui

    EndPaint(hwnd,&ps);

    return 0; // retorna OK

  }

  break;

  case WM_DESTROY:

  {

    // trate de liberar tudo o que você quer

    PostQuitMessage(0);

    return 0; // retorna OK

  }

  break;

  default:

  break;

}

return (DefWindowProc(hwnd, msg, wparam, lparam));

}

 

Você notou como e fácil ;-) Tudo bem que existem vários outros eventos que um menu pode disparar, mas no geral só queremos saber se foi ou não clicado não e mesmo ?

Conclusão

CONCLUSÃO

Bem, espero que você tenha gostado do tutorial, existem vários outros aspectos sobre um arquivo de recurso (resource) que eu não abordei aqui, ainda mais porque muitos deles eu não encontrei material bom o suficiente para que eu mesmo compreenda ;-) Mas no geral o que foi dito aqui já é bastante para quem está começando a se aventurar no mundo da programação WINDOWS. Qualquer duvida mande um e-mail ou poste no fórum que eu irei fazer o máximo possível para ajudar. Até a próxima.

  

GDJ 2005 - Colaboração das Comunidades ArsLudica/PDJ (2001/2003)

...

Baixar como (para membros premium)  txt (23.2 Kb)   pdf (221.3 Kb)   docx (58.7 Kb)  
Continuar por mais 17 páginas »
Disponível apenas no TrabalhosGratuitos.com