RAPID-Q
Carlos Godinho
Site de Rapid-Q
  em Português
Uma linguagem de Programação
    Basic criada por William Yu
DOCUMENTAÇÃO DE PROGRAMAÇÃO EM RAPID-Q
Clique em um dos botões para acessar as informações correspondentes, conforme a documentação original fornecida por William Yu traduzida para o português.

Foram traduzidos os capítulos mais necessários para programas gráficos (GUI), quanto aos demais serão traduzidos mais tarde. Estão disponíveis os capítulos: 1, 2, 3, 5, 6, 7, 8, 9 e 11.



     


Rapid-Q é uma linguagem de programação, uma linguagem de programação BASIC para ser mais preciso. É multiplataforma, embora não completamente compatível em termos da interface gráfica, mas a linguagem é bastante compatível entre plataformas. Rapid-Q está disponível para; Windows, Linux, e Unix (Solaris e HP-UX). O Rapid-Q pode ser usado para criar tanto GUI (gráficos) como aplicativos de CONSOLE (DOS), incluindo CGI. O compilador Rapid-Q gera um executável em byte-code, diferente do opcode nativo do seu processador. Assim, se requer um intérprete para ler e executar o byte-code. Este intérprete é embutido em cada um dos seus aplicativos, assim nenhum arquivo extra é necessário quando você distribui seu executável. A questão preocupante é a velocidade, mas dependendo de em que você está usando o Rapid-Q, isso pode não ser um fator tão importante. O Rapid-q foi originalmente criado para fornecer uma implementação grátis de uma linguagem de programação BASIC para o Windows.

1.1 Quão lento é o Rapid-Q realmente?

Embora o Rapid-Q compile seus fontes para byte-code, a execução não é tão lenta quanto se possa imaginar. Seus programas vão obviamente ficar lentos se você utilizar computação pesada ou executar muita manipulação de dados em baixo nível. Em termos de exibir gráficos, controles e forms, o Rapid-Q é tão rápido quanto qualquer outra linguagem. Para aplicações que necessitam de mais rapidez sugere-se que se escreva uma DLL em outra linguagem, como FreeBasic ou C, e se chame a DLL em Rapid-Q quando se necessitar de velocidade extra.

1.2 Que dizer sobre o tamanho do software?

Isto é obviamente uma coisa sobre o que não há muito o que dizer. O Rapid-Q (a parte do intérprete) em si mesma requer por volta de 300KB (150KB no Linux, 600KB no Sparc). Assim esta é a carga extra que todos os seus programas carregarão. Para a maioria dos programas do Windows, isto não é um problema, mas podem ficar um pouco inchados se você deseja um simples programa "Olá Mundo!".

1.3 Rapid-Q requer alguma DLLs extra?

Não. Nenhum de seus programas requer alguma DLL extra ou outro. Somente distribua seu arquivo .EXE. Ele roda em qualquer sistema Win95/98/NT/XP/7. O usuário final não precisa fazer nada ou alterar o Sistema do Windows. Se você comparar o tamanho acrescido do Rapid-q com o uso de DLLs de outras linguagens, o Rapid-q não ficará devendo nada em termos de tamanho.

1.4 Quanta memória o Rapid-Q consome além de seus programas?

O Rapid-Q não consome mais memória que seus outros programas (não literalmente), embora ele consuma memória do Windows o que na verdade limita a quantidade de memória para seu(s) programa(s). Isto realmente não é tão ruim como se poderia pensar. Ele consome menos que 200KB no global, virtual, e memória de pilha combinados. Isto é assim porque todas as variáveis e objetos são criados dinamicamente. A pilha de memória é o que merece cuidado. Se seu programa tem muitas rotinas aninhadas dentro de outras ou envolve intensa recursividade, isso pode crescer e decrescer rapidamente. No Rapid-q você não necessita destruir seus objetos ou variáveis quando fecha o programa, ele remove todos os objetos e libera o espaço de memória para você.

1.5 Quão fácil é raquear meus programas?

Sendo um compilador byte-code, se o usuário final souber como tudo é feito, então seria bastante fácil. Entretanto, o Rapid-Q gera um tipo de byte-code de saída não-padronizado, ou não-específico, desta forma, a menos que o usuário especificamente saiba o que cada operação faz, fica muito muito difícil raquear seus programas. É fácil fazer alterações dentro de suas variáveis strings (como em qualquer outra linguagem), porque elas não são codificadas de nenhum modo. Na maioria dos casos, seu programa será como um cofre para os bisbilhoteiros, eles não poderão nem mesmo saber que seu programa é byte-code! Diferente do Java ou outras linguagens, o Rapid-Q não anexa qualquer tabela de símbolos ou informação de debugging extra em seu .EXE.

1.6 Necessito de uma licença para distribuir meus programas?

Não. Não é necessário uma licença para usar Rapid-Q (ele é totalmente gratuito), e definitivamente não é necessária qualquer licença para distribuir ou vender seus programas. O único acordo que você tem que aceitar é que se você escolher usar Rapid-Q, você assume quaisquer e todas as consequências, diretas ou indiretas do uso do Rapid-Q. O Rapid-Q pode ser utilizado para qualquer tarefa, sejam elas visando lucro ou não. Você não paga nem um centavo, e nem; mesmo tem de dar a conhecer que seu programa foi criado em Rapid-Q (embora isto possa ser um gesto simpático). Entretanto, NÃO É PERMITIDO, vender o Rapid-Q (o programa compilador) e lucrar com ele. Você tem permissão para compactar o Rapid-Q junto com seu(s) programa(s), desde que o usuário seja informado que não está comprando o Rapid-Q, mas comprando o seu programa. Como isso é feito? Por não fazer publicidade de que sua distribuição inclue uma cópia de Rapid-q. Chame isso de recurso oculto.

Antes de passar a usar qualquer linguagem, ajuda saber como usá-la, primeiro. Este capítulo introduzirá você no ambiente do Rapid-Q, como compilar e executar seus programas, e como a estrutura geral do Rapid-Q é.

2.1 Ambiente Rapid-Q

Sua distribuição Rapid-Q deve incluir um programa IDE (Ambiente Integrado de Desenvolvimento chamado RAPIDQ.EXE), junto com o compilador Rapid-Q, o RC.EX), e as bibliotecas RAPIDQ32.LIB e RAPIDQCC.LIB. Estes são necessários para compilar qualquer de seus programas.Se qualquer um destes arquivos estiver faltando, relate o problema para quem lhe forneceu o Rapid-Q. Esses são os principais (4) programas que você necessitará para desenvolver e compilar seus códigos fonte em; Rapid-Q. A IDE é opcional, assim se sua distribuição não incluir este arquivo, não se preocupe. Se você se sentir mais confortável escrevendo seus programas no NotePad ou qualquer outro editor que você escolher, ótimo. Se esse for o caso, você poderá pular a próxima seção "Usand a IDE Rapid-Q". Todos estes arquivos devem; permanecer no mesmo diretório.

2.2 Usando a IDE Rapid-Q

Diferente de algumas IDEs que oferecem opções do debugging, a IDE Rapid-Q não oferece nada mais que um ambiente para escrever seus programas e executá-los, e fornece opções de comando em tempo de execução. Tem um indicador de erros primitivo, primitivo porque a IDE não faz a checagem de qualquer erro, mas o (RC.EXE) compilador é que faz isso. Também fornece realce da sintaxe, tornando a linguagem Rapid-Q muito mais fácil.
Suas limitações e características:
- Nem todos os componentes estão disponíveis no painel do designer.
- Quando seu programa é executado, seu código fonte é automaticamente salvo
- Todos os arquivos são compilados antes de serem executados.
A IDE do Rapid-Q é uma boa ferramenta para se aprender a linguagem Rapid-Q, mas quando ficamos mais acostumados com a linguagem, a pessoa poderá mudar para seu editor de texto favorito. Não significa que você tem de deixar de usar a IDE Rapid-Q se você se sente confortável usando-a.

2.3 Compilando e executando uma aplicação Simples

Ainda nem mesmo dominamos a escrita de uma aplicaçãco simples em Basic. Não se preocupe, é melhor primeiro saber como compilar e executar antes de qualquer outra coisa. Aqui está uma aplicação GUI (gráfica) simples que você pode digitar e compilar:     

      
  DIM MainForm AS QFORM
        MainForm.ShowModal


Se você está usando a IDE Rapid-Q, digite esse código (ou copie e cole se desejar). Clique em Run Menu, e  selecione Run. Se tudo estiver bem, você deverá ver uma Janela vazia (form) no canto esquerdo da tela. Feche-a e volte para a IDE. Se você preferir a linha de comando para compilar, então carregue o código em seu editor favorito, salve-o como um arquivo (Forms.BAS). Para compilá-lo, você digita a linha de comando: RC Forms.BAS
Se tudo estiver bem, você deveria obter um arquivo Forms.EXE no diretório corrente. Apenas digite Forms para executá-lo. Se tudo estiver bem, continue a leitura. Se não, então alguma coisa está errada, veja a seção "Localização de defeitos".

2.4 Chaves da linha de comando para RC.EXE

Discutiremos as 3 mais importantes:

      
  -I [Path] Change Include Path
        -L[Path] Change Library Path
       -G[File] Icon file


O compilador por default busca no diretório corrente os arquivos .LIB e quaisquer arquivos 'include'. Você pode substituir isso por especificar as chaves de comando das linhas acima. Visto que estamos falando sobre tipos de parâmetros C, há algumas facilidades sobre as quais se deve saber, primeiro, os caminhos devem ser especificados assim:

    
RC -Ic:/rapidq/includes

Você pode usar \ ao invés de / mas há uma razão por que / é preferida, considere isto:

    
RC -I"c:\rapidq long dir\includes\"

Se você nunca utilizou C (ou C++) você não vai provavelmente entender. Se você prefere o comando de linha acima, você deve adicionar um extra \ ao fim (ou deixar para lá), assim:

     RC -I"c:\rapidq long dir\includes\\"
     ou
     RC -I"c:\rapidq long dir\includes"


Isto é porque C traduz \ "dentro" o que é óbvio para qualquer programador de C.Também, nomes de arquivo longos devem estar entre aspas. Estas são as vantagens, assim agora você sabe (ou já sabia). Para trocar o icone default de seu arquivo EXE, você pode usar a chave de comando -G.

   
  RC -Gzip.ico zipview.bas

Isto é porque C traduz \" dentro " o que é óbvio para qualquer programador de C.Também, nomes de arquivo longos devem estar entre as- pas. Estas são as vantagens, assim agora você sabe (ou já sabia).
Para trocar o icone default de seu arquivo EXE, você pode usar a chave de comando -G.

    
RC -Gzip.ico zipview.bas

Quando seu arquivo ZIPVIEW.EXE é gerado, o arquivo "zip.ico" substituirá o icone default. Note que seu icone deve ser 32x32 pixels e 16 cores (também 766 bytes).

2.5 Estrutura Geral de um Programa Rapid-q

Não há nenhuma estrutura pré-definida de um programa Basic, mas há uma sequência geral que a maioria das pessoas utilizam.

    Directivas

    Declarações

    Variáveis Globais

    Programa Principal

Isto é apenas um exemplo genérico, seus aplicativoss Rapid-Q podem ser estruturadas em uma variedade de modos. Alguns levam a resultados parecidos, e alguns a resultados diferentes ou mais personalizados. Por Exemplo, $INCLUDE é uma diretiva para importar um arquivo e inserí-lo em um determinado ponto do programa. Assim, você pode usar $INCLUDE em um certo ponto de seu programa principal, isto poderá dar um resultado diferente do que se você incluísse o $INCLUDE no começo do programa.

2.6 Quão similar à linguagem BASIC é o Rapid-Q?

Há muitas similariedades, todo os comandos comuns como DIM, PRINT, MID$, etc foram herdados. Isto não significa que você poderá trazer seu programa DOS baseado em QBasic para Rapid-Q sem qualquer modificação. Visto que Rapid-Q é baseado em Windows, provendo suporte primitivo para CONSOLE, não significa que poderá converter seus aplicativos em QBasic para Rapid-Q sem ter de reescrever 50 a 90% de seu código fonte (dependendo do que o código faz).
Comentários em Rapid-Q começam com um apóstrofo ['], como em QBasic, ou você poderá usar REM ao invés.
Poderá separar linhas por usar o símbolo dois pontos [:]. Isto tem o mesmo efeito que começar uma nova linha.
Poderá continuar a digitação de uma linha para a outra colocando no final um espaço e o símbolo gráfico " _".

           
PRINT "Olá" : PRINT "Mundo"
            é o mesmo que
           
PRINT "Olá"
            PRINT "Mundo"


A Maioria dos programadores em Basic tornam-se muito acostumado à linguagem uma vez que passam a usá-la. Entretanto, Rapid-Q não herda as propriedades do Visual Basic. Por Exemplo, não há SUB MAIN procedure, nem há qualquer identificador particular ou público utilizado em Rapid-Q. Seu programa inteiro é considerado o programa PRINCIPAL. Indo de Rapid-Q para Visual Basic provavelmente se requer um bit a mais de esforço que o inverso.

Este capítulo, pode parecer igual à linguagem BASIC, isto ocorre porque o Rapid-Q é derivado da linguagem BASIC. Há no entanto alguns novos conceitos que não são parte da linguagem BASIC como a passagem de parâmetros infinita e usar o operador '- ' em sequências de caracteres (strings).

3.1 Diretivas do Rapid-Q

As diretivas são usadas no Rapid-Q para dizer ao compilador como e que tipo de código deve ser gerado. Rapid-Q suporta as seguintes diretivas, que são geralmente inserídas no começo de seus programas:

$APPTYPE [ CGI | CONSOLE | GUI ]
O padrão é GUI (Interface Gráfica do Usuário). $APPTYPE é usado dizer ao compilador que tipo de aplicativo você quer gerar. Embora o padrão seja GUI se nenhuma diretivas $APPTYPE for usada, o Rapid-Q detecta que tipo de aplicativo você quer gerar verificando o código de fonte. Assim para modificar esta característica, inclua a diretiva $APPTYPE no começo de seu código fonte.

Exemplo: $APPTYPE CONSOLE

$TYPECHECK[ OFF | ON ]
O padrão é OFF (desligado). $TYPECHECK é usado para dizer ao compilador se você quer o código de fonte analisado gramaticalmente usando o tipo estrito que verifica [ON] (todas as variáveis precisam ser declaradas), ou o tipo liberado  [OFF] como no BASIC tradicional onde você não necessita declarar suas variáveis.
Nota: Você pode alterar para [ON] ou [OFF] a qualquer momento, assim você pode escrever uma parcela de seu código com a diretiva ligada e desligá-la mais adiante.

Exemplo: $TYPECHECK ON

$INCLUDE [ FileName (nome do arquivo) ]
$INCLUDE tem o mesmo efeito que inserir um arquivo diretamente em seu programa no ponto da chamada. Você pode usar $INCLUDE em qualquer ponto de seu programa, exceto em uma SUB (subrotina) ou em uma FUNCTION (função), mas é mais frequentemente incluído no começo do código fonte. Quando colocamos o nome do arquivo entre aspas (" ") o compilador procurará por esse arquivo no diretório onde está o programa, ou pode-se especificar um outro local. Se colocarmos o nome do arquivo no meio de indicadores (< >) o compilador procurará o arquivo apenas no diretório corrente.

Exemplo: $INCLUDE "RAPIDQ.INC"
                   $INCLUDE "C:\RAPIDQ\RAPIDQ.INC"
                   $INCLUDE

$RESOURCE Handle as FileName
A maioria das linguagens de programação do Windows incluem suporte a arquivos de recursos (Resource Files). Em Rapid-Q, isto é um tanto diferente. Em Rapid-Q, os arquivos de recursos são na verdade uma coleção de arquivos de gráficos. Não há nada como recursos de sequências de caracteres (strings) em Rapid-Q. Em todo o caso, o bom sobre arquivos de recursos é que são encaixado em seus aplicativos. Assim se você estiver usando 10 bitmaps diferentes, você não necessita inclui-los em sua distribuição ZIP porque eles são encaixados diretamente no EXE.
Colocando o nome do arquivo entre aspas, ele será procurado no diretório do aplicativo, ou em um outro local especificado. Colocando o nome do arquivo no meio de indicadores (< >) ele será procurado apenas no diretório corrente.

Exemplo: $RESOURCE RES_BMP1 AS "CLOUDS.BMP"
                   $RESOURCE RES_BMP1 AS "C:\RAPIDQ\CLOUDS.BMP"
                   $RESOURCE RES_BMP1 AS <CLOUDS.BMP>


$DEFINE NewDefinition [ OldDefinition ]
Quando você tem necessidade de redefinir ou criar Palavras-Chaves/símbolos (tokens) novos, use esta diretiva. Você não pode criar uma série de símbolos a serem redefinidos.Você não pode redefinir operadores ou pontuações. $DEFINE  não pode ser usado para definir uma função (como e m C). O parâmetro de OldDefinition é opcional, assim você pode fazer isso assim:
$DEFINE NEWDEF

Exemplo: $DEFINE INT16 SHORT
                   $UNDEF DefName [, DefName ]


Para desfazer uma definição previamente chamada por $DEFINE, use $UNDEF. Você pode especificar várias instâncias na mesma linha,; separadas por virgulas.
Exemplo: $UNDEF INT16, INT32

$IFDEF Definition 
Esta diretiva é usada para especificar se uma parte de código deve ou não deve ser compilada. É usada conjuntamente com $DEFINE. Se Definition tiver sido definida, o código dentro da indicação de $IFDEF..$ENDIF será compilado, se não é pulado inteiramente.

Exemplo:
$IFDEF INT16     ' se INT16 tiver sido definido compilar esta porção do código
                  $ELSE                 
' se INT16 não tiver sido definido compilar esta porção do código
                  $ENDIF

$IFNDEF Definition
$IFNDEF tem o efeito oposto a $IFDEF.

Exemplo: $IFNDEF INT16    ' se INT16 não tiver sido definido compilar esta porção do código
                    $ELSE                  
' se INT16 tiver sido definido compilar esta porção do código
                    $ENDIF

$OPTION OptionName [ parâmetros ]
BYREF, BYTECODE, DECIMAL, DIM, EXPLICIT, GTK, ICON, INKEY$, e WEAKTYPE são OptionNames válidos.

Exemplo: $OPTION BYREF                    - Muda a maneira que as variáveis são passadas.Por padrão as variáveis do Rapid-q são passadas
                                                                         por valor. Esta opção inverte isso. Use com cuidado.
                
$OPTION BYTECODE             - Compile como “byte code” somente.
                
$OPTION DECIMAL  ","
                
$OPTION DECIMAL 64            - Isto muda o caractere decimal padrão para o uso em VAL. O caractere decimal padrão é o ponto (.).
                 
$OPTION DIM BYTE
                 $OPTION DIM WORD
                 $OPTION DIM DWORD
                 $OPTION DIM SHORT
                 $OPTION DIM INTEGER
                 $OPTION DIM LONG
                 $OPTION DIM SINGLE
                 $OPTION DIM DOUBLE
                 $OPTION DIM STRING
                 $OPTION DIM VARIANTE
        - Isto muda o tipo de padrão para uma variável não declarada. Por padrão, todas as variáveis não
                                                                         declarada são tidas como do tipo DOUBLE se nenhum sufixo for fornecido.
                
$OPTION EXPLICIT                  - O mesmo que usar TYPECHECK ON. Mantido para compatibilidade com VB.
                
$OPTION GTK                           - Use GTK em vez de XFORMS Certifique-se de ter os arquivos apropriados.
                
$OPTION ICON "path\file.ico"  - Isto muda o ícone padrão do arquivo executável.
                
$OPTION INKEY$ DEFAULT
                
$OPTION INKEY$ TRAPALL   - Isto permite que INKEY$ aceite/rejeite a determinados caracteres extendidos. TRAPALL pegará
                                                                        Shift/Ctrl/Alt/Menu assim como Caps/Num/Scroll lock.
                
$OPTION VBDLL ON
                
$OPTION VBDLL OFF              - Estes determinam como as DLLs são manuseadas. Se VBDLL estiver (ON), suas declarações
                                                                         DLLs em Rapid-Q imitam o VB e seu código pode ser usado da mesma maneira em ambas as
                                                                         linguagens. Esta opção está desativada por padrão.
                
$OPTION WEAKTYPE ON
                
$OPTION WEAKTYPE OFF     - O WeakType é desabilitado (OFF) por  padrão. WeakType permite analisar gramaticalmente mais
                                                                         rápido e pode ajudar suportando algum código VB. Não se deve geralmente usar, a menos que se
                                                                         saiba que código é suportado.

$OPTIMIZE [OFF| ON ] por padrão está OFF (desativado)
$OPTIMIZE pode ser usado para reduzir o tamanho do “byte code” eliminand o instruções desnecessárias. Se você usar esta opção, certifique-se de colocá-la na primeira linha do aplicativo.

Exemplo: $OPTIMIZE ON

$ESCAPECHARS [OFF | ON ]  O padrão é OFF (desativado)
Se $ESCAPECHARS for ativado (ON), você poderá usar seqüências de escape em suas séries de caracteres (strings). As; seqüências de escape podem ser caracteres ou escapes numéricos. Note que maiúsculas/minúsculas faz diferença.
Exemplo: $ESCAPECHARS ON

String de escape       Detalhes
      \a                                Alarm Bell
       \b                               Backspace
       \f                                Form feed ]      
       \n                               New line
       \r                                Carriage return
       \t                                Horizontal Tab
       \v                               Vertical Tab
       \\                                Backslash
       \”                                Double quote       
       \###                          ### é qualquer número de 0 a 255       
       \ xHH                         HH é um valor hexadecimal de 00 a FF

Exemplos: PRINT "\" "                                            A saída é "
                     PRINT "\x41"                                       
A saída é A
                     PRINT “\t\65\66\67\t\68\69\70           
A saída é ABC           DEF
                     PRINT "Hey\r\n";                                 
A saída é Hey com nova linha

$MACRO MacroName[(Parâmetros...) ] MacroDefinition
Uma macro simplesmente substitui uma definição por outra. Pode ser tratada como uma função se você fornecer parâmetros. O Rapid-Q  permite que macros sejam embutidas dentro de outras macros (apenas evite a recursividade), um símbolo seja copiado usando ##, e até nesmo sobrecarga de macro. Você pode também redefinir operadores e caracteres especiais exceto aspas. Uma definição pode estender-se a múltiplas linhas usando os dois pontos como um separador. Uma diretiva $MACRO é global se preceder quaisquer diretivas $INCLUDE. Isto é os arquivos incluídos têm acesso àquelas definições macro. Uma diretiva $MACRO afeta somente o nível do módulo (isto é o arquivo atual) se usado em um arquivo $INCLUDE. Assim se A depender de B e B tiver alguma diretiva $MACRO, então aquelas diretivas $MACRO afetam somente B, e A não tem nenhum acesso a elas a menos que seja redeclarada no módulo A.
Notas: Se você usar a diretiva $MACRO, você estará forçando o Rapid-Q a preprocessar seu código, o que prolongará o processo da compilação. O Rapid-Q executa normalmente um processo de passo-único de compilação.

Exemplo:  $MACRO ~,                                - Redefine o caractere vírgula. Tente: ? STRING$(10 ~ "A")
                   
$MACRO strcat(a, b) a=a+b     - Implementa uma função STRCAT strcat(a$, "abc" ) traduz para a$=a$+"abc"
                  
  $MACRO VARID(x) V##x         - Um exemplo de símbolo que copía DEFINT VARID(1) traduz para DEFINT V1
                   
$MACRO TWO_PI (2*PI) $MACRO PI 3.14159  - Macros embutidas (à frente somente).  Não podem ser encaixadas como um
                                                                            parâmetro. Isto evita chamar a si mesma & e ser recursiva.
                  
$MACRO ADD(a, b, c, d) ADD(a, b)+ADD(c, d) $MACRO ADD(x, y) x+y $MACRO ADD(x, y, z) x+y+z    - Um exemplo de sobrecar-
                                                                            regar macro

3.2 Variáveis e Atribuições

As variáveis são armazenadas na memória principal, e permanecem lá enquanto seu programa estiver rodando. Em; Rapid-Q, a verificação
da digitação é muito liberal. Isto significa que você pode atribuir uma sequência de caracteres (string) a uma variável inteira (integer). O resultado é indefinido, mas muito provávelmente o retorno será 0. O mesmo ocorre com a atribuição de um número a uma variável string, o resultado é uma string vazia. A verificação é feita realmente ao nível do interpretador do Rapid-Q, mas provavelmente você não quer mensagens de erro aparecendo em toda parte, assim desabilitei a verificação inteiramente. O que isto significa finalmente é que você deve ter muito mais cuidado em atribuir valores a suas variáveis, deste modo você não necessitará de nenhum aviso do Rapid-Q. A única verificação feita é estritamente em QObjects. Você não pode atribuir um número ou uma sequência de caracteres (string) a uma variável que espera um objeto como seu valor. Alguns exemplos de atribuições válidas: A% = "Alô" > "Mundo"      A$ = "Alô  Mundo!"   A # = 34 + 34 - 324 * 3/(34/5 + 5)
Se você nunca programou em BASIC antes, está querendo saber provavelmente o que todos esses símbolos após o A significam. Bem, desde que a maioria das implementações em BASIC não o obrigam a definir suas variáveis antes que você as use, você pode defini-las usando os seguintes símbolos: ?,  ??,  ???,  %,  &, !,  #,  $. Estes representam BYTE, WORD, DWORD, SHORT, LONG ou INTEGER,  SINGLE,  DOUBLE, e STRING, respectivamente. Naturalmente, se você não puser nenhum símbolo na frente de sua variável não declarada, supõe-se automaticamente que a variável é um DOUBLE. Essa é a implementação usada em Rapid-Q. Em outras linguagens, padrão talvez seja SINGLE, LONG, ou outro. Eis aqui uma tabela para sua apreciação:

Digite               Identificação    Tamanho           Faixa 
Byte                             ?                      1                     0...255
Word                        ??                       2                     0...65535
Dword                    ???                       4                    somente Linux...
Short                          %                      2                    -32768..32767
Integer                        &                      4                    -2147483648..2147483647            
Long                           &                      4                    -2147483648..2147483647
Single                         !                       4                    1.5 x 1045..3.4 x 1038           
Double                       #                       8                    5.0 x 10324..1.7 x 10308

Se você não gostar dessa maneira de fazer a coisa, você poderá também usar DIM para declarar suas variáveis, assim:
DIM Number AS INTEGER                      DIM S AS STRING                        DIM B AS BYTE

Bem, você escolhe. Se você ativar $TYPECHECK ON, você será forçado então a declarar suas variáveis como acima. Seria um bom hábito escolher esse modo, especialmente se você quiser migrar para as linguagens C ou Pascal. Você será forçado a declarar todas as suas variáveis antes que as use. Aqui está algo que é válido em Rapid-q, mas que pode confundir a muitos: DIM Num$ AS INTEGER    Sim,  evite uma situação confusa como esta.

3.3 Componentes/Objetos  Rapid-Q

Rapid-Q oferece componentes (ou objetos) reutilizáveis o que ajuda o programador a desenvolver aplicativos no Windows. A maioria das linguagens de programação RAD (desenvolvimento rápido de aplicativo) tambem os oferecem. Para C++ visual, são MFC, para Java,; são JFC Swing. Assim, que são componenntes visuais? São, o código de um envoltório que implementa todas as características sob o controle da API do Windows (ou sob o Linux, qualquer GUI API  que você esteja usando) sem todas as dores de cabeça envolvidas em usar a API diretamente. Por exemplo, você pode criar janelas usando chamadas diretas da API do Windows somente, mas isto pode não funcionar bem. Em Rapid-Q, tudo isso foi englobado em um componente chamado QFORM. Assim em vez de ter que primeiramente registar sua classe janela, mostre-a e manuseie então todas as chamadas, tudo que você precisa fazer em Rapid-Q é o seguinte:

DIM Form AS QFORM
Form.ShowModal


Comparando isso a um programa em C/C++, é muito mais limpo, e mais fácil de compreender. Há muitos componentes suportados pelo Rapid-Q, como por exemplo: QBUTTON, QIMAGE, QFILESTREAM, etc.. Há também alguns que não são suportadas, esta é uma limitação envolvida em se usar o Rapid-Q. Os componentes podem ser criados exatamente da mesma maneira que você cria variáveis, usando DIM. Cada componente tem suas próprias propriedades, métodos e eventos. As propriedades são como os atributos do componente. Por exemplo, Left, Top, Width, e Height são propriedades comuns a todos os  controles visíveis, que definem sua colocação dentro do form ou da janela. Caption é uma propriedade do tipo STRING. Para um componente QFORM, a propriedade Caption define o título desse form. Caption para um QBUTTON define o texto desse botão. Caption para alguns objetos como QFILESTREAM não existe, visto que um QFILESTREAM  não é um componente visível. Ele tem suas próprias propriedades. Para ver um guia completa das propriedades, os métodos e os eventos para todos os componentes, verifique por favor a seção do apêndice. Aqui vai um exemplo rápido de como você pode atribuir propriedades a
seus componentes:

DIM Form AS QFORM                                                                                                                
Form.Caption = "Meu aplicativo"                                                                                 
Form.Left = 100                                                                                                                  
Form.Top = 100


Procure saber que propriedades são de apenas-leitura, e quais são de apenas-escrita. Um exemplo de uma propriedade de apenas-leitura é a propriedade ITEMCOUNT de um QLISTBOX. Um exemplo de uma propriedade apenas-escrita é a propriedade ICON de um QFORM, usado para especificar a posição de um ícone a ser usado como padrão. Ler os valores de apenas-escrita, ou escrever aos valores apenas-leitura não têm nenhum sentido, e pode resultar em um erro de compilação ou mais sério, seu programa deixar de funcionar.

3.4 Componentes Métodos & Eventos

Bem, até aqui consideramos somente as propriedades, mas os métodos não são muito diferentes. Que é exatamente um método? É realmente uma SUB-ROTINA ou uma FUNÇÃO. É chamado método, porque a SUB-ROTINA ou a FUNÇÃO refere-se somente a esse componente. Isto é mais difícil de explicar do que compreender. Aqui vai um exemplo de um método:

DIM Form AS QFORM
Form.Center
Form.ShowModal


Nós usamos 2 métodos aqui, Center, e ShowModal. O método Center é uma SUB-ROTINA que não aceita nenhum parâmetro. O que faz é centralizar o form em seu desktop (área de trabalho). ShowModal é realmente uma FUNÇÃO, mas nós ignoramos o valor do retorno neste caso. É um método usado  para  mostrar o seu form, e ficar esperando até o usuário fechá-lo.
Nota:
Ao contrário de algumas execuções do BASIC, uma FUNÇÃO pode ser chamada como uma SUB-ROTINA em Rapid-Q. Você apenas ignora
o valor do retorno. Isso é similar em C/C++.
Outros métodos podem requerer alguns parâmetros extra, e algum aceitam mesmo um número infinito de parâmetros. Você pode também escrever estes em Rapid-Q , mas cobriremos esse tópico mais tarde. Assim bàsicamente, os métodos são as funções que executam uma
tarefa específica nesse componente. Em nosso exemplo acima, nós estamos centrando o formulário, e estamos mostrando-o. Ok, agora sobre eventos. Bem, este pode ser um assunto confuso se estivessemos falando em termos de mensagens passadas e chamadas. Bem, felizmente, nós podemos fazer um exame de um ponto de vista muito mais elevado. Um evento ocorre sempre que algo acontece. Isto é muito vago, algo que acontece poderia ser qualquer coisa num lapso de tempo, o usuário clicando o mouse, o usuário pressionando uma tecla, etc. O Rapid-Q não pode pegar todos estes eventos, mas os mais importantes sim. Por exemplo, se um usuário clicar sobre um botão, você deveria escrever algum código para executar essa tarefa. E teremos de definir uma função de CHAMADA para pegá-la. Para cada evento que ocorrer, você tem que dirigir o evento a uma sub-rotina que você escreveu para manusear o evento. É como aqui:

SUB ButtonClicked     
     PRINT “O botão foi clicado”
END SUB

DIM Button AS QBUTTON
DIM Form AS  QFORM
Button.Parent = Form                  
'Propriedade
Button.onClick = ButtonClicked 
'Evento
Form.ShowModal                        
'Método

Parece fácil e é realmente. Sempre que um "OnClick" mensagem/evento ocorre, o programa salta para a sub-rotina ButtonClicked e executa o código que você colocou dentro dela. O programa acima está completo, pode tentar executá-lo. Para uma lista de todos os eventos que um componente particular pode receber, olhe na seção do apêndice.

3.5 As sub-rotinas & as funções do Rapid-Q

Há alguns segredos associados com as SUBs do Rapid-Q e as FUNCTIONs. O primeiro, e o mais importante, é que todos os valores passados são passados como VALOR (à exceção dos objetos, das variants, das UDTs, e ARRAYs). Para passar uma variável como referência, você necessita iniciá-la com o símbolo @. Em segundo lugar é que somente um máximo de 10 parâmetros pode ser aceito (exceto ao chamar uma função da API, é limitada então a  25). Não há nenhuma diferença na maneira que você cría SUBs ou FUNCTIONs em Rápid-Q (em comparação a outras linguagens BASIC como QBasic/PB):

FUNCTION FindMax (X AS INTEGER, Y AS INTEGER) AS INTEGER    
       IF X > Y  THEN         
             FindMax = X   
'O valor do retorno é X     
       ELSE         
             FindMax = Y    
'O valor de retorno é Y      
       END IF
END FUNCTION

O código acima é válido, como é o seguinte:

FUNCTION FindMax (X%, Y%) AS INTEGER     
       IF X% > Y% THEN         
              FindMax =  x%     
'O valor do retorno é X     
       ELSE          
              FindMax  = Y%   
'O valor do retorno é Y     
       END IF
END FUNCTION 


Entretanto, como pode observar, você não pode ter FindMax% sem juntar AS INTEGER no fim da função. Para passar uma variável como referência, coloque o símbolo @ na frente de sua variável:

SUB StrCat (Source AS STRING, Text AS STRING)
        Source =  Source + Text
END SUB

A$ = "Alô"
StrCat(@A$, " Mundo!")
PRINT A$
' - - deve imprimir: Alô Mundo!

Ou se você preferir, você pode também unir uma palavra-chave BYREF em sua lista de parâmetros assim:

SUB StrCat (BYREF Source AS STRING, Text AS STRING)     
        Source = Source + Text
END SUB

A$ = "Alô"
StrCat(A$, " Mundo!")
PRINT A$
' - - deve imprimir: Alô Mundo! 

Contudo uma outra maneira que você pode usar (note que esta é a maneira "antiga", mantida por razões de compatibilidade):

SUB Strcat  (A$, B$)     
       A$ = A$ + B$
end sub

A$ = "Alô"
Strcat(A$, " mundo!")
A$ = STACK.STR(0)
PRINT A$


A pilha contem um array de inteiros e strings, para alcançar o parâmetro correto, você tem que especificar o número (da esquerda para a direita). O primeiro elemento sendo 0. Você pode também passar QObjects, mas você não pode usá-lo como um valor do retorno.

SUB(Button AS QButton)
       Button.Left = 100
END SUB


Todos os componentes QObject são passados por referência.

3.6 Regras de Escopo

O Rapid-Q não é como o BASIC neste caso.Visto que Rapid-Q é um compilador de passo-único simples, o escopo é muito similar ao do Pascal. Eis aqui o que significa:

SUB Teste     
       DIM I AS INTEGER    
        I = 100     
        PRINT I
END SUB

DIM I AS INTEGER
I = 10

SUB Test2     
       PRINT I
END SUB


Teste 'Chama a Sub-rotina Test2
Aqui está como o escopo trabalha, para a SUB Test, a variável LOCAL I se estende somente para baixo até a extremidade desse bloco SUB. A variável I no programa principal se estende para baixo até o fim do programa. Isso significa que a SUB Test2 pode usar a variável global I porque está dentro de seu espaço. Você pode também redeclarar a variável I na SUB Test2, que no caso a local I será usada em vez da I global. Aqui vai uma outra situação, para demonstrar o escopo de nomes variáveis:

DIM I AS INTEGER

SUB Escopo (I AS INTEGER)     
       DIM I AS INTEGER    
       PRINT I
END SUB

PRINT I


De fato, a linha DIM I AS INTEGER é uma instrução desperdiçada, visto que a variável I é sobreposta pelo nome do parâmetro I. O Rapid-Q o advertirá sobre isto, mas não mostrará erro.


Os conceitos básicos de forms (formulários) são introduzidos neste capítulo. Usaremos os termos form (formulários) e window (janela) como intercambiáveis.

5.1 O que é um form?

Um form (ou a janela), é um recipiente genérico que pode ser mostrado em seu desktop. É como um painel (veja QPANEL). Um form pode ter muitos aspectos. Em Rapid-Q, os únicos forms que são válidos, são (você pode igualmente definir shapes [figuras] feitas pelo usuário para seus forms, usando o método SHAPEFORM):

0 = bsNone, 1 = bsSingle, 2 = bsSizeable, 3 = bsDialog, 4 = bsToolWindow, 5 = bsSizeToolWindow.

O form padrão é de tamanho redimensionável ou (Sizeable) significando que clicando na borda você pode maximizar o form usando tela cheia ou ajustar para qualquer tamanho. Em Rapid-Q puro seria escrito: 'Form.BorderStyle = 2'. Pode porém ser mudado para (Single) tamanho fixo com borda, assim:

'Form.BorderStyle = 1'.

Há no entanto 6 opções para a propriedade BorderStyle e seria um pouco difícil memorizar a função de cada numero. Para facilitar a programação usa-se o arquivo 'Rapidq.inc' (veja exemplo abaixo) que nos permite usar descrições curtas como 'bsSizeable' em vez de 2 e 'bsSingle' em vez de 1.

         
$INCLUDE "RAPIDQ.INC"
          DIM Form AS QFORM
          Form.BorderStyle = bsDialog
          Form.ShowModal


Usando bsSingle, o form ficará exatamente igual a bsSizeable, exceto que você não poderá redimensionar o form. Em muitos casos, as únicas duas opções úteis são bsSizeable, e bsDialog.

5.2 Adicionando componentes a um form

Você pode adicionar componentes ao form simplesmente atribuindo o nome do form à propriedade 'parent' desse componente.

       
DIM Form AS QForm
        DIM Button AS QButton

       Button.Parent = Form      
'Adiciona um botão ao form

Se estiver querendo saber porque isso é assim, apenas considere um programa com multiplos forms. Todos os componentes visíveis devem ter uma propriedade 'parent'. Os componentes, tais como QMENUITEM, ou QTIMER não têm uma propriedade 'parent'. Embora QMENU- ITEM seja considerado um componente visível, seu 'parent' pode somente ser um QMAINMENU ou um QPOPUPMENU. Mas em vez de atribuir uma propriedade 'parent', você tem que adicionar ou inserir os itens no menu. Se nenhum 'parent' é atribuido, o componente permanece invisível. No caso de QCANVAS ou de QIMAGE, você obterá mensagens de erro se tentar desenhar nesses componentes. Isto é porque você não pode desenhar em algo não visível. Na maioria dos casos, a propriedade 'parent' deve ser a primeira propriedade que você atribui antes de fazer qualquer outra coisa. Para esconder componentes, você pode usar a propriedade 'visible = false' em vez de eliminar a propriedade 'parent'.

5.3 Seguindo a posição do mouse sobre um form

Você pode usar MouseX e MouseY para detectar a posição do mouse sobre um form. Pode, em vez disso ser utilizado o evento OnMouseMove que fornecerá as coordenadas de X e de Y da posição do mouse.

5.4 Eventos especiais para forms

Há alguns eventos especiais que somente os forms podem utilizar. O mais importante é o evento OnResize. Isto ocorre sempre que o usuário
clicar no botão maximizar ou redimensiona manualmente seu form. Seu programa deve quase sempre utilizar este evento e atualizar o tamanho de seus controles apropriadamente. Imagine que a janela do seu Browser (Navegador) seja 640x480, e quando você redimensiona o aplicativo para 1024x768, a janela do navegador continua sendo 640x480. Fica feio. Se você não quer que ninguém redimensione seu form, use um BorderStyle diferente como mencionado mais acima. Um outro evento especial é onKeyPress. É melhor usar OnKeyDown em vez de OnKeyPress. A diferença é que OnKeyDown pode detectar teclas estendidas, e pode detectar o estados do SHIFT, do Alt, e do CTRL. Se sua aplicação não precisa detectar teclas estendidas, então poderá usar OnKeyPress preferivelmente. A manipulação do teclado é útil para muitas aplicações que a exigem, como um treinador de dactilografia, jogos, etc…. O que merece nossa atenção são os parâmetros de retorno de OnKeyPress e de OnKeyDown que podem ser usados. É diferente do que esperaríamos desse evento. Veja um eventos simples que não exige um processamento especial.

     
SUB Button01Click
           '' Preenchimento
      END SUB

      Button01.OnClick = Button01Click


Um clique simples passa somente uma mensagem simples para o nosso botão, dizendo-lhe que um clique foi recebido. Bem, com OnKeyPress é mais  complicado. Não é suficiente dizer que uma tela foi pressionada, nós temos que saber que tecla foi pressionada, assim temos que pegar esta informação extra.

     
SUB KeyPressed(Key AS WORD)
          '' Preenchimento
          PRINT Key
      END SUB

      Form.OnKeyPress = KeyPressed


A tecla pressionada é informada na variável Key. Você pode nomeá-la de qualquer coisa que você queira, mas sua SUB deve ter pelo menos esse parâmetro. O Rapid-Q; não reclama se você adicionar mais parâmetros, nem se queixa-se se você não suprir algum parâmetro. Fique alerta. Para OnKeyDown, há dois parâmetros que serão passados:

     
SUB KeyDown(Key AS WORD, Shift AS INTEGER)
          '' Preenchimento
          PRINT Key;" ";Shift
      END SUB

      Form.OnKeyDown = KeyDown


Há 3 tipos de shift (deslocamento), ShiftDown, AltDown, e CtrlDown associados respectivamente às teclas Shift, Alt e Ctrl. Seja cuidadoso com o ShiftDown. Ao contrário de OnKeyPress que detecta todo o estado Shiftdown, o OnKeyDown não. Isto significa que se você pressionar SHIFT+i você obterá o código da letra maiúscula da tecla.
Usando o OnKeyDown o código da tecla virtual é retornado, que não é um código de 2 bytes. A tecla da seta; ascendente = 38, tecla da seta à esquerda = 37, tecla da seta à direita = 39, e tecla da seta para baixo = 40

5.5 Caixas de diálogo

Um tipo especial de form é a caixa de diálogo simples. As caixas de diálogo são geralmente interfaces para o seu form principal. Você geralmente não implementa uma caixa de diálogo como seu form principal. Por exemplo, considere um form principal onde você tem 3 escolhas:

Para cada escolha (exceto talvez para a última), você deve criar caixas de diálogo para pegar dados do usuário. Quando o usuário clicar sobre a primeira tecla, uma caixa de diálogo como esta deve aparecer:

As belas teclas são botões adaptados. Veja RAPIDQ.INC para todos os valores.

    
OKButton1.Kind = bkOK
     CancelButton1.Kind = bkCancel


Muito bem, mas como você sabe se o usuário pressionou OK ou CANCEL? Há realmente duas maneiras de fazer isto, ambas envolvem usar a propriedade de ModalResult. Recorde como você chamou sua caixa de diálogo:

  
IF Dialog1.ShowModal = mrOK THEN
      '' Usuário pressionou OK
   ELSE
       '' Usuário pressionou Cancel
   END IF


O ModalResult é uma propriedade tanto de QButtons como de QForms. Quando escolhemos o tipo do primeiro botão "OK", fizemos :

    
OKButton1.Kind = bkOK

E automaticamente o seguinte:

    
OKButton1.ModalResult = mrOK

O que isto significa é que sempre que você pressiona o botão "OK", o ModalResult retornado é mrOK. Este resultado fecha automaticamente sua Caixa de Diálogo e Dialog1 também. Uma outra maneira menos elegante é apenas atribuir o ModalResult a seu form. Por exemplo, vamos supor este cenário:

     
SUB ButtonClick
            Dialog1.ModalResult = mrOK
      END SUB

      OKButton1.ModalResult = mrNone '' No result
      OKButton1.OnClick = ButtonClick

Na cenário acima, clicar no botão transferirá o controle para a SUB de ButtonClick. Nesta SUB, a propriedade ModalResult da caixa Dialog1 é ajustada, o que fará exatamente o mesmo que o nosso exemplo precedente.

5.6 Usando o método CREATE (Criação)

Para criar componentes/objetos na maioria dos casos, usa-se CREATE em vez de DIM, isto é porque se economiza muito trabalho de; digitação, e é fornecida uma vista mais limpa de seu código fonte. Vamos observar quanta digitação se poderia economizar:

       
DIM MainForm AS QForm
        MainForm.Left = 100
        MainForm.Top = 50
        MainForm.Height = 300
        MainForm.Width = 400
        MainForm.Caption = "Hello world!"


Veja agora como o código acima ficará usando CREATE:

CREATE MainForm AS QForm
             Left = 100
             Top = 50
             Height = 300
             Width = 400
             Caption = "Hello world!"
END CREATE


Como você pode ver, não é necessário digitar a cada vez o nome MainForm, já que o compilador saberá exatamente o que você quer que seja analisado.

5.7 Aninhando CREATEs

Aí está onde o uso do método CREATE em vez de DIM economiza seu tempo e evita confusão. Isto é o que eu chamo de " aninhamento" de seus controles:

       
CREATE MainForm AS QForm
             Center
             CREATE Button1 AS QButton
                   Left = 10
                  Top = 10
                   Height = 20
                   Width = 20
             END CREATE
             ShowModal
        END CREATE



Você já deve ter observado que ao usarmos Create não necessitamos usar a propriedade Parent. Isto é porque o compilador entende que você quer incluir um Button ao MainForm (devido ao CREATE aninhado), assim ele gera automaticamente o Button1.Parent = MainForm para você. Como você pode igualmente ver, isso tem aparência muito melhor, e você pode visualmente observar que componentes pertencem a que form ou outro recipiente. No Rapid-q podemos armazenar até 25 camadas, no exemplo acima, nós precisamos somente de uma camada. Não acredito que alguém necessite de mais de 4 ou 5 camadas (no máximo), mas eu deixei 25 como o máximo apenas para  satisfazer o 1% que o queira realmente. Certamente você entenderá porque componentes sem a propriedade Parent (componentes não visíveis) são inválidos se aninhados no CREATE.

5.8 Criando menus

Criar menus usando o método CREATE não é diferente:

       
CREATE MainForm AS QForm
               Center
               CREATE MainMenu AS QMainMenu
                       CREATE FileMenu AS QMenuItem
                              Caption = "&File"
                              CREATE OpenItem AS QMenuItem
                                     Caption = "&Open"
                              END CREATE
                              CREATE SaveItem AS QMenuItem
                                      Caption = "&Save"
                              END CREATE
                       END CREATE
              END CREATE
              ShowModal
        END CREATE



Em vez da propriedade Parent, neste caso nos poupamos de usar AddItems. Isso é provavelmente mais fácil de compreender do que a utilização de DIM para definir todos os seus menus.

Os aplicativos de console são tão fáceis de criar como os aplicativos de Janelas (Forms). Você se sente como que criando aplicativos de texto, apenas compile como CONSOLE em vez de GUI. Como pode ver, é mais fácil aprender os fundamentos do Rapid-Q (ou uma linguagem BASIC em geral) simplesmente  criando aplicativos simples de console e testando suas rotinas. Pode tambem utilizá-los como uma ferramenta de debug (procura de erros) para seus aplicativos GUI.

6.1 Vista geral de funções de console

Um aplicativo de console é uma aplicação de texto capaz de escrever e de aceitar a entrada de dados pelo console. Um aplicativo de console, em Rapid-q consiste em uma tela visível, e em 7 buffers (memória) de tela. Aquí vai uma vista geral de algumas funções do console:

  
CLS    - Limpa a tela do console
  
PRINT  - Escreve na tela de saída (visível/fora da vista)
 
  INPUT  - Aceita entrada de dados do usuário
  
PCOPY  - Usada para copiar de, e para os buffers de tela
  
LOCATE - Posição do cursor
  
POS(0) - Pega a coluna atual sob o cursor
  
CSRLIN - Pega a linha atual sob o cursor
  
COLOR  - Troca os atributos de cor do texto
  
POKE   - Põe atributo ou caracter na tela
  
PEEK   - Pega atributo ou caracter da tela
  
SLEEP  - Retardo de tempo
  
INKEY$ - Checa teclado sem bloquear

Estas funções são similares às que você teria no QBasic. O PEEK e o POKE operam da mesma maneira, SLEEP é mais preciso do que no QBasic (IE. você pode especificar uma fração de segundo, como no PowerBASIC). Observe que você não precisa de uma instrução DEF SEG, desde que esteja no Windows.

6.2 Exemplo: "Alô mundo!"

O Rapid-q é como a maioria das linguagens BASIC, você pode criar um programa "Alô mundo" em apenas uma linha:

PRINT "Hello World!"

Compile e execute, você criou seu primeiro aplicativo de console para Windows! Vamos ocupar-nos com algo mais interessante, copiando dos, e para os buffers de tela.

  
CLS
   PRINT "Esta é a página 1"

   PCOPY 0, 1   
'-- Copia da visível pág. 0 para a fora da vista pág. 1

   CLS
   PRINT "Pressione uma tecla para ver a página 1"

   DO:LOOP UNTIL INKEY$<>""

   PCOPY 1, 0  
  '-- Copy off screen page 1, to visible page 0

Como você pode ver, nossa página visível é sempre 0. O numero das páginas apresentáveis é de 1-7. PCOPY apenas copia o conteúdo de um buffer e o coloca em outro. Você pode copiar um buffer de tela para outro assim:

   
PCOPY 1,5

O conteúdo do buffer 1, é copiado para o buffer 5. Nada de novo é introduzido aqui que você não encontre no QBasic. Uma característica nova é que você pode agora escrever numa pagina em offscreen (fora da vista)

  
PRINT #1, "Olá! mundo!"       '-- Escreve na página 1

O exemplo acima escreverá a string (sequência de caracteres) "Olá! mundo! " na página 1 que está fora da vista. Certifique-se de usar o sinal '#' para o '1' ser tratado como um número. Para ver o que escreveu, use PCOPY 1, 0.
LOCATE e COLOR tem o mesmo efeito nas páginas em offscreen. Você pode igualmente dar PEEK e POKE nas páginas offscreen, apenas forneça um parâmetro adicional, o primeiro a ser escrito é o número da página escrita/lida.

6.3 Usando PEEK e POKE

Se você nunca usou PEEK e POKE antes, não é problema. Em Rapid-q, o PEEK e POKE  têm a mesma funcionalidade do QBasic (à exceção da característica adicional de PEEK/POKE para uma página offscreen). A faixa de endereços é de 0 a 3999. Isto é porque o tamanho de tela é restrito a 80x25 em Rapid-q. Mas se 80x25 = 2000, para que são os 2000 extra?

   
POKE 0, ASC("A")
    POKE 1, 15


Se você rodar este código, você terá uma letra branca 'A' mostrada no canto superior esquerdo de seu console. Uma coisa que você deve notar é que um "byte" na tela contem um caractere e seu atributo. Todo o endereço numerado par contém o caractere, todos os endereços numerados ímpares contem o atributo. Se você não sabe exatamente o que é um atributo, ele é apenas uma combinação da cor do primeiro plano e a cor de fundo tudo em um byte (8-bits). Os 4 bites mais baixos são reservados para a cor do primeiro plano, e os 4 bites superiores são reservados para a cor do fundo. Se você sabe qualquer coisa sobre bites e bytes, você pode facilmente calcular o atributo:

   
B? = &H07       '-- 0 = cor de fundo, 7 = cor do caracter

Sim, você pode ter cores do fundo de alta intensidade. Piscar não é suportado.

6.4 Aceitando entrada de dados do usuário

Você pode aceitar entrada de dados do usuário através de indicação de INPUT ou pela função INKEY$. A instrução INPUT do Rapid-q' não é como a INPUT "normal" do Basic que talves lhe seja familiar. É mais como uma LINE INPUT que alertará para uma linha completa de informação e termina somente se o usuário pressionar a tecla ENTER.

  
INPUT "Qual a sua idade? ", Idade

Uma string entre aspas pode seguir uma instrução INPUT, a seguir uma vírgula ou um ponto e vírgula seguido por uma variável. A instrução de entrada armazenará o resultado na variável, string ou numérica. A instrução INPUT permanecerá ativa até que o usuário pressione ENTER. Para entradas de teclado que não permanecem ativas, use INKEY$. É um pouco confuso usá-la pela primeira vez, mas permite uma manipulação personalizada do teclado. Vamos usar o mesmo exemplo acima para demonstrar como isto é feito usando INKEY$. Entretanto, nesta versão, nós podemos personalizar a entrada e filtrar caracteres  não numéricos.

  
PRINT "Qual a sua idade? ";
   AGE$ = ""
   DO
        DO
             A$ = INKEY$
        LOOP UNTIL LEN(A$)
        IF A$ = CHR$(13) THEN EXIT DO
        IF A$ >= "0" AND A$ <= "9" THEN PRINT A$;:AGE$=AGE$+A$
   LOOP
   PRINT
   PRINT "Você tem "; AGE$; " ano(s)."


Observe que se você pressionar uma tecla não numérica, esse caráter não é reconhecido. Isto é porque nós restringimos a entrada aos caracteres 0...9 somente. Embora o exemplo seja muito mais longo do que o precedente, nós personalizamos nossa entrada usando INKEY$ o qual nos dá mais poder do que INPUT. Uma outra vantagem de usar INKEY$ é que você pode igualmente pegar teclas estendidas.


6.5 Pegando teclas estendidas

As teclas estendidas têm 2 bytes de comprimento, o primeiro byte é um caractere ESC, que pode ser ignorado, e o segundo byte é o código virtual da tecla. Na versão Windows, você pode adicionar a opção para pegar todas as teclas, incluindo SHIFT, CTRL, a tecla CAPS LOCK, NUM LOCK, SCROLL LOCK  e  a  tecla  PopupMenu (se você estiver usando um teclado específico do Windows, não pega a tecla StartMenu, ou outra semelhante). Você pode ligar/desligar esta opção quando você quiser:

  
$OPTION INKEY$ TRAPALL

   'pega todas as teclas incluindo shift, ctrl, caps/num/scroll lock
   'e a PopupMenu 

  
$OPTION INKEY$ DEFAULT

   'pega todas as teclas estendidas exceto as listadas acima

Se você já usou QBasic, você provavelmente sabe os códigos de varredura, assim todos os códigos das teclas virtuais que Rápido-Q encontra são exatamente os mesmos códigos de varredura que você encontra no QBasic. O seguinte código é um exemplo de como pegar/analisar teclas estendidas, particularmente as teclas de seta:

   DO
        DO
             A$ = INKEY$
        LOOP UNTIL LEN(A$)

        IF LEN(A$) = 2 THEN  
'Tecla estendida
             SELECT CASE RIGHT$(A$, 1)
             CASE "P"
                   PRINT "Seta para baixo pressionada."
             CASE "H"
                   PRINT "Seta para cima pressionada."
             CASE "M"
                   PRINT "Seta prá direita pressionada."
             CASE "K"
                   PRINT "Seta prá esquerda pressionada."
             CASE ELSE
                   PRINT "Tecla estendida "; A$
             END SELECT
       END IF

   LOOP UNTIL A$ = CHR$(27)  
'Tecla ESC

Mesmo que você não saiba os códigos de varredura, você pode olhar a saída do exemplo acima para configurá-los.


6.6 Misturando CONSOLE com GUI

Como você observou no exemplo acima, nós misturamos algumas operações de CONSOLE com o GUI. Isto é perfeitamente válido contanto que você compile seu aplicativo como CONSOLE. Sob o linux/Unix há algumas diferenças, por favor veja o guia de compatibilidade para mais informações.
Por exemplo, um aplicativo GUI para linux/Unix pode executar algumas operações de CONSOLE tais como PRINT, INPUT, e o CLS sem ser compilado como CONSOLE.

6.7 O Console linux/Unix


Desde que a maioria dos terminais são diferentes, algumas características de console não funcionarão. Se seu terminal não suporta a cor, nenhuma será mostrada. Você não precisa preocupar-se com esses casos, o Rapid-Q executa toda a verificação de erros para você. O mesmo pode ser dito para teclas estendidas, alguns terminais não suportam certas teclas. Entretanto, a maioria dos terminais suportam as teclas de setas e teclas Page up/down. Você tem a garantia de que quase todas as teclas trabalharão na maioria dos terminais.


O Rapid-Q trabalha com streams (fluxos) de arquivos em vez do comando OPEN padrão usado no BASIC. Tem a mesma funcionalidade, mas é chamado de modo diferente. O que é interessante sobre streams é que eles são ilimitados quanto ao tamanho, e você pode carregar de, e para os streams diferentes arquivos (IE. De arquivo para memória).

7.1 Streams de arquivos

Os streams de arquivos são usados para fazer leitura e escrita de arquivos. Todos os streams são abertos como binários, com a capacidade de não travamento (IE. todas os arquivos abertos são compartilhados).
Familiarize-se com estas modalidades de arquivos:

   
fmCreate    - Cria o arquivo (de leitura/gravação), não é executado se o arquivo existe
   
fmOpenRead  - Abre arquivo  para leitura somente
  
  fmOpenWrite – Abre arquivo para escrita somente
   
 fmOpenReadWrite - Abre arquivo  para leitura e escrita

Para abrir um arquivo você precisa passar o nome do arquivo, e fornecer uma das modalidades de arquivo acima.

    
DIM File AS QFileStream
     File.Open("Test.txt", fmOpenRead)


Isto abrirá o arquivo Test.txt para leitura somente. Para ler dados, você pode usar os métodos ReadNum e ReadStr. Veja; RAPIDQ.INC para saber os números válidos.

    
PRINT File.ReadNum(Num_SINGLE)   '-- Lê 4 bytes como um valor SINGLE
     PRINT File.ReadStr(1024)       
  '-- Lê 1024 bytes

Um caso parecido é armazenar o valor em uma variável, você pode usar as rotinas genéricas Read e Write.

    
File.Read(A&)          ' -- Leia um valor LONG e armazene-o em A&
     File.Write(B#)         
' -- Escreva um valor DOUBLE B#

   
  ' -- ou similarmente, com mais trabalho:

     A& = File.ReadNum(Num_LONG)
     File.WriteNum(B#, Num_DOUBLE)


Após ter usado o arquivo, certifique-se de que você o tenha fechado. Você pode então reutilizar o stream e abrir um outro arquivo. Para testar se você alcançou o final do arquivo, você pode comparar File.Position com o File.Size ou ver se File.EOF é verdadeiro (- 1). Enquanto File.Position for menor do que File.Size, você não alcançou o fim do arquivo.     
Há 2 maneiras de testar se um arquivo existe:

    
IF FileExists("Test.txt") <> FALSE THEN
           File.Open("Test.txt", fmOpenRead)
     END IF


Outra forma de fazer isso, assim:

    
IF File.Open("Test.txt", fmOpenRead) = FALSE THEN
        ShowMessage("Não pode abrir arquivo")
     END IF


File.Open retornará um número diferente de zero se o arquivo foi aberto com sucesso e 0 se não foi.

7.2 Streams da memória

Um stream de memória é criado sempre que você o dimensiona (DIM). É similar ao stream de arquivo, todos os métodos são os mesmos, tais como ReadNum, WriteNum, etc…. A única diferença é que os streams da memória estão armazenados na memória, e que podem crescer/encolher sem limites superiores. É possível armazenar arrays (estruturas de dados) de números num stream de memória (veja o exemplo de ARRAYS.BAS). Os streams de memória servem para muitos propósitos, deixe sua imaginação assumir o comando. Uma finalidade útil pode ser armazenar dados provisórios, tal como copiar de um arquivo para um stream de memória e manipular os dados desta maneira.
Bem, como posso eu copiar de um stream para outro? É muito fácil, você pode fazer copia entre streams; diferentes (IE. De arquivo para memória) ou do mesmo tipo (IE. De memória para memória).

   
DIM File AS QFileStream
    File.Open("test.txt", fmOpenRead)

    DIM Memory AS QMemoryStream
    Memory.CopyFrom(File, File.Size) 
'-- Copie tudo

Como você pode ver, nós apenas copiamos o conteúdo todo de nosso arquivo "test.txt" para nosso stream de memória. Agora você pode manipular seu stream de memória sem afetar o arquivo.

7.3 Salvando/carregando UDTs (Types definidos pelo usuário) e arrays

Salvar e carregar UDTs é tão simples quanto passar UDT para QFileStream ou para QmemoryStream.

         
TYPE Ttest   
                S AS STRING*8                                                                                                                
                N AS INTEGER                                                                
          END TYPE

          DIM Test AS TTest
          DIM File AS QFileStream

          File.Open("test.txt", fmCreate)
          File.WriteUDT(Test)
          File.Close

No exemplo acima, 12 bytes foram gravados no arquivo test.txt. 8 bytes na string S, e 4 bytes na varável N. Para recuperar uma UDT, use o método ReadUDT.

         
File.Open("test.txt", fmOpenRead)
          File.ReadUDT(Test)


  Salvar/carregar é ingualmente fácil.

       
DIM A(1 TO 100) AS LONG
        DIM File AS QFileStream

        File.Open("test.txt", fmCreate)
        File.SaveArray(A(1), 100)
        File.Close

O primeiro parâmetro de SaveArray é o elemento inicial do array a salvar, este pode ser qualquer valor de 1 a 100 no exemplo acima. O parâmetro seguinte especifica quantos elementos do array serão salvos. Neste caso nós escolhemos salvar todos os 1..100 elementos. Similarmente, nós podemos usar LoadArray para recuperar nossos dados:

       
File.Open("test.txt", fmOpenRead)
        File.LoadArray(A(1), 100)


Os streams são muito populares na maioria das linguagens de alto nível para a manipulação de arquivos/memória, mas tomam algum tempo para nos habituarmos com eles especialmente se você é um usuário obstinado do BASIC.

A maior parte das linguagens de programação Windows oferecem suporte para .RES (arquivo de recurso). Um arquivo Recurso é nada mais do que uma coleção de imagens (ou recurso de strings e dados). Inicialmente, você aprenderá sobre listas de Imagens, como embutir os recursos em seu executável, e como usá-los devidamente em seus aplicativos.

8.1 RECURSOS suportados

Rapid.q apenas suporta "diretamente" recursos de imagem, ie. Ícones e Bitmaps. E daí, qual exatamente é o diferença entre carregar uma imagem em tempo real, ou carregar um recurso? Bem, quando você declara um apelido de um recurso, o arquivo de imagem fica automaticamente embutido dentro do seu executável. Aí vai um exemplo:

    
$RESOURCE ICO_TEST AS "C:\ICONS\TEST.ICO"
     DIM Form AS QForm

     Form.ICOHandle = ICO_TEST  
'Form.Icon = "C:\ICONS\TEST.ICO"

O arquivo C:\ICONS\TEST.ICO fica embutido dentro do seu executável, o que significa que você não necessita incluir TEST.ICO em sua distribuição com o programa .EXE. O Rapid.Q não gera arquivos .RES, e não roda arquivos .RES de outros. A maioria das linguagens têm um arquivo RES, mas você não pode usá-los em Rapid-Q, você terá que extrair as imagens deles, e usar a diretiva $RESOURCE para inclui-las em seu aplicativo. É também possível fazer o seguinte:

    
Form.ICOHandle = ICO_TEST

     DIM Image AS QImage

     Image.Icon = Form.Icon


Image.icon normalmente aceita uma string como rótulo, mas se nenhuma string foi especificada, lerá o ícone anteriormente armazenado. Nesse caso, nós estaremos carregando Form.icon para nosso "cache" (armazenamento) e Image.icon lerá dele. Assim, o que isso faz?

    
Image.Icon = Form.Icon + Form2.Icon

Isso carregará Form2.icon para o cache, assim Image.icon copiará Form2.icon. No Entanto, observe que você não pode atribuir ICOHandles, como por exemplo:

    
Image.IcoHandle = Form.IcoHandle

O compilador tentará achar um IcoHandle, mas não o achará.

8.2 Introdução a listas de Imagens

Você pode consultar o Apêndice nas propriedades e métodos de QImageList. Um lista de Image é um array (sequência) de ícones e/ou bit-maps. Eles estão ordenados de 0 a N. A primeira imagem que você adicionar será a de índice 0, a próxima de índice 1, etc.. uma lista da imagens armazena imagens de largura e altura FIXA. A qualquer momento você pode mudar a propriedade Largura ou Altura, e finalmente apagar o array inteiro. Note, isto não significa que você não pode adicionar um arquivo BMP 100x100 quando sua lista de imagens é de largura 32. Isto apenas significa que seu BMP será redimensionado, queira-o você ou não.


       
DIM ImageList AS QImageList
        ImageList.Width = 32
        ImageList.Height = 32
        ImageList.AddICOFile("app.ico")
        ImageList.AddBMPFile("app.bmp", 0)


Você pode adicionar ícones ou bitmaps em qualquer ordem. Como antes, você pode também adicionar as imagens do "cache" se nenhum filename (nome  de arquivo) estiver especificado. Como segue:

   
ImageList.AddICOFile(Form.Icon)
    ImageList.AddICOFile(ImageList.GetICO(0))



8.3 Outros tipos de Recursos

Você pode incluir qualquer tipo de recurso em seu programa Rapid-Q, mas como você pode usar estes outros tipos? Por exemplo, se incluir um  arquivo .WAV como recurso, como eu poderia usá-lo em minha rotina PLAYWAV? Bem, visto que PLAYWAV pode receber o recurso, você pode passá-lo como um parâmetro:

   
$RESOURCE Bomb_WAV AS "BOMB.WAV"
   
    PlayWav(Bomb_WAV, 1)     
'-- 1 = toque de fundo

    SLEEP 5                 
  '-- Espere até o wav terminar

Quanto a outros tipos, você pode extrair o recurso para ser manipulado.

   
$RESOURCE Whatever_TYPE AS "text.txt"
   
    ExtractResource(Resource(0), "text.txt")



Depois de você tê-lo usado, você simplesmente elimina o arquivo. Há uma grande variedade de usos para extração de recursos, você pode usar essa técnica em um programa de instalação. Para mais informações sobre EXTRACTRESOURCE, RESOURCE, e RESOURCECOUNT,
veja o Apêndice C: Outras palavras chaves reservadas.


Você aprenderá tudo que precisa para saber criar suas próprias SUB/ FUNCTIONs com um número enorme de parâmetros. Se você sabe um pouco de C, você provavelmente conhece o popular comando printf. Sim, é possível implementar seu próprio printf em Rapid-Q!

9.1 SUB/FUNCTIONs  com parâmetros variáveis

As tradicionais SUBs (Subrotinas) ou FUNCTIONs (Funções) têm um número fixo de parâmetros que podem ser passados, mas SUBI e FUNCTIONI podem aceitar um número enorme (255) parâmetros  variáveis. Você está provavelmente imaginando como podemos reaver estes
parâmetros, bem, eles estão realmente armazenados em uma pilha interna. E daí o que você terá que fazer é sondar esta pilha para obter o
parâmetro correto. Aqui estão as palavras-chaves usadas para fazer isso:
:
 
  ParamStr$()   - Array de parâmetros string
 
  ParamVal()    - Array de parâmetros numéricos
  
ParamValCount – Número de parâmetros numéricos passados
  
ParamStrCount – Número parâmetros string passados

Com um simples exemplo nós testamos como funciona:

  
SUBI TestSUBI (...)
         PRINT "Parâmetros String:  "; ParamStrCount
             FOR I = 1 TO ParamStrCount
                 PRINT I;" "; ParamStr$(I)
             NEXT I
           PRINT "Parâmetros Numéricos: "; ParamValCount
             FOR I = 1 TO ParamValCount
                 PRINT I;" "; ParamVal(I)
             NEXT I
    END SUBI

    TestSUBI "Olá", 1234, "Hmmm", "Sim...", 9876, 1*2*3*4, UCASE$("O último")


Você provavelmente reparou que os arrays ParamStr$() e Paramval() não são zero-based (ie. seu primeiro valor começa com 1). Isto pode parecer estranho, desde que tudo em Rapid-Q é zero-based. De qualquer modo, além disso, uma coisa igualmente estranha é o uso de (...) em lugar de parâmetros nomes. Não faz realmente diferença o que você põe entre parenteses, desde que seja um token válido (uma palavra). Tentar criar uma FUNCTIONI, não é diferente de criar uma FUNÇÃO padrão, exceto que você deve substituir seus parâmetros nomes formais por (...).

9.2 Mais sobre FUNCTIONI

Para ampliar nosso conhecimento, vamos ver mais exemplos, logo em seguida acharemos o número máximo de parâmetros que você fornece.

  
FUNCTIONI FindMax (...) AS DOUBLE

       DIM Largest AS DOUBLE
       DIM I AS BYTE

       Largest = -999999999999


      
FOR I = 1 TO ParamValCount
           IF ParamVal(I) > Largest THEN
                Largest = ParamVal(I)
           END IF
       NEXT

       FindMax = Largest        
' ou Result = Largest
   END FUNCTIONI


A função percorre a lista inteira de parâmetros numéricos e verifica qual é a maior. Alguns parâmetros string são apropriadamente ignorados. Vamos testar isso:

  
PRINT "O número maior é: "; FindMax(523, 12.4, 602, 45, -1200)
   PRINT "O número maior é: "; FindMax(523, 12.4, FindMax(602, 45, -1200))


Você pode confortavelmente embutir sua FUNCTIONI se desejar. Não há realmente nada contra isso. Apenas lembre as palavras-chaves que você precisa para acessar a pilha interna, e vá em frente!

9.3 Introdução às DLLs

O que é uma DLL? É uma Biblioteca de Vínculo Dinâmico (Dynamic Link Library) que contém funções que podem ser usados por qualquer linguagem de programação que suporte DLLs. Assim você poderia escrever sua DLL em Delphi ou C++ e usá-las em Rapid-Q. DLLs trabalham ao serem carregadas no mesmo espaço do processo de seu programa, assim você tem acesso a suas funções exportadas. DLLs em Rapid-Q somente podem ser dinamicamente vinculadas em tempo-real (ie. executadas em tempo-real).

9.4 Como para chamar uma DLL

Há um programa exemplo chamado DLL.BAS incluido em sua distribuição Rapid-q. Está também incluído uma DLL chamada PASCAL.DLL. Você pode querer dar uma olhada. Para chamar DLLs em Rapid-Q é quase idêntico ao modo de você chamar DLLs em PowerBASIC ou VisualBASIC. Isto pode parecer ruim, mas decidí aceitar esse padrão... Uma coisa a notar é essa CASE MATTERS!! Sim, tem razão, se você colocar em minúsculas sua função, ka-bum! Opa, não fique mais confuso, mas há realmente 2 funções. Uma que você chamará em seu programa, e a outra é a contida na DLL. Estas duas subs/functions devem ter parâmetros iguais, ou ka-bum!

   
DECLARE SUB Test LIB "TEST.DLL" ALIAS "MyFunc" (S AS STRING)

Assim você deve estar imaginando, qual é sensível ao tipo das letras, Teste ou Myfunc. Bem, Test é o que você estará chamando em seu próprio programa, assim você sabe como não deve ser, Myfunc é o contido na DLL, assim assegure-se de digitá-lo corretamente! A SUB acima aponta para o endereço de memória de sua DLL (quando carregada), e executa o código dentro dela. Como você pode ver, há apenas um parâmetro para nossa rotina Myfunc. Há somente 2 palavras-chave extras introduzidas aqui, LIB e ALIAS. Depois de LIB dever haver uma string correspondente ao caminho de sua DLL.Se sua DLL reside em seu caminho estabelecido, você não precisa especificar um caminho (ie. se seu TEST.DLL reside em C:\WINDOWS\SYSTEM). A segunda palavra-chave ALIAS é para especificar a Sub/Function que você executará nessa DLL. Assegure-se de que você verifique o tipo de letras, é muito importante. Se você não estiver certo do tipo de letras, você pode usar um visor (viewer) de DLLs (Tal como Quick View, mais adiante).
Qual é o tipo de letras válido para passar?

SHORT/WORD, INTEGER/LONG/DWORD, DOUBLE, STRING, QRECT, QNOTIFYICONDATA, e qualquer UDT que você tenha criado.

Diferentemente de uma implementação que force você a passar sua STRING como um ponteiro, O Rapid-Q faz tudo isto para você, assim você não necessita se preocupar. Quando você vê LPZSTR como um parâmetro, isso apenas quer dizer Long Pointer Zero-Terminated String (Longo Ponteiro de String Terminada em zero.) Você pode com segurança passar uma variável string qualquer. Pode reparar que algumas linguagens BASIC (como VB)  ignoram o ALIAS quando a função combina com a mesma função que você está declarando. Por Exemplo:

   
DECLARE SUB MyFunc LIB "TEST.DLL"

Desde que sua função combine com a função da DLL, você pode deixar de usar o ALIAS. No Entanto, isto NÃO se aplica em Rapid-q, você não pode fazer isso, nem tente.
Chamando uma DLL ou chamando sua própria função é exatamente o mesmo, não há o nada mais que você precise saber:

   
MyFunc("Olá")

9.5 Usando o Quick View

Como menciondo antes, se você não souber do tipo de letras de sua função, será um boa idéia dar uma olhada no Quick View. O Quick View vem incluído no Windows 95. Nos Windows seguintes ele não foi mais incluido mas, ainda pode ser baixado do site abaixo:

http://www.viennacomputerproducts.com/downloads/QuickView/Setup.exe

É uma versão atualizada que roda até no Vista, instale-o em seu computador Para chamá-lo abra o Windows Explorer (ou clique em "Meu Computador"), então procure um arquivo DLL, clique nele com o botão direito do mouse e em "Abrir com.." seguido de "Escolher programa", aí selecione o Quick View no menu. O nome que aparecerá é "Schnellansicht". Veja o exemplo:

Com o Quick View aberto procure uma Export Table (Tabela de Exportação) onde você achará todas as funções disponíveis na DLL.

9.6 Escrevendo suas próprias DLLs

Com relação a essa escrita, não é possível escrever DLLs em Rapid.Q. A maioria das linguagens são capazes de de criar DLLs, o único problema que eu vejo são as strings, especialmente se você está usando VisualBasic ou PowerBasic. Use ponteiros (pointers) para strings em seus parâmetros para evitar usar OLE para alocar as strings ele próprio. O Rapid-Q não usará OLE para alocar as strings, visto que eu não preví muitas DLLs que não usam ponteiros para strings.

9.7 Usando tipos não suportados em chamadas de DLL

ESTA SEÇÃO É NULA COM A INTRODUÇÃO DE STRUCT, MAS PODE AINDA SER USADO SE VOCÊ DESEJAR. O Rapid-Q tem um mecanismo de armazenagem diferente para o usuário definir TYPES (tipos) que os tornam inúteis para chamadas de DLLS que requerem certo TIPO de parâmetros. No entanto, nem tudo está perdido. Há um modo barato de contornar que funciona muito bem, mas precisa de algumas adaptações. Funções de DLLs que requerem parâmetros de TYPE necessitam que seja passado um ponteiro para esse TYPE. Vejamos um exemplo de uma função de DLL que requer um parâmetro de TIPO:

   
DECLARE FUNCTION GetWindowRect LIB "USER32" ALIAS "GetWindowRect" _
                      (hWnd AS LONG, lpRect AS RECT) AS LONG 
  

    RECT é uma estructura de form:

    TYPE RECT
        Left AS LONG
        Top AS LONG
        Right AS LONG
        Bottom AS LONG
    END TYPE


Naturalmente você gostaria que isso funcionasse:


    DIM R AS RECT

    GetWindowRect(MainForm.Handle, R)


PARECE QUE FUNCIONA. No entanto, o Rapid.Q armazena tipos definidos não contiguamente, ie. não como um bloco total como requerido pela chamada da DLL, assim esta chamada é inválida. O que fazer para que funcione? Um jeito simples de contornar é usar o já útil componente MEMORYSTREAM:

    '' Observe a mudança em lpRect

   
DECLARE FUNCTION GetWindowRect LIB "USER32" ALIAS "GetWindowRect" _
                      (hWnd AS LONG, lpRect AS LONG) AS LONG

    DIM R AS RECT
    DIM M AS QMEMORYSTREAM


   
M.WriteUDT(R)
    GetWindowRect(MainForm.Handle, M.Pointer)
    M.Position
    M.ReadUDT(R)


O primeiro coisa que nós fizemos foi mudar os parâmetros da Função da DLL. Nós apenas mudamos o tipo de lpRect para LONG. Isso porque nós queremos passar um ponteiro (o qual é realmente um número). Aqui nós usaremos QMEMORYSTREAM para armazenar o tipo definido pelo usuário. (Usando M.WriteUDT) em um bloco contíguo para nossa chamada da DLL. Temos que fazer isso, estarmos passando o ponteiro para esse bloco de memória e a DLL lerá dessa localidade da memória. Depois de ter chamado a função nós devemos reaver os dados retornados (se necessário, nesse caso Getwindowrect retorna a estrutura do rect do Windows). Está um tanto simples, mas e se houver tipos aninhados dentro de um outro TYPE?

    
TYPE Struct1
         A AS INTEGER
     END TYPE

     TYPE Struct2
         S AS Struct1
         I AS INTEGER
     END TYPE


Se uma DLL requer um TYPE que pode ter aninhados dentro dele outros TYPEs, então você pode fazer o mesmo tipo de conversão:

   
TYPE Struct1
         A AS INTEGER
     END TYPE

     TYPE Struct2
         S AS LONG
         I AS INTEGER
     END TYPE

     DIM S1 AS Struct1
     DIM S2 AS Struct2

     DIM M1 AS QMEMORYSTREAM
     DIM M2 AS QMEMORYSTREAM

     M1.WriteUDT(S1)
     S2.S = M1.Pointer
     M2.WriteUDT(S2)


   
'Chama a função da DLL...

Agora o último caso que temos de considerar é strings dentro de TYPEs. Não declaramos mais uma string, nós temos que declarar um ponteiro para uma string, por exemplo:

   
TYPE TStruct
         ST AS STRING
         Other AS INTEGER
     END TYPE


Isto não é válido desde que ST. estará sendo passada por valor (neste caso pode consumir um número qualquer de bytes na memória). No entanto, a maioria das chamadas de DLL requerem strings passadas por referência (ie. apenas dar um ponteiro à string). Uma conversão é necessária usando VARPTR:

    
TYPE TStruct
         ST AS LONG
         Other AS INTEGER
     END TYPE

     DIM Struct AS TStruct
     DIM S AS STRING

     S = SPACE$(100)
' Reserva qualquer espaço para a string
     Struct.ST = VARPTR(S)

    
'Para pegar de volta a string use VARPTR$
    S = VARPTR$(Struct.ST)


Note que esta conversão de string é somente necessária para TYPEs e não para chamada de DLL normais. Para chamadas normais que têm STRINGs como parâmetros, apenas se passa a string e não o ponteiro, visto que o Rapid-Q traduz isso automaticamente para você. Poderá
naturalmente, fazer a conversão você mesmo, apenas lembre-se de mudar o parâmetro de STRING para alguma coisa como LONG, e passar VARPTR(S$) em vez da string real S$.


9.8 tabela de conversão de API

Muitas pessoas têm pedido um guia de API para converter suas declarações de WinAPI em C ou VB para uso com Rapid-Q, assim vai abaixo um resumo com algumas explicações breves. Agradecimentos especiais para Mayuresh S. Kadu para este guia WIN32 API em VB.
Para aqueles tipos de dados em vermelho, eles são ponteiros para a variável. Em Rapid.q, isso requer o uso de VARPTR para passar o endereço da variável, e não o valor da variável. Aí vão algumas conversões de amostra:
 

Em VB:
        DECLARE SUB Test LIB "USER32" ALIAS "What" _
                      (byval L AS LONG, byval S AS STRING)

        Test (1230, "Alô Mundo!")


  In Rapid-Q:
       
DECLARE SUB Test LIB "USER32" ALIAS "What" _
                      (byval L AS LONG, byval S AS STRING)

        Test (1230, "Alô Mundo!")


Nada diferente, note porém que Rapid-Q não usa BYVAL, assim você pode ignorá-lo aquí. No exemplo acima, a string é passada por referência, ie. o endereço da string é passado, não a string propriamente dita. Aqui está um exemplo que passa toda a string (não o endereço, assim isso envolve OLE para alocar o espaço para a string), isto funciona muito bem em VB mas não em rapid-Q:

  Em VB:
       
DECLARE SUB Test LIB "USER32" ALIAS "What" _
                      (byval L AS LONG, S AS STRING)

        Test (1230, "Alô Mundo!")


  Em Rapid-Q:
        Não é possivel, porque que não usa OLE para alocar a string.


O que há sobre strings NULL? Aí é outra situação (Observe que eu não uso muito o VB assim pode ser possível declarar S como STRING e
passá-la como vbnullstring, Eu não estou realmente certo assim vou jogar com segurança):

  Em VB:
       
DECLARE SUB Test LIB "USER32" ALIAS "What" _
                      (byval L AS LONG, byval S AS LONG)

        Test (1230, 0&)


  Em Rapid-Q:
       
DECLARE SUB Test LIB "USER32" ALIAS "What" _
                      (byval L AS LONG, byval S AS LONG)

        Test (1230, 0&)


E daí se nós quiseremos passar uma string em vez de uma string NULL, então precisariamos usar VARPTR:

  Em VB or Rapid-Q:
       
DIM S AS STRING
        S = "Alô Mundo!"

        Test (1230, VARPTR(S))


Uma string NULL basicamente quer dizer um endereço 0. VARPTR apenas retorna o endereço de uma variável.
O que há acerca de outros ponteiros, como do LPWORD? Uma WORD (Palavra) é exatamente um número de 16-bits não assinalado, O VB
somente suporta números assinalados de 16-bits chamados INTEGERs, mas a idéia geral é remover a palavra-chave byval quando estiver passando variáveis por referência:

  Em VB (não suporta WORD, assim usa a próxima melhor coisa):
       
DECLARE SUB Test LIB "USER32" ALIAS "Another" _
                      (L AS INTEGER)

        DIM L AS INTEGER

        Test (L)


  Em Rapid-Q:
       
DECLARE SUB Test LIB "USER32" ALIAS "Another" _
                      (BYREF L AS WORD)

        DIM L AS WORD

        Test (L)


Em Rapid-Q, use BYREF quando passar variaveis por referencia, em VB você não precisa usar BYREF, como o exemplo acima demonstra.

Você não pensaria em ver ponteiros de função em uma linguagem de programação BASIC. Bem, em Rapid-Q os ponteiros de função são
bastante diferentes do que a maioria tem usado. Há algumas coisas estranhas, mas a maioria das características importantes estão lá, como a redefinição de ponteiros de  função, e ponteiros dinâmicos da função. Infelizmente, você não pode passar ponteiros de função como argumentos.

11.1 Introdução ao conceito

Assim o que que são ponteiros de função? Eu sei que muita gente está cansada da terminologia, simplesmente porque a definição é provavelmente muito fácil de compreender. Ajuda a compreender como as coisas são armazenadas na memória e endereçadas. Por exemplo, uma função (IE. Uma SUB ou uma FUNCTION) ocupam espaço em algum endereço da memória. O ponteiro dessa função é apenas o endereço dela. Assim se eu souber o endereço da função, eu posso saltar para esse "espaço de processamento" e executar o código alí. Este é o modo de pensar de baixo nível, primeiramente se pegam os parâmetros, e então se salta para (IE. CALL) a função. Assim se os ponteiros são somente números, como nós encontramos o endereço da função? Esta é a pergunta que será considerada em seguida.

11.2 Definindo ponteiros da função

Se você tem experiência prévia com ponteiros de função (IE. em C) há algumas diferenças às quais você tem que se habituar. Em Rapid-Q, desde que os ponteiros da função são apenas números, você pode usar qualquer tipo numérico válido para definir seus ponteiros de função. Parece estranho, sim, mas os ponteiros são somente números. Tente evitar o BYTE, visto que o endereço de sua função pode; ser maior de 255. Eu sugiro usar INTEGER ou LONG.

DECLARE SUB Test (I AS INTEGER)

DIM FPtr AS INTEGER   
BIND FPtr TO Test


Bem, nós usamos BIND (LIGAMENTO) em Rapid-Q para ligar nossa varável FPtr à função de Test. Isto fará 2 coisas, obviamente FPtr apontará para o espaço do endereço de Test, mas igualmente liga o número de parâmetros a FPtr, isto é importante para o compilador de modo que ele saiba quantos parâmetros passar/aceitar. Para chamar nossa função, nós temos que usar a palavra reservada CALLFUNC:

CALLFUNC(FPtr, 120)

Isto chama nossa SUB Test com 1 parâmetro. Obviamente este não é um exemplo muito útil, assim considere-o enquanto for útil…

DECLARE SUB Test1 (I AS INTEGER) 
DECLARE SUB Test2 (I AS INTEGER)  

DIM FPtr(1 TO 2) AS INTEGER  
BIND FPtr(1) TO Test1   
BIND FPtr(2) TO Test2

Quando você está ligando arrays, note que a primeira função que é ligada será o MODELO para todos seus elementos restantes. Confuso?
Ok, deixe-me pôr isto em palavras que se possam compreender. :) Nossa array é FPtr, nosso BIND liga FPtr(1) à SUB Test1. FPtr é então BOUND (ligada). O que isto significa é que se você BIND (liga) qualquer outro FPtr (i), a função DEVE TER O MESMO NÚMERO DE PARÂMETROS. Talvez um exemplo ajude:

DECLARE SUB Test1 (I AS INTEGER)  
DECLARE SUB Test2 (I AS INTEGER, J AS STRING)   
DIM FPtr(1 TO 2) AS INTEGER 
BIND FPtr(1) TO Test1  
BIND FPtr(2) TO Test2


O segundo BIND está incorreto, desde que Test2 não tem o mesmo numero de parâmetros que Test1. Você receberá uma mensagem de erro se você fizer isso. Observe que eu disse combinar o NÚMERO de parâmetros, você realmente não precisa combinar o TIPO de parâmetros. Porém não vale a pena experimentar, pois os resultados podem variar.

11.3 Usando ponteiros de função corretamente

Fica claro (se alguma vez que você usou ponteiros de funçãoo) que há ocasiões em que usar ponteiros de função é necessário, e ocasiões
em que não. Eu acho que cada situação é diferente, mas considere esta:

       
SELECT CASE I               
        CASE 1       
                Process1("1", 44)                
        CASE 2       
                Process2("2", 55)                
        CASE 3        
                 Process3("3", 66)                
        CASE 4 
             :              :               
                etc...

Você pode generalizar esta situação, por exemplo, um menu com diversas opções, ou talvez um (analisador de códigos) parser (sim Rapid-Q usa ponteiros de função para analisar gramaticalmente seu código). Como você pode observar, há um monte de comparações, especialmente se você está escrevendo um analizador onde há muitas palavras-chaves (mais de 50). Nós podemos facilmente eliminar estas comparações usando os ponteiros de função,& acelerando conseqüentemente seu programa. Veja FUNCPTR.BAS para um exemplo concreto.

    
BIND FPtr(1) AS Process1
     BIND FPtr(2) AS Process2    
     BIND FPtr(3) AS Process3    
     BIND FPtr(4) AS Process4   
     BIND FPtr(5) AS Process5    
     BIND FPtr(6) AS Process6    
     BIND FPtr(7) AS Process7   
     x = VAL(INPUT$(1))    
     CALLFUNC(FPtr(x), 33)


O código que você vê acima não é executável como está mas, acho que você entende a idéia geral.Não necessitamos de nenhuma das instruções de Case como você pode ver (supondo que você particionou seus ponteiros de função corretamente).

11.4 O que não é suportado em Rapid-Q

Ponteiros de função como argumentos não são suportados. Tão engraçado como soe, isso é realmente muito proveitoso. Infelizmente não há a possibilidade de executar isso (agora). Você pode passar o valor do ponteiro de função como um argumento, mas como você pode ver, você não poderá chamar sua função. A única maneira seria definir um ponteiro global de função como seu template (modelo), e atribuir esse ao seu argumento. É mais fácil ver um exemplo:

 
    DECLARE FUNCTION MyFuncTemp (X AS LONG) AS LONG   
     DECLARE FUNCTION Func1 (X AS LONG) AS LONG   
     DECLARE FUNCTION Func2 (X AS LONG) AS LONG


    
DIM Template AS INTEGER   
     DIM I(100) AS INTEGER    

     BIND Template TO MyFuncTemp   
     BIND I(1) TO Func1   
     BIND I(2) TO Func2   

     SUB Test (FPtr AS INTEGER)            
            Template = FPtr           
             PRINT CALLFUNC(Template, 10)   
     END SUB  
 
     Test I(1)

Isto é tudo sobre a única maneira que você pode trabalhar em torno desta limitação.



 
Envie-me um Email:    godin13@hotmail.com     Críticas, sugestões e solicitações são benvindas     Meus doownloads favoritos:    Kerodownload