quarta-feira, 30 de dezembro de 2015

Game Jolt

Neste artigo apresento um site dedicado a publicar jogos indie, o GameJolt.

Atualmente tenho dois jogos publicados:

Bouncing World
http://gamejolt.com/games/bouncingworld/95233


Ground Protection
http://gamejolt.com/games/ground-protection/116880

sexta-feira, 17 de abril de 2015

Guardar Imagens numa base dados do Access

Uma discussão que nunca terá fim diz respeito ao melhor modo de guardar imagens num servidor, dentro da base de dados ou somente em pastas utilizando o sistema de ficheiros?

Em minha opinião, como tudo na vida, depende da situação. Mas adiante, neste post vou mostrar como se podem guardar imagens dentro de uma base de dados do Access utilizando C#.

Começamos por criar um projeto novo no VS 2013.





De seguida vamos criar uma classe para gerir a base de dados.


Nesta classe precisamos de adicionar uma referência para uma DLL que permite criar o ficheiro da base de dados do Access.





No gestor de referências pesquisas na secção COM.

De volta à classe adicionamos um namespace: using ADOX;


 Agora adicionamos três propriedades à nossa classe: o caminho para a base de dados, uma string de ligação e um objeto de ligação à base de dados.

    class BaseDados
    {
        string caminhoBD;
        string strLigacao;
        OleDbConnection ligacaoBD;
    }

Para o objeto de ligação precisamos de outro namespace: using System.Data.OleDb;

 No construtor da classe vamos definir a string de ligação, o caminho para o ficheiro da base de dados e executar as funções que vão criar a base de dados e a tabela que vai conter as imagens.

        //construtor
        public BaseDados()
        {
            caminhoBD = Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData) + @"\BD_Access";
            if (Directory.Exists(caminhoBD) == false)
                Directory.CreateDirectory(caminhoBD);

            string nomeBD = @"\myAccessFile.accdb";
            caminhoBD += nomeBD;
            Console.WriteLine(caminhoBD);
            strLigacao = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoBD + ";";
            strLigacao += "Jet OLEDB:Database Password='12345';";  
            createDB();
            openDB();
            createTables();
        }
Na string de ligação foi definida a palavra passe de acesso à base de dados.

O próximo passo é criar essas funções:

        private void createDB()
        {
            if(File.Exists(caminhoBD)==false)
            {
                Catalog cat = new Catalog();
                cat.Create(strLigacao);
            }
        }

        private void openDB()
        {
            try
            {
                ligacaoBD = new OleDbConnection(strLigacao);
                ligacaoBD.Open();
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message);
            }
        }

        private void createTables()
        {
            string strSQL = "CREATE TABLE Images(";
            strSQL += "_id COUNTER,";
            strSQL += "_name VARCHAR(200),";
            strSQL += "_image OLEOBJECT,";
            strSQL += "PRIMARY KEY(_id));";
            OleDbCommand comando = new OleDbCommand(strSQL, ligacaoBD);
            try
            {
                comando.ExecuteNonQuery();
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message);
            }
            comando.Dispose();
            comando = null;
        }
Deve-se ter algum cuidado com o nome dos campos pois o Access não aceita determinadas palavras por serem reservadas.

Para fechar a base de dados vamos criar um destrutor para a classe
        //destrutor
        ~BaseDados()
        {
            closeDB();
        }

        private void closeDB()
        {
            try
            {
                ligacaoBD.Close();
                ligacaoBD.Dispose();
                ligacaoBD = null;
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message);
            }
        }

Agora necessitamos duas funções. Uma para ler um ficheiro e devolver o seu conteúdo num vetor de bytes, que posteriormente será inserido no campo do tipo OLEOBJECT, e outra função que recebe um vetor de bytes e cria um ficheiro com o seu conteúdo.
Assim vamos criar uma Helper classe para conter essas duas funções do tipo static para que não seja necessário criar um objeto antes de as utilizar.

    class Helper
    {
        static public byte[] ImagemParaVetor(string imagem)
        {
            FileStream fs = new FileStream(imagem, FileMode.Open, FileAccess.Read);
            byte[] dados = new byte[fs.Length];
            fs.Read(dados, 0, (int)fs.Length);
            fs.Close();
            fs.Dispose();
            return dados;
        }

        static public void VetorParaImagem(byte[] imagem, string nome)
        {
            FileStream fs = new FileStream(nome, FileMode.Create, FileAccess.Write);
            fs.Write(imagem, 0, imagem.GetUpperBound(0));
            fs.Close();
            fs.Dispose();
        }
    }

Para inserir uma imagem na base de dados primeiro temos de a escolher, assim construimos o seguinte formulário.


O primeiro botão vai permitir escolher a imagem.

         private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ficheiro = new OpenFileDialog();

            ficheiro.Filter = "Imagens|*.JPG;*.PNG";
            if (ficheiro.ShowDialog() == DialogResult.Cancel) return;
            string nome = ficheiro.FileName;

            pictureBox1.Image = Image.FromFile(nome);
            lbFile.Text = nome;
        }

Para o segundo botão necessitamos de criar uma função na classe que recebe a imagem a inserir e executa o SQL necessário.

         private void button2_Click(object sender, EventArgs e)
        {
            byte[] image = Helper.ImagemParaVetor(lbFile.Text);
            bd.insertImage(textBox1.Text, image);
        }

Neste segundo botão fazemos uso da função definida anteriormente, passando-lhe o nome do ficheiro e recebendo o vetor de bytes com a imagem.

A função na classe fica assim:

public bool insertImage(string name, byte[] foto)
        {
            string strSQL = "INSERT INTO Images (_name,_image) VALUES (?,?);";
            OleDbCommand comando = null;
            try
            {
                comando = new OleDbCommand(strSQL, ligacaoBD);
                ////////////////////////////////preencher os parâmetros
                comando.Parameters.AddWithValue("?", name);
                comando.Parameters.AddWithValue("?", foto);
                ////////////////////////////////executar o comando
                comando.ExecuteNonQuery();
            }
            catch (Exception erro)
            {
                Console.WriteLine(erro.Message);
                comando.Dispose();
                comando = null;
                return false;
            }
            comando.Dispose();
            comando = null;
            return true;
        }

Agora vamos criar um segundo formulário para listar todas as imagens existentes na base de dados.

O formulário vai conter um painel para fazer o scroll vertical pelas imagens que são adicionadas como pictureboxes ao painel.

 public partial class Form2 : Form
    {
        Form1 f = Application.OpenForms[0] as Form1;
        BaseDados bd;
        Panel panel1 = new Panel();

        public Form2()
        {

            InitializeComponent();
            bd = f.bd;
            panel1.Dock = DockStyle.Fill;
            panel1.AutoScroll = true;
            this.Controls.Add(panel1);
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            DataTable dados = bd.listImages();
            int count=0;
            foreach(DataRow row in dados.Rows)
            {
                PictureBox pb = new PictureBox();
                pb.Name = row[0].ToString();
                pb.Parent = this.panel1;
                pb.Size = new Size(320, 240);
                pb.SizeMode = PictureBoxSizeMode.StretchImage;
                pb.Location = new Point(0, count * 240);

                //criar um ficheiro com a imagem
                byte[] image=(byte[])row[2];
                Helper.VetorParaImagem(image, "temp"+count+".jpg");
                pb.Image = Image.FromFile("temp"+count+".jpg");

                count++;
            }
        }

        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach(Control ctrl in this.Controls)
            {
                ctrl.Dispose();
            }
            GC.Collect();
        }
    }


Primeiro é criada uma referência para o formulário principal que já contém um objeto do tipo BaseDados, que vamos utilizar aqui para aceder à base de dados.

Na base de dados adicionamos uma função para devolver todos os registos.

        public DataTable listImages()
        {
            DataTable dados = new DataTable();
            string strSQL = "SELECT * FROM Images;";
            OleDbDataReader registos;
            OleDbCommand comando = new OleDbCommand(strSQL, ligacaoBD);

            registos = comando.ExecuteReader();

            dados.Load(registos);

            return dados;
        }

Quando o formulário é fechado forçamos a libertação de todos os controlos para evitar erros no acesso aos ficheiros que são criados com as imagens.

O resultado final é o seguinte.

O projeto.

sexta-feira, 27 de março de 2015

Web Service em C#

Depois do artigo sobre um web service em PHP segue agora um artigo sobre como implementar o mesmo serviço com o Visual Studio e .NET utilizando C#.

Começamos por criar um projeto ASP.NET com o Visual Studio 2013.





De seguida escolhemos uma aplicação vazia.

Agora adicionamos um novo item ao projeto.



Escolhemos do tipo Web Service.


Com esta opção o Visual Studio gera a estrutura básica de um Web Service.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace WebApplication2
{
    /// <summary>
    /// Summary description for WebService1
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class WebService1 : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World";
        }
    }
}

Esta estrutura disponibiliza um método por defeito designado HelloWorld. Neste artigo vamos criar um método chamada lista que vai receber um parâmetro indicando o formato do output a ser gerado.

 [WebMethod]
        public string lista(string op)
        {
            if(op=="json"||op=="JSON")
                return devolveRegistosJSON();

            return devolveRegistosXML();
        }

Este método vai, em função do valor recebido pelo parâmetro, chamar uma função que devolve os registos em formato JSON ou em formato XML.

Começamos pelo formato JSON.

public string devolveRegistosJSON()
        {
            var jsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            string registos = "";
            string strligacao = "Database=base_dados;Server=servidor;UID=utilizador;PWD=password;";
            MySqlConnection ligacao = new MySqlConnection(strligacao);
            ligacao.Open();
            string strSQL = "SELECT * FROM test_users;";
            MySqlCommand comando = new MySqlCommand(strSQL, ligacao);
            MySqlDataReader dados = comando.ExecuteReader();
            List<Registo> lDados = new List<Registo>();
            while (dados.Read())
            {
                lDados.Add(new Registo(int.Parse(dados[0].ToString()), dados[1].ToString(), dados[2].ToString()));
            }
            registos = jsonSerializer.Serialize(lDados);
            lDados.Clear();
            dados.Dispose();
            comando.Dispose();
            ligacao.Close();
            ligacao.Dispose();
            return registos;
        }

Neste caso utilizamos um serializador em formato JSON para converter os dados carregados para a lista a partir da base de dados MySQL.

Não nos podemos esquecer de adicionar a referência ao MySQL bem como a class Registo, que se apresenta a seguir:

        [Serializable]
        public class Registo
        {
            public int id { get; set; }
            public string name { get; set; }
            public string country { get; set; }
            public Registo(int id, string name, string country)
            {
                this.id = id;
                this.name = name;
                this.country = country;
            }
        }

Agora a função que devolve os dados em formato XML:

        public string devolveRegistosXML()
        {
            string registos = "";
            string strligacao = "Database=base_dados;Server=server;UID=utilizador;PWD=password;";
            MySqlConnection ligacao = new MySqlConnection(strligacao);
            ligacao.Open();
            string strSQL="SELECT * FROM test_users;";
            MySqlCommand comando = new MySqlCommand(strSQL,ligacao);
            MySqlDataReader dados = comando.ExecuteReader();
            while (dados.Read())
            {
                string registo = "";
                registo=adicionarTag("id", dados[0].ToString());
                registo += adicionarTag("name", dados[1].ToString());
                registo += adicionarTag("country", dados[2].ToString());
                registos += adicionarTag("registo", registo);
            }
            registos = adicionarTag("registos", registos);
            dados.Dispose();
            comando.Dispose();
            ligacao.Close();
            ligacao.Dispose();
            return registos;
        }

Neste caso necessitamos da função adicionarTag que é utilizada para facilitar a criação dos nós XML.

        public string adicionarTag(string tag, string texto)
        {
            string temp = "<" + tag + ">" + texto + "</" + tag + ">";
            return temp;
        }

Agora ao testar o serviço somos recebidos por uma página assim:


Ao escolher o link lista chegamos a uma página que permite invocar o método bem como definir o valor do parâmetro.

Se a opção introduzida for XML

Se a opção introduzida for JSON

O projeto

Web service em PHP

Neste artigo vamos utilizar PHP para criar um serviço web que devolve dados a partir de uma base de dados MySQL em formato XML ou JSON.

Para a consulta dos dados vamos utilizar MySQLi.

O serviço não vai requerer nenhum tipo de credênciais nem limites ao número de pedidos processados o que pode ser necessário em determinadas situações.

Começamos por definir os parâmetros para estabelecer a ligação ao servidor de base de dados.

<?php
$server="servidor";
$user="utilizador";
$password="password";
$database="base_dados";
?>


De seguida temos o código do serviço propriamente dito.

<?php

require "config.php";
//ligar à base de dados com objeto
$ligacao=new mysqli($server,$user,$password,$database);
//verificar ligação
if($ligacao->connect_error)
die("Erro na ligação: ".$ligacao->connect_error);

//parametros
if(isset($_GET['format']))
$output=$_GET['format'];
else
$output="xml"; //por defeito
if($output!='JSON'&&$output!='json'&&$output!='xml'&&$output!='XML')
$output="xml";
//dados
$sql="SELECT * FROM test_users";
$resultado=$ligacao->query($sql);
if($resultado->num_rows>0){
while($registo=$resultado->fetch_assoc())
$registos[]=array("registo"=>$registo);

header("Cache-Control: no-cache, must-revalidate");
if($output=="xml"||$output=="XML"){
$conteudo="Content-type: text/xml; charset=utf-8";
header($conteudo);
$linhas="";
$linha="";
foreach($registos as $index => $registo)
{
if(is_array($registo)){
foreach($registo as $campo => $valor){
if(is_array($valor)){
foreach($valor as $tag => $val)
$linha.=adicionarTag($tag,htmlentities($val));
}
$linha=adicionarTag("registo",$linha);
}
}
$linhas = $linhas.$linha;
$linha="";
}
echo adicionarTag("registos",$linhas);
}else{
$conteudo="Content-type: application/json; charset=utf-8";
header($conteudo);
echo json_encode(array('registos'=>$registos));
}
}
function adicionarTag($tag,$texto){
$temp="<".$tag.">".$texto."</".$tag.">";
return $temp;
}
?>



Este código pode ser picado aqui.

quinta-feira, 26 de março de 2015

Consumir um serviço web com C#

Neste artigo vamos aprender como consumir um serviço web com uma aplicação desktop criada em C#.

Um serviço web (web service) é uma API disponível através da Web que pode ser utilizada desde que se respeite as condições de uso. Em alguns casos os web services são gratuítos e não requerem nenhum registo ou credênciais, no entanto, existem outros serviços que depende de um registo e até de um pagamento.

Para demonstrar o conceito vamos utilizar um serviço oferecido pelo Banco Central Europeu disponível no seguinte link http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml.
Este serviço devolve as cotações diárias de moedas em função do valor do euro. Os dados são codificados em formato XML.

O pedido dos dados não pode acontecer na thread principal do programa uma vez que isso provocaria a paragem da interface. Assim é necessário recorrer a um pedido assincrono numa thread separada da thread responsável pela atualização a interface gráfica (GUI).

O código que vai permitir consumir os dados é o seguinte:

async void downloadXML()
        {
            string url = "http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml";
            WebRequest pedido = WebRequest.Create(url);
            WebResponse resposta = await pedido.GetResponseAsync();
            XDocument documento = XDocument.Load(resposta.GetResponseStream());
            textBox1.Text = documento.ToString();

            //preencher a listview
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(documento.ToString());
            XmlNodeList lista = xml.GetElementsByTagName("Cube");
            foreach (XmlNode no in lista)
            {
                if (no.Attributes.Count > 1)
                {
                    ListViewItem novo = new ListViewItem(no.Attributes[0].InnerXml);
                    novo.SubItems.Add(no.Attributes[1].InnerXml);
                    listView1.Items.Add(novo);
                }
            }
        }


Trata-se de uma função assincrona que é chamada a partir de um clique num botão.
Como se pode verificar na linha de definição da função é utilizada a palavra reservada async.
Esta foi introduzida no Visual Studio 2012 e permite simplificar o trabalho de criar uma função assincrona deixando a parte mais dificil para o compilador (Ler mais sobre o assunto).
A execução corre normalmente até encontrar a linha com o termo await onde o controlo da execução é devolvido para a linha que chamou a função assincrona, não bloqueando a interface, enquanto espera que o processamento dessa linha termine para continuar até ao final da função.

O resto da função permite manipular os dados recebidos em formato XML.

Projeto

sexta-feira, 9 de janeiro de 2015

Arduino através de PHP

Neste post vamos criar um projeto que vai implicar utilizar três linguagens de programação diferentes e tudo para permitir comunicar com um Arduino através do browser.

O objetivo é criar uma página Web que permitirá enviar e receber dados de um Arduino ligado a um servidor Web.

No Arduino implementamos um protocolo muito simples de comunicação que faz a interação com os sensores e comunica com o servidor Web.

O servidor Web é um computador com Windows a correr o Apache e a linguagem de programação utilizada para a criação das páginas será o PHP.

Para permitir a comunicação do PHP com a porta USB do Arduino será desenvolvimento um pequeno programa em C#, modo Consola, que será chamado a partir do PHP com alguns parâmetros e que posteriormente fará a ligação ao Arduino.

Apesar de existirem livrarias de funções em PHP para utilizar a porta série não consegui fazer com que funcionassem em Windows.



No Arduino vamos ter um servo ligado ao pino 9, um LED ligado ao pino 12 e um LDR no pino A0. O pino 9 e o pino 12 são digitais, o pino A0 é um pino analógico. Os pinos analógicos estão ligados a um ADC (Analog Digital Convert) que faz a conversão entre a tensão elétrica recebida e um valor entre 0 e 1024. O valor máximo, por defeito, é de 5 volts que corresponde a 1024.
O pino 9 é um pino com função PWM (Pulse Width Modulation) que permite simular o envio de valores entre 0 (LOW) e 1 (HIGH), é esta função que permite fazer efeito de fade com LEDs.

Neste caso vamos ter um protocolo muito simples que respeita as seguintes regras:
- Se o valor recebido pelo Arduino estiver entre 0 e 180, inclusive, este corresponderá ao ângulo de posicionamento do servo.
- Se o valor recebido é 181 então o LED liga ou desliga, de acordo com o estado anterior.
- Se o valor recebido é 182 então o Arduino envia o valor do LDR.

Assim o código do Arduino fica:

 #include <Servo.h>

Servo myservo; 
int pos = 0;   
int led=0;
int ledPort=12;
int ldrPin=A0;
byte nivelLuz=0;

void setup()
{
  myservo.attach(9); 
  Serial.begin(9600);
  pinMode(ledPort,OUTPUT);
}

void loop()
{
    byte valor;
    if(Serial.available()){
    valor=Serial.read();
    if(valor>=0 && valor<=180) myservo.write(valor);
    if(valor==181){
      if(led==0){
        digitalWrite(ledPort,HIGH);
        led=1; 
      }else{
        digitalWrite(ledPort,LOW);
        led=0;
      }
    }
    if(valor==182){
      delay(150);
      nivelLuz=analogRead(ldrPin);
      Serial.println(nivelLuz);
    }
  }
}

A página Web vai conter um formulário simples com três botões para controlar cada um dos dispositivos presentes no Arduino (LED, LDR e o servo).
Cada botão invoca o programa desenvolvido em C# com o comando e a porta séria a utilizar.

O código PHP fica:
<html>
<body>
    <form method="POST">
        <input type="text" name="angulo">
        <input type="submit" name="bt" value="Servo">
        <input type="submit" name="bt_luz" value="Led">
        <input type="submit" name="bt_ldr" value="Luz">
    </form>
</body>
</html>
<?php
    ini_set("display_errors",1);
    if(!empty($_POST)){
        if(!empty($_POST["bt"])){
            echo "POST servo<br>";
            if(empty($_POST["angulo"])){
                echo "Preencha o angulo<br>";
                return;
            }
            $angulo=$_POST["angulo"];
            $comando="console_servo.exe COM18 ".$angulo;
            $resultado=exec($comando);
        }
        if(!empty($_POST["bt_luz"])){
            echo "POST led<br>";
            $resultado=exec("console_servo.exe COM18 181");
        }
        if(!empty($_POST["bt_ldr"])){
            echo "POST ldr<br>";
            $resultado=exec("console_servo.exe COM18 182 er");
            $resultado=(int)$resultado;
        }
        if($resultado<0) $resultado="";
        echo $resultado."<br>";
    }
?>

O programa em C# inclui uma classe que implementa a comunicação com a porta série.
O código do programa começa por verificar os parâmetros que são passados e em função deles utilizar um objeto criado com base na classe de comunicação para enviar e receber os dados do Arduino.

namespace console_servo
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 1)
            {
                string strler = args[0];
                Comunica portaler = new Comunica(strler);
                int valorlido=portaler.lerLinha();
                Console.Write(valorlido.ToString());
                return;
            }
            if (args.Length == 3)
            {
                string strenvia = args[0];
                byte valorenviar;
                if (byte.TryParse(args[1], out valorenviar) == false)
                {
                    Console.Write("Usage: servo porta valor er");
                    return;
                }
                Comunica portalerenviar = new Comunica(strenvia);
                portalerenviar.envia(valorenviar);
                Thread.Sleep(150);
                int valordevolver = portalerenviar.lerLinha();
                Console.Write(valordevolver);
                return;
            }
            if (args.Length != 2)
            {
                Console.Write("Usage: servo porta valor");
                return;
            }
            byte valor = 0;
            if (byte.TryParse(args[1], out valor)==false)
            {
                Console.Write("Usage: servo porta valor");
                return;
            }
            string str=args[0];
            Comunica porta = new Comunica(str);
            porta.envia(valor);
            Console.Write("ok");
        }

    }
    class Comunica
    {
        string porta;
        SerialPort sp;
        string buffer;

        public Comunica(string porta)
        {
            this.porta = porta;
            sp = new SerialPort(porta);
            sp.Open();
        }
        ~Comunica()
        {
            if (sp.IsOpen) sp.Close();
            sp.Dispose();
        }
        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            buffer = sp.ReadExisting();
        }
        public void envia(byte x)
        {
            byte[] valores = new byte[2];
            valores[0] = x;
            sp.Write(valores, 0, 1);
        }
        public byte ler()
        {
            byte[] valores=new byte[2];
           
            sp.Read(valores, 0, 1);
            return valores[0];
        }
        public int lerLinha()
        {
            string linha =sp.ReadLine();
            int valor = 0;
            if(int.TryParse(linha, out valor)==false)
                return -1;
            else
                return valor;
        }
    }
}

O projeto completo pode ser descarregado aqui.

segunda-feira, 31 de março de 2014

Numeração Romana

Vamos hoje fazer um pequeno programa que converte os números da numeração árabe para a numeração romana.

Segundo a Wikipédia "O sistema de numeração romana (ou números romanos) desenvolveu-se na Roma Antiga e utilizou-se em todo o seu Império."

Uma vez que os romanos não conheciam o 0 (zero) vamos limitar o nosso programa a valores entre 1 e 3999.



Para começar definimos dois vetores:

Dim valores As Integer() = New Integer() {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
Dim romanos As String() = New String() {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}

O primeiro tem os valores inteiros que correspondem a determinadas letras que se encontram no segundo.
O segundo vetor contém as letras que são combinadas, ou não, e que permitem representar os valores na numeração romana.

Para fazermos a conversão vamos criar uma função que recebe o valor inteiro árabe e devolve a string com o valor em numeração romana.

Public Function paraRomanos(numero As Integer) As String

Nesta função utilizamos um ciclo que vai percorrer os valores do primeiro vetor e enquanto o valor a converter for maior que o valor existente no vetor subtrai esse valor do valor original, ou seja, começa em 1000 e se o número é superior a 1000 subtrai este valor do original até restar um valor menor que 1000 para passar à segunda posição do vetor.
De cada vez que um valor é subtraído é adicionada a letra ou letras correspondentes a esse valor a uma string onde a numeração romana está a ser construída.

For i = 0 To 12
            Do While numero >= valores(i)
                numero -= valores(i)
                resultado.Append(romanos(i))
            Loop
Next

No final a função devolve a string com a numeração romana.

Return resultado.ToString()

A função completa fica assim:
    Public Function paraRomanos(numero As Integer) As String
        Dim valores As Integer() = New Integer() {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
        Dim romanos As String() = New String() {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
        Dim i As Integer
        Dim resultado As New StringBuilder()

        If numero > 3999 Or numero < 1 Then
            MessageBox.Show("Erro! Deve introduzir valores entre 1 e 3999")
            Return ""
        End If
        For i = 0 To 12
            Do While numero >= valores(i)
                numero -= valores(i)
                resultado.Append(romanos(i))
            Loop
        Next
        Return resultado.ToString()
    End Function

O projeto em Visual Basic pode ser retirado aqui.