quarta-feira, 6 de julho de 2011

Usando DllImport no Csharp (C#)

Algumas vezes temos a necessidade de usarmos funções que estão em Dlls que não foram escritas em C#. Surge então, alguns problemas, como passagens de parâmetros, passagens de ponteiros, entre outros que não são diretamente compatíveis entre C# e a linguagem em que a Dll foi escrita. O Objetivo desse tutorial é introduzir a utilização do comando DllImport utilizando uma Dll que foi escrita em C. Assim teremos funções que precisaremos passar ponteiros, structs, etc que precisarão de uma compatibilização.

Primeiramente vamos criar um projeto em C++ para criarmos nossa Dll. Vou apenas colocar o código que usamos para nossas funções e structs. O restante acrescentado pelo compilador será omitido.

Assim as funções de nossa DLL são as seguintes: Observe que normalmente você já terá a Dll que será importada, aqui estamos criando para fins de demonstração.

struct dados //estrutura simples
{
int valor;
};

struct dadosString //contem string interna
{
int valor;
char nome[50];
};

extern "C"
{
__declspec(dllexport) void Printf()
{
printf ("Parabens conseguimos importar a DLL\n");
}

}

extern "C"
{
__declspec(dllexport) void Printf_M(char * mensagem)
{
printf("A mensagem que voce quer mostrar na tela:\n%s\n",mensagem);
}
}

extern "C"
{
__declspec(dllexport) int ContaCaracteres(char * mensagem)
{
int contador = 0;
while(*mensagem)
{
contador++;
mensagem++;
}

return contador;
}
}

extern "C"
{
__declspec(dllexport) char* ConverteParaMaiuscula(char * mensagem)
{
char *p;
p = mensagem;
while(*p)
{
*p = toupper(*p);
p++;
}
return mensagem;
}
}

extern "C"
{
__declspec(dllexport) void PreencheEstruturaInt(struct dados * d)
{
d->valor = 45;
}
}

extern "C"
{
__declspec(dllexport) void PreencheEstruturaString(struct dadosString * d)
{
d->valor = 55;
strcpy(d->nome,"Teste da Funcao");
}
}

Bem simples....

Nosso próximo passo será criar um projeto em CSHARP, para chamarmos nossa DLL.

Dentro de nosso projeto vamos criar uma classe chamada: operacional.cs . É nessa classe que faremos a chamada do DllImport.

Aqui está o código da nossa classe. Veja que a string “path” contém o caminho onde está a Dll, você deve ajustar para onde sua Dll está, por exemplo: path = @“C:\minhaDll”;

Importante, para usar DllImport, precisamos incluir o seguinte namespace:

using System.Runtime.InteropServices;


class operacional
{
private const string path = @"C:\Users\Eduardo\Documents\Visual Studio 2010\Projects\Dll\Debug\Dll.dll";
[StructLayout(LayoutKind.Sequential)]
public struct dados
{
public int valor;
}

public struct dadosString
{
public int valor;
[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 50)]public string nome;
}

//importa função: void Printf()
[DllImport(path)]
public static extern void Printf();

//Importa Função: void Printf_M(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern void Printf_M(string mensagem);

//Importa Função: int ContaCaracteres(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern int ContaCaracteres(string mensagem);

//Importa Função: char* ConverteParaMaiuscula(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern string ConverteParaMaiuscula(string mensagem);

//Importa Função: void PreencheEstruturaInt(struct dados * d)
[DllImport(path,CallingConvention = CallingConvention.Cdecl)]
public static extern void PreencheEstruturaInt(ref dados estrutura);

//Importa Função: PreencheEstruturaString(struct dadosString * d)
[DllImport(path,CallingConvention = CallingConvention.Cdecl)]
public static extern void PreencheEstruturaString(ref dadosString estrutura);

}

Agora a segue nosso programa de teste:

namespace ChamaDll
{
class Program
{
static void Main(string[] args)
{

int retorno=0;
string mensagem = "Teste com passagem de ponteiros";
operacional.dados estrutura;
estrutura.valor = 0;

//chamada sem parâmetros e sem valor de retorno
operacional.Printf();

//chamada com parametros
operacional.Printf_M(mensagem);

//Chamada com valor de retorno
retorno = operacional.ContaCaracteres(mensagem);
Console.WriteLine("Numero de caracteres: {0}",retorno);

//Chamada modificando o ponteiro - string
mensagem = operacional.ConverteParaMaiuscula(mensagem);
Console.WriteLine(mensagem);

//chamada com struct
Console.WriteLine("Valor da estrutura antes da chamada: {0}", estrutura.valor);
operacional.PreencheEstruturaInt(ref estrutura);
Console.WriteLine("Valor da estrutura depois da chamda: {0}", estrutura.valor);

//chama strutura que contem uma string
operacional.dadosString estruturaString;
estruturaString.nome = "NULL";
estruturaString.valor = 0;

operacional.PreencheEstruturaString(ref estruturaString);

Console.WriteLine("Nome: {0}, Idade:{1}", estruturaString.nome, estruturaString.valor);
Console.Read();
}
}
}



Resultado:

  

sexta-feira, 24 de junho de 2011

Arrays bidimensionais

Olá pessoal,

hoje vamos tentar trabalhar um pouco com arrays bidimensionais, mais especificamente em como passá-los como parâmetros de funções.

Para isso vamos escrever duas funções, uma para imprimir um array e outra para preencher.

No exemplo vamos utilizar um array de 3 por 4.


void ImprimeMatriz(int matriz[][4], int linha)
{
    int c_linha; //conta linhas
    int c_coluna;//conta colunas
    int coluna = 4;
    for(c_linha = 0; c_linha < linha; c_linha++)
    {
        printf("\n");
        for(c_coluna = 0; c_coluna < coluna; c_coluna++)
            printf("%d ", matriz[c_linha][c_coluna]);
    }

}

void PreencheMatriz(int matriz[][4], int linha)
{
    int contador = 0;
    int c_linha; //conta linhas
    int c_coluna;//conta colunas
    int coluna = 4;
    for(c_linha = 0; c_linha < linha; c_linha++)
    {
        for(c_coluna = 0; c_coluna < coluna; c_coluna++)
            matriz[c_linha][c_coluna] = contador++;
    }
}

Veja que, temos que passar o tamanho da linha como parâmetro da matriz, vejas as assinaturas: void PreencheMatriz(int matriz[][4], int linha) e void ImprimeMatriz(int matriz[][4], int linha)

Bom, isso porque na verdade é passado o endereço do array, e o C precisa saber até onde ele pode ir na linha.

Outra possibilidade é utilizarmos aritmética de ponteiros, na verdade, um array é um conjunto contínua na memória, assim por exemplo, se o endereço inicial é 10, e um inteiro ocupar 2 bytes temos que o primeiro elemento está no endereço 10, o segundo no endereço 12, o terceiro no 14.... Isso deve ser bem observado, por exemplo para chegar no endereço 12, se nosso endereço base é 10, fazemos a seguinte operação com nosso ponteiro: ponteiro +1, ou seja, o + 1 diz que vamos para o próximo endereço, com o passo de acordo com nosso tipo, que no exemplo é int e assumimos que ele é de 2 bytes. Vejamos abaixo uma função que vai fazer esse tipo de operação para imprimir nosso array:

void ImprimeMatrizPonteiros(int * matriz, int tamanho)
{
    int cont = 0;
    for (cont = 0; cont < tamanho; cont ++)
    {
        printf("% d", *(matriz + cont));
    }
}

Nosso método main:

int main()
{
    int matriz[3][4];
    PreencheMatriz(matriz,3);
    ImprimeMatriz(matriz,3);
    printf("\nVamos usar apenas ponteiros:\n");
    ImprimeMatrizPonteiros(matriz[0],12);


    return 0;
}

que irá reproduzir a seguinte saída:

segunda-feira, 13 de junho de 2011

Interagindo com o sistema

Vamos tentar enviar alguns comandos para a linha de comando, dentro de nosso programa em C. O programa foi criado e testado em um sistema Debian.

Vamos ao código:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int  MostraOpcoes()  //mostra as opções que vamos implementar e retorna o valor digitado pelo usuário
{
    int opcao;
    printf("\n1 - Lista diretorios");
    printf("\n2 - Cria diretorio");
    printf("\n3 - Cria arquivo");
    printf("\n4 - Deleta diretorio");
    printf("\n\tDigite a opcao:\t");
    scanf("%d",&opcao);

    return opcao;
}

void ExecutaComandos(int opcao)
{
    char dir[50], cmd[50];
    int op;
    int ret;
    struct stat files;
    FILE * arquivo;
    switch(opcao)
    {
        case 1: printf("\n\tListando\n"); //apenas lista o diretório
                system("ls -la"); //usamos o comando system que enviará o comando para o sistema
                break;
        case 2: printf("\n\tCriando Diretorio\n"); //vamos ler o nome do diretório a ser criado
                printf("\n\tInforme o nome do diretorio: ");
                scanf("%s",dir);
                sprintf(cmd, "mkdir %s",dir);
                if(stat(dir,&files)==0) //Vamos verificar se ele existe
                {
                    printf("\nO arquivo ja existe!!!\nDeseja Remove-lo? 1-Sim\t");
                    scanf("%d", &op);
                    if(op == 1) //caso ele existe e o usuário desejar apagar, vamos remover o diretório e criar novamente
                    {
                        sprintf(cmd,"rm -r %s",dir);  //cmd recebe a string rm -r + o conteúdo de "dir"
                        system(cmd);
                        sprintf(cmd, "mkdir %s",dir);
                        system(cmd);
                    }
                }

                else
                    ret = system(cmd);


                break;
        case 3: printf("\n\tInforme o nome do arquivo:\t"); //vamos criar um arquivo
                scanf("%s",dir);
                arquivo = fopen(dir,"r"); //aqui é simples, usamos fopen com opção 'r' assim se o arquivo existir vamos informar ao usuário
                if (arquivo != NULL)
                    printf("\nO arquivo ja existe");
                else
                    arquivo = fopen(dir,"w"); //se não existir o criamos
                break;
                fclose(arquivo);

        case 4: printf("\n\tInforme o nome do diretorio:\t"); //apenas excluiremos o diretório
                scanf("%s", dir);
                if(stat(dir,&files)==0)
                {
                    sprintf(cmd,"rm -r %s",dir);
                    system(cmd);
                }
                else
                printf("\nArquivo inexistente!!\n");
                break;
    }

}

int main()
{
    int opcao;
    int continua;
    do
    {
        opcao = MostraOpcoes();
        ExecutaComandos(opcao);
        printf("\n\tPressione 0 para sair:\t");
        scanf("%d",&continua);
    }while(continua !=0);
    return 0;
}

Expressões Regulares em C#

Hoje vamos falar de expressões regulares em C#. O objetivo será:


  • Contar o número de palavras em um arquivo
  • Contar o número de ocorrências de uma determinada palavra

Bom para isso vamos iniciar o Visual Studio e dar início ao trabalho:

Vamos montar a seguinte tela:


Bom vamos programar uma classe, para trabalhar com nosso arquivo. Vamos chamá-la de File.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace ContaPalavrasArquivo
{
class File
{
StreamReader arq; //vai ser usado para ler o arquivo
public File(string caminho) //nosso construtor que recebe o caminho do arquivo
{
arq = new StreamReader(caminho,Encoding.Default);
}
public void readFile(TextBox texto) //lê o arquivo e mostra no TextBox
{
string linha;
do
{
linha = arq.ReadLine();
if (linha != null)
texto.Text = texto.Text + linha + System.Environment.NewLine;
} while (linha != null);
}

public int contaPalavras(TextBox text) //Conta palavras
{
int nPalavras = 0;
string linha;
MatchCollection exp; //vai armazenar o número de ocorrências
linha = text.Text;
if (linha != null)
{
exp = Regex.Matches(linha, @"[\S]+"); //expressão regular para espaços em branco
nPalavras = exp.Count;
}
return nPalavras;
}
//vai contar o número de ocorrências do texto. Para isso, vamos passar o texto que //está no textBox, vamos passar a string que desejamos comparar, e uma variável do //tipo boll que irá indicar se desejamos ou não diferenciar maiúsculas de minúsculas
public int contaOcorrencias(TextBox text, string palavra, bool ignoreCase)
{
MatchCollection exp;
if(ignoreCase)
exp = Regex.Matches(text.Text, palavra,RegexOptions.IgnoreCase);
else
exp = Regex.Matches(text.Text, palavra);
return exp.Count;

}
}
}


Vamos programar o nosso menu:

Vale ressaltar que temos duas variáveis que possuem acesso em todo nosso programa, que devem ser declaradas da seguinte forma:

String caminho;
File arquivo;

Vamos ao código:

private void abrirToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog UI = new OpenFileDialog(); //Vamos abrir uma caixa para ir //diretamente ao arquivo
UI.Filter = "txt files (*.txt)|*.txt";
UI.InitialDirectory = "C:";
UI.Title = "Selecione um arquivo";
if (UI.ShowDialog() == System.Windows.Forms.DialogResult.OK)
caminho = UI.FileName;
else
caminho = null;
if (caminho != null)
{
arquivo = new File(caminho); //criamos a instância de nossa classe
arquivo.readFile(textBoxTexto); //lemos o arquivo que será colocado no //textBoxTexto
}
}



Quando utilizarmos nosso menu irá aparecer um dialogbox como este:



Perfeito, agora depois de abrir o arquivo teremos uma tela como a que segue:




Vamos codificar nosso botão “Conta”

private void buttonContaPalavras_Click(object sender, EventArgs e)
{
int contador = arquivo.contaPalavras(textBoxTexto);
textBoxNPalavras.Text = contador.ToString();
}

Vamos codificar o botão “Conta Ocorrencias”.

Primeiramente vamos adicionar outro form, para que quando clicarmos em “Conta Ocorrencias” nos seja mostrado um form para digitar a palavra que estamos procurando.


Esse form, terá duas variáveis públicas que irão nos ajudar a trabalhar, são elas:

public string palavra;
public bool ignoreCase;

Veja o código do form:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ContaPalavrasArquivo
{
public partial class ContaOcorrencias : Form
{
public string palavra;
public bool ignoreCase;
public ContaOcorrencias()
{
InitializeComponent();
}

private void buttonContaOcorrencias_Click(object sender, EventArgs e)
{
palavra = textBoxPalavra.Text;
if (checkBoxDifMaiuscula.Checked == true)
ignoreCase = false;
else
ignoreCase = true;
this.Close();
}
}
}

Agora sim, vamos codificar nosso botão “Conta Ocorrencias”

private void button1_Click(object sender, EventArgs e)
{
ContaOcorrencias formContaOcorrencias = new ContaOcorrencias();
formContaOcorrencias.ShowDialog();
string palavra = formContaOcorrencias.palavra;
int contador = arquivo.contaOcorrencias(textBoxTexto, palavra, formContaOcorrencias.ignoreCase);
textBoxNOcorrencias.Text = contador.ToString();

}
Bom, executando temos os seguintes resultados:






sábado, 4 de junho de 2011

Métodos não estáticos

Olá Pessoal, hoje vamos focar em outra linguagem de programação, a linguagem Csharp. Vamos ver a diferença entre métodos estáticos e métodos não estáticos.

Para isso, vamos criar uma classe, chamada "operacoes", nela vamos inserir dois métodos, o primeiro o método estático chamado "soma" e o segundo, que não é estático, chamado "subtracao".


Veja a classe abaixo:

class operacoes
{
public static int soma(int numero01, int numero02)
{
return numero01 + numero02;
}

public int subtacao(int numero01, int numero02)
{
return numero01 - numero02;
}
}


Bom, agora, vamos definir um form com o layout como o abaixo:


Vamos clicar no botão "Soma" e colocar o seguinte código dentro dele:

textBoxResultSoma.Text = operacoes.soma(int.Parse(textBoxSomaN1.Text), int.Parse(textBoxSomaN2.Text)).ToString();



Assim, quando informarmos os valores para "Num 1" e "Num 2" teremos a soma dos dois números, como segue:



Agora, vamos realizar a operação de subtração. Para isso, vamos colocar o seguinte código, no botão "Subtração"

operacoes op = new operacoes();

textBoxResultSub.Text = op.subtacao(int.Parse(textBoxSubN1.Text), int.Parse(textBoxSubN2.Text)).ToString();




Veja a diferença, precisamos instanciar um novo objeto, para termos acesso a operação de subtração, pois a mesma não é estática, forçando o uso do operador "new" para instanciarmos um novo objeto.