quarta-feira, 27 de outubro de 2010

Como hoje estou de bom humor ... campanha!

A mensagem está dada.

De pensar que um jogo pra crianças incentivava essas coisas ...

Vida de Programador ... Estranho

Cara, hoje eu sou programador, mas já trabalhei 2 anos com suporte e daí eu tirei as histórias mais hilárias da minha carreira. (e perdi 70% dos meus cabelos também).

Mas você pensa que o único problema do pessoal do suporte são os usuários? Nada, os programadores são boa parte do problema. No blog Vida de Suporte me identifiquei muito com o personagem Mauro que fala .... estranho. Eu falo igualzinho quando dá algum problema.

A explicação é simples: se o programador desenvolve sem uma metodologia fixa (vamos desconsiderar aqui as pressões por preço e prazo) ele testa a aplicação num universo ou contexto familiar a ele.

Quando ocorre algum problema ou é descoberto um bug, ou a maneira de reproduzir o bug é desconhecida do universo de testes do programador ou ele até já esqueceu que funcionalidade é esta que ele construiu, o que ela fazia, para que servia e como usá-la.

Ele só sabe que não era para dar erro. Se der é no mínimo ..... estranho. Agora vai saber (ou imaginar) por que ocorreu o erro e se o problema não é inconsistência dos dados legados, portados, repetidos ou "mal-inseridos".





Tem também o estressado Gerson, um programador Delphi que bota medo até em gerente.


Bom pessoal, as tirinhas do André Farias são muito legais, vale a pena ler.

segunda-feira, 25 de outubro de 2010

Obtendo e formatando datas no .Net com C# e Iron Python

Formatos para obter a data:
1.  Use dd para obter simplesmente o dia
2. Use ddd para obter um nome curto para o dia 
3. Use dddd para obter  o nome completo do dia
4. Use MM em maiúsculo para obter o mês
5. Use yyyy para obter o ano com 4 dígitos
6. Use hh para obter as horas
7. Use mm em minúsculo para obter os minutos
8. Use ss para obter os segundos


Código no C# para imprimir a data (no console)

using System;
using System.Text;
namespace TesteDatas
{
    class FormatoData
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current Date and Time:");
            Console.WriteLine(System.DateTime.Now);
            Console.WriteLine("Data: {0:dd}", System.DateTime.Now);
            Console.WriteLine("Dia: {0:ddd}", System.DateTime.Now);
            Console.WriteLine("Dia Longo: {0:dddd}", System.DateTime.Now);
            Console.WriteLine("Data Completa: {0:dddd - dd \\de MMMM \\de yyyy - hh:mm:ss}", System.DateTime.Now);
            Console.ReadLine();
        }
    }
}

Repare que na string de formatação temos um \\ antes do "De". Duas barras significa que estamos usando uma barra como caracter de escape para escrever uma barra dentro de uma string de formatação dentro de outra string. Então duas contrabarras são viram uma contrabarra (uma é escape e a outra é o caractere em si) e uma contrabarra + d serve para imprimir/mostrar a letra d de "de" em vez do dia.


Agora em Iron Python:


Depois de instalar o Iron Python adicione "C:\Arquivos de programas\IronPython 2.7\" na variável de ambiente path ou digite no cmd path "C:\Arquivos de programas\IronPython 2.7\"

Crie o arquivo Datas.py assim:  (Sem identações)


import System

System.Console.WriteLine("Data:")
System.Console.WriteLine(System.DateTime.Now)
System.Console.WriteLine("Data: {0:dd}", System.DateTime.Now)
System.Console.WriteLine("Dia: {0:ddd}", System.DateTime.Now)
System.Console.WriteLine("Dia Longo: {0:dddd}", System.DateTime.Now)
System.Console.WriteLine("Data Completa: {0:dddd - dd \\de MMMM \\de yyyy - hh:mm:ss}", System.DateTime.Now)
System.Console.ReadLine()


Execute com o comando ipy Datas.py


O resultado será o mesmo conseguido com C#. 


Have Fun ;)

domingo, 24 de outubro de 2010

Como "matar" um processo no Delphi

Para fechar um programa executável pelo seu nome crie a seguinte função:


function killtask(ExeFileName: string): Integer;
const
  PROCESS_TERMINATE = $0001;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  Result := 0;
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);

  while Integer(ContinueLoop) <> 0 do
  begin 
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeFileName))) then 
      Result := Integer(TerminateProcess(
                        OpenProcess(PROCESS_TERMINATE,
                                    BOOL(0),
                                    FProcessEntry32.th32ProcessID),
                                    0));
     ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;


E use-a assim:

killtask('notepad.exe');

Essa foi uma "dica expressa" só código, sem mais delongas.

Até a próxima ;)

Migração do Live Spaces para o Wordpress

Uma boa notícia (ou não) para quem tem um blog no Live Spaces mas acha uma droga. A Microsoft, em parceria com o Wordpress resolveu migrar os blogs feitos no Live para a plataforma Wordpress.

Eu acho isso uma boa idéia pois assim a microsoft pode focar no que ela tem de melhor e realmente sabe fazer, e deixar a parte que ela não tem muito know how para terceiros.

Pelo calendário da microsoft você tem até 4 de janeiro de 2011 para baixar o conteúdo e migrar, até essa data você ainda pode postar conteúdo. Passando de 4 de janeiro o blog será travado para novas pstagens, permitindo-se apenas consultas e o download do conteúdo. No dia 16 de março de 2011 o live spaces será fechado de vez.

Veja abaixo a mensagem original que enviaram para os usuários:



Dear Windows Live Spaces customer,
We are very excited to announce our collaboration with a premier and innovative blogging service, WordPress.com, to offer you an upgraded blogging experience. We'll help you migrate your current Windows Live Spaces blog to WordPress.com or you can download it to save for later. On March 16th, 2011 your current space will close.
With the new release of Windows Live services, we've made a series of changes and improvements across our products. We chose to partner to provide our users with a fantastic blogging solution. However, we realize the changes will have an impact on you - this email aims to address any concerns you may have.

Why is this happening?
Our customers have asked for richer blog functionality including an integrated statistics system, continuous saving of drafts and improvements to spam-fighting technology. To deliver the best possible blogging experience, we are collaborating with WordPress.com to provide their free service to you. For those of you that already have a blog on Windows Live Spaces, we will make it easier for you to get started while helping you move what you've already built up on Spaces.


What is the timeline?
Starting the end of September, 2010, when you visit your Windows Live Space you'll be given the opportunity to upgrade your blog by migrating it to WordPress.com and to download your content to save for later. 

As of January 4th, 2011, you won't be able to make changes to your Spaces blog, but you can continue to review past posts, download your content to save for later and upgrade your blog to WordPress.com. 

On March 16th, 2011 Windows Live Spaces will close and you will not be able to access or migrate your blog on Spaces.


What you need to do before Windows Live Spaces closes
Starting the end of September, when you visit your space you'll have the following options:
  • Upgrade your blog by migrating to WordPress.com - We will provide a simple way to move your blog posts and comments to WordPress.com.
  • Download your blog – You can download your old posts to keep a copy with you. You can also do this and then migrate to WordPress.com.
  • Delete your space - If you decide that you do not want to have a space anymore, you have the option to delete it permanently. If you want to save your content, please make sure to do that before deleting your space.
  • If you can't decide, take some time – Over the next few months, Windows Live Spaces will continue to be accessible while you make your decision. But we're very excited about what you can do on WordPress.com and hope you'll decide to take advantage of this improved blogging experience.
Note: some content such as gadgets, guestbook, lists, notes and draft posts won't be migrated. See FAQs for more info on how to preserve this type of content. 

Go to your space to choose an option that's right for you.


What you can expect by moving to WordPress.com
All of your posts, comments, and links will transfer, and you will have the option to share your blogging updates with your Messenger friends.

On WordPress.com, you'll get tools to help you track how your blog is doing and who's visiting. You'll get tagging that enables people to find you, and for you to find like-minded people. It includes great blog comment functionality, and trackback spam prevention to help keep your experience clean. Check out more.
Thanks for using Windows Live and we hope you enjoy the new blogging experience.
Sincerely,
The Windows Live team
Frequently Asked Questions
Q: What is WordPress.com ?
A: WordPress.com is a free blogging site, similar to Windows Live Spaces. On WordPress.com, you'll find great themes and widgets to customize your experience, tools to help you track how your blog is doing and who's visiting. You'll get tagging that enables people to find you, and for you to find like-minded people. It includes great blog comment functionality, and trackback spam prevention to help keep your experience clean.


Q: What happens to my Windows Live Spaces content and what can I migrate?
A: Here's a list of different functionality available on Windows Live Spaces today and what you can expect:

  • Blogs and comments: If you choose to migrate or download, your blog content (including inserted photos, videos and comments) comes with you.
  • Photos: Photos not part of your blog but part of Windows Live will continue to live on SkyDrive and, if you choose, you can continue to share them with others. Photos that were part of your blog will be migrated if you choose to migrate your blog. If you add any other modules provided by other services that shared your photos, those will not be migrated.
  • Visitors will know where to find you: If you migrate, existing links to your blog and specific articles will continue to work and your visitors will be redirected to your new location on WordPress.com. You'll also be able to keep your friends up to date with your latest posts on Windows Live Messenger.          
  • Private blogs: If you have a private blog, your blog will be checked as private unless you choose another option during migration. You'll be able to share your blog back with your Windows Live Messenger friends or just choose to select a few people to invite to WordPress.com.
  • Gadgets, guestbook, lists, notes, and draft posts: Unfortunately, you won't be able to move these. You might consider publishing your draft posts over the coming months and moving content in lists and notes into your blog before migrating.
  • Profile and Contacts modules: That information stays on Windows Live. You can add your new blog to your Profile and share it with your friends.


Q: What can I expect between now and the date Windows Live Spaces closes?
A: Starting at the end of September 2010, when you visit your space you'll be given the opportunity to upgrade your blog by migrating it to WordPress.com and to download your content to save for later. Your space will continue to be available for you to publish to. As of January 4th, 2011, you will lose the ability to make changes to your Windows Live Spaces blog, but you can continue to review past posts, download your content to save for later and upgrade your blog to WordPress.com. On March 16th, 2011, Windows Live Spaces will close and you will not be able to access or migrate your blog.


Q: Where can I learn more?
A: Are you a parent of a child who uses Windows Live Spaces? Do you have a private blog? Do you use Writer to publish to Windows Live Spaces? Do you have other questions? Please visit our Help Center for additional information and resources.


Eu mostrarei a seguir como fazer a migração:

1) Clique no link logo na primeira página:
Clique em "Começar"
 2) Você será avisado de que as listas, gadgets, recados e rascunhos não serão portados e isso é irreverível. "E eu tenho escolha?" Vamo que vamo.

        
3) Quando solicitar que você se logue, use seu login do live spaces.

4) Escolha domínio, título, linguagem e fuso horário.

5) Clique em "Criar Blog" e seja o que Deus quiser.


Depois de passada a barra de progresso você terá um novo blog (ou não) http://vitorrubio.wordpress.com/

Acho que vou aproveitar o blog novo, já que o wordpress é infinitamente melhor que o live spaces, e vou postar nele todos os assuntos referentes a reflexões, protestos, reclamações etc. Assim eu deixo este blog dedicado apenas à programação.

Have fun (ou não) :p

Olá, mundo!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

sexta-feira, 22 de outubro de 2010

Barrinha Social Wibia

Sabe essa barrinha de ferramentas vista no rodapé deste blog? Ela é feita pelo site http://www.wibiya.com/

Entre no site, cadastre-se e aqui você poderá instalá-la. O site publicará um widget no seu blogspot, a única coisa que você precisa fazer agora é coloca o widget em um lugar mais apropriado e talvez remover o título.




Será colocado um script como esse

<script src="http://cdn.wibiya.com/Toolbars/dir_[[dir id]]/Toolbar_[[seu id]]/Loader_[[seu id]].js" type="text/javascript"></script>

No próximo post falaremos da barra do meebo

Até lá ;)

Hook sem usar bibliotecas externas

Hooking, ou API Hooking é quando substituimos a entrada de um método qualquer por um método nosso. Assim todas as chamadas de um determinado método chamarão na verdade o método novo.

Existem outras técnicas e bibliotecas para fazer isso. Para dizer a verdade eu não sou expert nesse assunto. Geralmente as bibliotecas tem uma opção/função de callback onde você armazena uma referência à função original, assim pode chamar a função original dentro da substituta, antes ou depois do seu próprio código, para que ela possa ser mais parecida ou equivalente à original, com pouco esforço da sua parte.

Nesta biblioteca que fizemos aqui nós não temos essa opção. Uma vez substituindo a função original  não teremos mais acesso a ela, só a nova, até o momento do UnHook. Além disso não podemos chamar a função original, apenas a nova. Chamar a original dentro da nova é como a nova chamar ela mesma, e isso resulta em loop recursivo e stack overflow.

Isso significa que você é obrigado a sobrescrever inteiramente a lógica da nova.

Esses tipos, funções de API e algoritmos eu retirei (copiei) de sites que fazem patches em bugs da VCL para versões antigas do Delphi, ou que fazem substituições ao Memory Manager. O FastMM por exemplo tem um código muito parecido com esse para substituir o memory manager nativo do delphi pelo proposto por eles.

Não entendo plenamente como e porque funciona isso aqui, mas .... funciona. Quem puder me dar uma ajuda para elucidar e complementar esse assunto aqui neste blog agradeço.

Abaixo o código que eu dei uma alterada:
unit uLazHook;
{**************************************************************************************************}
{                                                                                                  }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
{ License at http://www.mozilla.org/MPL/                                                           }
{                                                                                                  }
{ The Original Code is VCLFixPack.pas.                                                             }
{                                                                                                  }
{ The Initial Developer of the Original Code is Andreas Hausladen (Andreas.Hausladen@gmx.de).      }
{ Portions created by Andreas Hausladen are Copyright © 2008 Andreas Hausladen.                  }
{ All Rights Reserved.                                                                             }
{                                                                                                  }
{**************************************************************************************************}

{$mode objfpc}{$H+}

{$R-} // range check off
interface

type

  //abaixo alguns tipos de estruturas para obter informações da função ReadProcessMemory
  TJumpOfs = Integer;
  PPointer = ^Pointer;

  //esse serve para armazenar a distância entre os pontos de entrada da função original e da sua
  PXRedirCode = ^TXRedirCode;
  TXRedirCode = packed record
    Jump: Byte;
    Offset: TJumpOfs;
  end;

  //não faço idéia ;P
  PWin9xDebugThunk = ^TWin9xDebugThunk;
  TWin9xDebugThunk = packed record
    PUSH: Byte;
    Addr: Pointer;
    JMP: TXRedirCode;
  end;

  //menos ainda
  PAbsoluteIndirectJmp = ^TAbsoluteIndirectJmp;
  TAbsoluteIndirectJmp = packed record
    OpCode: Word;   //$FF25(Jmp, FF /4)
    Addr: PPointer;
  end;

  { TLazHook }
  TLazHook = class
  private
         FProc: Pointer;
         FDestinationProc: Pointer;
         FBackupRedirCode: TXRedirCode;
  public
        constructor Create(Proc, Dest: Pointer);
        destructor Destroy; override;
  end;


implementation
uses
  SysUtils,
  Windows,
  Classes;



//obtem o endereço do método baseado no tipo de SO
function GetActualAddr(Proc: Pointer): Pointer;

  function IsWin9xDebugThunk(AAddr: Pointer): Boolean;
  begin
    //não faço idéia do que significam esses endereços
    Result := (AAddr <> nil) and
              (PWin9xDebugThunk(AAddr)^.PUSH = $68) and
              (PWin9xDebugThunk(AAddr)^.JMP.Jump = $E9);
  end;

begin
  if Proc <> nil then
  begin
    if (Win32Platform <> VER_PLATFORM_WIN32_NT) and IsWin9xDebugThunk(Proc) then
      Proc := PWin9xDebugThunk(Proc)^.Addr;
    //não faço idéia do que significa esse $25ff
    if (PAbsoluteIndirectJmp(Proc)^.OpCode = $25FF) then
      Result := PAbsoluteIndirectJmp(Proc)^.Addr^
    else
      Result := Proc;
  end
  else
    Result := nil;
end;


//cria o hook (substitui uma função na memória pela sua)
procedure HookProc(Proc, Dest: Pointer; var BackupCode: TXRedirCode);
var
  n: DWORD;
  Code: TXRedirCode;
begin
  Proc := GetActualAddr(Proc);
  Assert(Proc <> nil);
  if ReadProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n) then
  begin
    //não sei porque deve ser $E9 nem o que significa, só sei que é 233 em hexadecimal
    Code.Jump := $E9;
    Code.Offset := PAnsiChar(Dest) - PAnsiChar(Proc) - SizeOf(Code);
    WriteProcessMemory(GetCurrentProcess, Proc, @Code, SizeOf(Code), n);
  end;
end;

//faz voltar ao normal
procedure UnhookProc(Proc: Pointer; var BackupCode: TXRedirCode);
var
  n: Cardinal;
begin
  if (BackupCode.Jump <> 0) and (Proc <> nil) then
  begin
    Proc := GetActualAddr(Proc);
    Assert(Proc <> nil);
    WriteProcessMemory(GetCurrentProcess, Proc, @BackupCode, SizeOf(BackupCode), n);
    BackupCode.Jump := 0;
  end;
end;




{ TLazHook }

//atalho para criar o hook com a facilidade de se usar uma classe
constructor TLazHook.Create(Proc, Dest: Pointer);
begin
     FProc:=Proc;
     FDestinationProc:=Dest;
     HookProc(FProc ,FDestinationProc, FBackupRedirCode);
end;

//destruindo o hook e voltando a procedure ao normal
destructor TLazHook.Destroy;
begin
  UnhookProc(FProc, FBackupRedirCode);
  inherited Destroy;
end;

end.


Agora o código usado para testar o Hook. Repare que são necessários 3 passos:
1) Definir a sua propria função, que deve ter a mesma assinatura que a função (método ou procedimento) hookada/substituída.
2) Criar o objeto de Hook passando como parâmetro ponteiros para os métodos de origem e destino e manter uma referência a este objeto até a hora de destruí-lo
3) quando o hook não for mais necessário ou no momento de fechar o programa devemos desfazer o hook.

No exemplo eu uso uma função com a mesma assinatura que a ShowMessage, chamada MyShowMessage, mas que internamente usa a messagebox, para substituir a ShowMessage. Eu comentei esse exemplo e abaixo fiz outro mais complexo, o contrário: eu sobrescrevo a api do windows MessageBox com uma função minha de mesma assinatura mas com chamada ao ShowMessage internamente. É importante aqui o uso de stdcall para indicar a ordem correta dos argumentos da função, pois a api do windows foi feita em C/C++.

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, uLazHook, windows;

type

  { TfrmHook }

  TfrmHook = class(TForm)
    btCriaHook: TButton;
    btMetodoOriginal: TButton;
    btMeuMetodo: TButton;
    btDestroiHook: TButton;
    procedure btCriaHookClick(Sender: TObject);
    procedure btMetodoOriginalClick(Sender: TObject);
    procedure btMeuMetodoClick(Sender: TObject);

  private
    { private declarations }
  public
    { public declarations }
    FHook: TLazHook;
  end;


var
  frmHook: TfrmHook;

  //minha procedure personalizada
  procedure MyShowMessage(const aMsg: string);
  function MyMessageBox(hWnd:HWND; lpText:LPCSTR; lpCaption:LPCSTR; uType:UINT):longint; stdcall; //stdcall pq a api foi feita em C


implementation

//minha procedure personalizada
procedure MyShowMessage(const aMsg: string);
begin

     //algo feito no início

     //chamada a procedure original
     MessageBox(0, pchar(aMsg), 'Mensagem', MB_ICONINFORMATION);

     //algo feito no fim

end;

function MyMessageBox(hWnd:HWND; lpText:LPCSTR; lpCaption:LPCSTR; uType:UINT):longint; stdcall;
begin
     ShowMessage(string(lpText));
     Result := 0;
end;

{ TfrmHook }

procedure TfrmHook.btCriaHookClick(Sender: TObject);
begin
     {
     FHook := TLazHook.Create(
           @ShowMessage, //ponteiro para a procedure original (seu código vai ser copiado na procedure acima)
           @MyShowMessage //procedure Destino, a que fizemos para substituir
     );
     }

     FHook := TLazHook.Create(
           @MessageBox, //ponteiro para a procedure original (seu código vai ser copiado na procedure acima)
           @MyMessageBox //procedure Destino, a que fizemos para substituir
     );
end;

procedure TfrmHook.btMetodoOriginalClick(Sender: TObject);
begin
     //ShowMessage('Hello World');
     MessageBox(0, 'Hello World', 'Mensagem', 0);
end;

procedure TfrmHook.btMeuMetodoClick(Sender: TObject);
begin
  //MyShowMessage('Hello World');
  MyMessageBox(0, 'Hello World', 'Mensagem', 0);
end;



initialization
  {$I unit1.lrs}

end.


Nesse exemplo só podemos hookar/enganchar/sobrescrever (não faço idéia do termo correto em português) funções no próprio processo e não no windows inteiro. Ainda não sei fazer a parte de injeção de "código" em dll's.
Funciona em Delphi e Lazarus. Para alternar entre os exemplos descomente os de cima e comente os de baixo.

Faça o download e divirta-se ;)

quinta-feira, 21 de outubro de 2010

Tweet Button oficial no blogspot

Há outras formas de colocar o Tweet Button no seu blog. Eu vou abordar  a mais "complicada" ;)

Ao editar o HTML do blogspot você o faz na verdade em XML. É um formato de XML que dirá ao mecanismo do blogger como será seu HTML depois. E esse XML aceita alguns "comandos" como veremos.

O botão Tweet é uma tag de âncora de hiperlink <a> comum que além dos atributos class e href possui esses atributos especiais, tradados pelo script que vem junto:


data-count='vertical'  --> tipo de contador: pode ser vertical, horizontal ou none
data-via='vitorrubio' --> seu nome  no twitter
data-text='titulo do post'  --> título do post :p
data-url='url do post' --> url do post ;)



Dentro de marcadores de tags < > você não pode colocar outros marcadores como estes. E para obter automaticamente o titulo do post e a url devemos usar os já ditos "comandos" do blogspot.

Para obter o títilo do post você deve utilizar data:post.title e para obter a url você deve utilizar data:post.url.

No entanto para utilizar os comandos citados você não pode simplesmente atribuí-los aos atributos, mas deve preceder os nomes dos atributos com "expr:" para que eles possam ser "calculados" antes de "montados".

Ou seja, colocar:



data-text='data:post.title'  
data-url='data:post.url' 
Estará errado pois fará com que seu título se torne a palavra "data:post.title" e a url seja a expressão "data:post.url" em vez dos respectivos título e endereço corretos. Você deve preceder os atributos com expr: assim:

expr:data-text='data:post.title' 
expr:data-url='data:post.url' 

Se quiser "concatenar" ou adicionar algo mais no titulo deve "somar" com a sua mensagem envolvendo em aspas, mas deve usar "&quot;" uma "html entitie" que se tornará aspas depois do cálculo. Não use aspas "de verdade"

expr:data-text='data:post.title + &quot; algo mais que eu queira dizer &quot;' 
expr:data-url='data:post.url' 

O código completo "normal" ficará assim:

<a 
  class='twitter-share-button' 
  data-count='vertical' 
  data-via='vitorrubio' 
  expr:data-text='data:post.title' 
  expr:data-url='data:post.url' 
  href='http://twitter.com/share'>Tweet</a>
   <script src='http://platform.twitter.com/widgets.js' type='text/javascript'></script>


Com o resultado:




O código com "um algo mais" ficará assim:

<a 
  class='twitter-share-button' 
  data-count='vertical' 
  data-via='vitorrubio' 
  data-text='Teste de Tweet Button no Blogspot' 
  expr:data-url='data:post.url' 
  href='http://twitter.com/share'>Tweet</a>
<script src='http://platform.twitter.com/widgets.js' type='text/javascript'></script>

Com o resultado:





Espero ter ajudado ;)

Clube Delphi número 122

Pela primeira vez a Revista Clube Delphi traz em sua capa um artigo sobre Lazarus e Free Pascal. Para quem não conhece o Lazarus trata-se de uma IDE para se trabalhar com Free Pascal e auxiliar o Desenvolvimento RAD ou OO de uma maneira muito semelhante ao Delphi 7.

O Compilador AINDA não tem todos os recursos que o moderno compilador do Delphi XE, como generics por exemplo, mas isso não é um impedimento. Já é possível criar aplicações robustas em Lazarus.

Na capa podemos ver um lindo Guepardo. Para quem não sabe a diferença entre Guepardo e Leopardo veja aqui.

Para quem gosta de POO e Design Patterns foi publicado um artigo meu, a ser dividido em 3 partes, sobre Abstract Factory e Factory Methods. No começo do artigo é explicado como criar classes e formulários de acordo com variáveis externas, sem usar nenhum IF. O que fazemos é delegar a responsabilidade de criar uma instância de um objeto a alguém que realmente saiba como criar e preparar esse objeto. Assim nós podemos isolar a classe que precisa de um objeto de uma linhagem ou interface qualquer da unidade que a implementa concretamente.

Na última parte falaremos sobre como criar uma aplicação totalmente independente de Banco de Dados e de Tecnologia de Acesso (componente) utilizando estas tecnicas.

Espero que todos tenham uma agradável leitura ;)

quarta-feira, 20 de outubro de 2010

Obtendo atributos de arquivos com o Lazarus

Como saber se um arquivo é oculto, de sistema, somente leitura e tal? Como saber a data de criação e última alteração de um arquivo?

E para saber se é arquivo, diretório ou unidade?

Criamos essa pequena classe no Lazarus (e por extensão Delphi) onde você passa um arquivo qualquer e ela te identifica quais são os atributos e ainda pode mudar alguns dos atributos em runtime.

A classe ainda não está completa, a idéia é expandi-la, mas ela já é bastante funcional. É muito mais fácil e intuitivo usar uma classe do que a API do Windows.

A classe:
unit uLazFile;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, windows;

type

  { TLazFile }

  TLazFile = class
  private
    FFileName: string;
    function Attributes: word;
    procedure Verify;
    //gets
    function GetFileName: string;
    function GetAttributeFile: boolean;
    function GetAttributeHidden: boolean;
    function GetAttributeSystem: boolean;
    function GetAttributeReadOnly: boolean;
    function GetAttributeDirectory: boolean;
    function GetAttributeVolume: boolean;

    function GetFileCreationDate: TDateTime;
    function GetLastFileAccessDate: TDateTime;
    function GetLastWriteFileDate: TDateTime;
    function GetFileSize: int64;
    //sets
    procedure SetFileName(const value: string);
    procedure SetAttributeFile(const value: boolean);
    procedure SetAttributeHidden(const value: boolean);
    procedure SetAttributeSystem(const value: boolean);
    procedure SetAttributeReadOnly(const value: boolean);
  public
    constructor Create(const filename: string);   overload;
    constructor Create; overload;
  published
    property  AttributeFile: boolean read GetAttributeFile write SetAttributeFile;
    property  AttributeHidden: boolean read GetAttributeHidden write SetAttributeHidden;
    property  AttributeSystem: boolean read GetAttributeSystem write SetAttributeSystem;
    property  AttributeReadOnly: boolean read GetAttributeReadOnly write SetAttributeReadOnly;
    property  IsDirectory: boolean read GetAttributeDirectory;
    property  IsVolume: boolean read GetAttributeVolume;
    property  FileName: string read GetFileName write SetFileName;

    property  FileCreationDate: TDateTime read GetFileCreationDate;
    property  LastFileAccessDate: TDateTime read GetLastFileAccessDate;
    property  LastWriteFileDate: TDateTime read GetLastWriteFileDate;
    property  FileSize: int64 read GetFileSize;
  end;


implementation

{ TLazFile }

Function WinToDosTime (Var Wtime : TFileTime;var DTime:longint):longbool;
var
  lft : TFileTime;
begin
  WinToDosTime:=FileTimeToLocalFileTime(WTime,lft) and
                FileTimeToDosDateTime(lft,Longrec(Dtime).Hi,LongRec(DTIME).lo);
end;

function TLazFile.Attributes: word;
begin
     Verify;
     Result := FileGetAttr(FFileName);
end;

procedure TLazFile.Verify;
begin
       if (FFileName = '') then
        raise Exception.Create('File Inexistente.');
end;

function TLazFile.GetFileName: string;
begin
  Result := FFileName;
end;

function TLazFile.GetAttributeFile: boolean;
begin
     Result := (Attributes and faArchive) = faArchive
end;

function TLazFile.GetAttributeHidden: boolean;
begin
     Result := (Attributes and faHidden) = faHidden
end;

function TLazFile.GetAttributeSystem: boolean;
begin
     Result := (Attributes and faSysFile) = faSysFile;
end;

function TLazFile.GetAttributeReadOnly: boolean;
begin
     Result := (Attributes and faReadOnly) = faReadOnly;
end;

function TLazFile.GetAttributeDirectory: boolean;
begin
     Result := (Attributes and faDirectory) = faDirectory;
end;

function TLazFile.GetAttributeVolume: boolean;
begin
     Result := (Attributes and faVolumeId) = faVolumeId;
end;

function TLazFile.GetFileCreationDate: TDateTime;
var
  Handle: THandle;
  FindData: TWin32FindData;
  temp: Longint;
begin
  Handle := FindFirstFile(Pchar(FFileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
    begin
      Windows.FindClose(Handle);
        If WinToDosTime(FindData.ftCreationTime,temp) then
          Result := FileDateToDateTime(temp);
    end
    else
        Result := 0;
end;

function TLazFile.GetLastFileAccessDate: TDateTime;
var
  Handle: THandle;
  FindData: TWin32FindData;
  temp: Longint;
begin
  Handle := FindFirstFile(Pchar(FFileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
    begin
      Windows.FindClose(Handle);
        If WinToDosTime(FindData.ftLastAccessTime,temp) then
          Result := FileDateToDateTime(temp);
    end
    else
        Result := 0;

end;

function TLazFile.GetLastWriteFileDate: TDateTime;
var
  Handle: THandle;
  FindData: TWin32FindData;
  temp: Longint;
begin
  Handle := FindFirstFile(Pchar(FFileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
    begin
      Windows.FindClose(Handle);
        If WinToDosTime(FindData.ftLastWriteTime,temp) then
          Result := FileDateToDateTime(temp);
    end
    else
        Result := 0;

end;



function TLazFile.GetFileSize: int64;
var
  Handle: THandle;
  FindData: TWin32FindData;
begin
  Handle := FindFirstFile(Pchar(FFileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
    begin
      Windows.FindClose(Handle);
      Result := FindData.nFileSizeHigh;
      Result := Result shl 32;
      Result := Result + FindData.nFileSizeLow;
    end;
end;


procedure TLazFile.SetFileName(const value: string);
begin
  FFileName:= value;
end;

procedure TLazFile.SetAttributeFile(const value: boolean);
begin
     Verify;
     if value then
        FileSetAttr(FFileName, Attributes or faArchive)
     else
        FileSetAttr(FFileName, Attributes and not faArchive);
end;

procedure TLazFile.SetAttributeHidden(const value: boolean);
begin
     Verify;
     if value then
        FileSetAttr(FFileName, Attributes or faHidden)
     else
         FileSetAttr(FFileName, Attributes and not faHidden)

end;

procedure TLazFile.SetAttributeSystem(const value: boolean);
begin
     Verify;
     if value then
        FileSetAttr(FFileName, Attributes or faSysFile)
     else
         FileSetAttr(FFileName, Attributes and not faSysFile)
end;

procedure TLazFile.SetAttributeReadOnly(const value: boolean);
begin
     Verify;
     if value then
        FileSetAttr(FFileName, Attributes or faReadOnly)
     else
         FileSetAttr(FFileName, Attributes and not faReadOnly)
end;

constructor TLazFile.Create(const filename: string);
begin

  FFileName:= filename;
end;

constructor TLazFile.Create;
begin

end;

end.



Modo de usar
unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, uLazFile;

type

  { TForm1 }

  TForm1 = class(TForm)
    btLeSomenteLeitura: TButton;
    btLeSistema: TButton;
    btLeEscondido: TButton;
    btLeArquivo: TButton;
    btSetaSomenteLeitura: TButton;
    btSetaSistema: TButton;
    btSetaEscondido: TButton;
    btSetaArquivo: TButton;
    btVerificaDiretorio: TButton;
    btVerificaUnidade: TButton;
    btInformacoes: TButton;
    edArquivo: TEdit;
    Label1: TLabel;
    procedure btInformacoesClick(Sender: TObject);
    procedure btLeArquivoClick(Sender: TObject);
    procedure btLeEscondidoClick(Sender: TObject);
    procedure btLeSistemaClick(Sender: TObject);
    procedure btLeSomenteLeituraClick(Sender: TObject);
    procedure btSetaArquivoClick(Sender: TObject);
    procedure btSetaEscondidoClick(Sender: TObject);
    procedure btSetaSistemaClick(Sender: TObject);
    procedure btSetaSomenteLeituraClick(Sender: TObject);
    procedure btVerificaDiretorioClick(Sender: TObject);
    procedure btVerificaUnidadeClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    Arquivo: TLazFile;
  end; 

var
  Form1: TForm1; 

implementation

{ TForm1 }

procedure TForm1.btLeSomenteLeituraClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.AttributeReadOnly));
end;

procedure TForm1.btSetaArquivoClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  Arquivo.AttributeFile:= not Arquivo.AttributeFile;
end;

procedure TForm1.btSetaEscondidoClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  Arquivo.AttributeHidden:= not Arquivo.AttributeHidden;
end;

procedure TForm1.btSetaSistemaClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  Arquivo.AttributeSystem:= not Arquivo.AttributeSystem;
end;

procedure TForm1.btSetaSomenteLeituraClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  Arquivo.AttributeReadOnly:= not Arquivo.AttributeReadOnly;
end;



procedure TForm1.btVerificaDiretorioClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.IsDirectory));
end;

procedure TForm1.btVerificaUnidadeClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.IsVolume));
end;

procedure TForm1.btLeSistemaClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.AttributeSystem));
end;

procedure TForm1.btLeEscondidoClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.AttributeHidden));
end;

procedure TForm1.btLeArquivoClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(BoolToStr(Arquivo.AttributeFile));
end;

procedure TForm1.btInformacoesClick(Sender: TObject);
begin
  Arquivo.FileName := edArquivo.Text;
  ShowMessage(
              'Data Criação ' + DateTimeToStr(Arquivo.FileCreationDate) + #13#10 +
              'Data Modificação ' + DateTimeToStr(Arquivo.LastWriteFileDate) + #13#10 +
              'Data Ultimo Acesso ' + DateTimeToStr(Arquivo.LastFileAccessDate) + #13#10 +
              'Tamanho ' + IntToStr(Arquivo.FileSize) + #13#10
  );

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
     Arquivo := TLazFile.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Arquivo.Free;
end;

initialization
  {$I unit1.lrs}

end.




Download

Have fun ;)

terça-feira, 19 de outubro de 2010

Códigos de versões do Delphi e Diretivas de compilação

Sabe aqueles componentes feitos para múltiplas versões do Delphi que tem várias diretivas para saber qual é a versão do Delphi e assim mudar o código em tempo de compilação?

Segue abaixo uma lista das diretivas de compilação/comnplicação :)

São mais ou menos assim:
{$IFDEF VER210}
//algum código aqui compatível com essa versão
{$ELSE}
//outro código genérico
{$ENDIF}

Seguem abaixo os códigos das versões:


VER80 - Delphi 1
VER90 - Delphi 2
VER100 - Delphi 3
VER120 - Delphi 4
VER130 - Delphi 5
VER140 - Delphi 6
VER150 - Delphi 7
VER160 - Delphi 8
VER170 - Delphi 2005
VER180 - Delphi 2006
VER180 - Delphi 2007
VER185 - Delphi 2007
VER200 - Delphi 2009
VER210 - Delphi 2010
VER220 - Delphi XE

É útil para você criar bibliotecas que compilem tanto em Delphi como em Lazarus, pois você pode usar um {$IFDEF FPC}.

Tente rodar o exemplo abaixo nos Delphis 2010, XE e 7.


procedure TForm1.Button1Click(Sender: TObject);
begin
{$IFDEF VER210}
  ShowMessage('Delphi 2010');
{$ELSE}
  {$IFDEF VER220}
    ShowMessage('Delphi XE');
  {$ELSE}
    ShowMessage('Whatever Version');
  {$ENDIF}
{$ENDIF}
end;

Happy Coding ;)

12° Congresso de Tecnologia FATEC-SP

Esta semana está rolando o 12° Congresso de Tecnologia FATEC-SP (Av. Tiradentes / Rua três rios, proximo ao metrô tiradentes)

Ontem dei uma passada lá só pra ver o movimento (tá certo, eu tava perdido e sem programação). Legal foi eu encontrar vários colegas da Adv Tecnologia (grande abraço pra todos vocês) que estavam só de passagem também. :)

Hoje vai rolar um evento bastante interessante: palestra sobre XBOX e Kinect pelo MVP Maurício Alegretti e eu assistirei.

Amanhã rola uma palestra interessante também sobre windows phone 7. Infelizmente perderei esta. Quem assistir e quiser me passar o que foi abordado agradeço.

É isso aí, para quem for, nos vemos lá :)


Mais informações em:
http://www.fatecsp.br/
http://www.objectiveclub.com.br/

IIF

Quem já programou em Clipper e Visual Fox Pro deve lembrar da função IIF. Esta função simplesmente aceitava três parâmetros, um booleano, ou condição, e dois valores, caso verdadeiro e caso falso.
Ela era interessante porque substituia (em partes) o operador ternário ?: do C e poderia ser usada em expressões para se somar ou concatenar o valor correto dependendo da condição.
Ela servia para substituir um if ... else e uma variável temporária colocando tudo na mesma linha.

O seu formato era:

iif(condição, valor se verdadeiro, valor se falso): valor;

Ela retornava um valor que seria do mesmo tipo do segundo e terceiro argumento.

É possível criar inúmeras variantes dessa função, tanto no Lazarus como no Delphi. Usando variants poderiamos  criar uma vertente da função que funcionaria para qualquer tipo de dado primitivo.

Mas o Delphi é ainda mais flexível porque hoje temos Generics e o tipo TValue, que são mais flexíveis ainda. Com Generics, Variants e sobrecarga de operadores podemos utilizar sim qualquer tipo de valor, classes, objetos instanciados e assim por diante. Além disso, com expressões lambda e funções "inline" podemos inclusive colocar função/execução de código no IIF o que extrapola o seu objetivo de substituir o operador ternário. Ele se torna realmente um if...else. Esse tipo de coisa poderíamos fazer nas versões antigas do Delphi e no Lazarus apenas com ponteiros para métodos.

Esse código abaixo mostra um exemplo da função IIF e seu uso, inclusive com generics nas versões modernas do Delphi.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

function iif(condicao: Boolean; ValorVerdadeiro, ValorFalso: T): T;

implementation

{$R *.dfm}

function iif(condicao: Boolean; ValorVerdadeiro, ValorFalso: T): T; 
begin
  if condicao then
    Result := ValorVerdadeiro
  else
    Result := ValorFalso;
end;

function iif(condicao: Boolean; ValorVerdadeiro, ValorFalso: string): string;
begin
  if condicao then
    Result := ValorVerdadeiro
  else
    Result := ValorFalso;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  a,b, resultado: string;
  x,y: integer;
  debita: boolean;
begin

  a := 'Clube';
  b := 'Delphi';

  x := 1;
  y := 2;

  debita := true;
  resultado:= inttostr(iif(debita, y-1, y+1));
  ShowMessage(resultado);

  debita := false;
  resultado:= inttostr(iif(debita, y-1, y+1));
  ShowMessage(resultado);

  resultado:= iif(x=y, 'x é igual a y', 'x é diferente de y');
  ShowMessage(resultado);

  x := y;
  resultado:= iif(x=y, 'x é igual a y', 'x é diferente de y');
  ShowMessage(resultado);

  resultado:= iif((a<>'')and (b<>''), a + ' ' + b , 'um dos dois é vazio');
  ShowMessage(resultado);

  a := '';
  resultado:= iif((a<>'')and (b<>''), a + ' ' + b , 'um dos dois é vazio');
  ShowMessage(resultado);

  resultado:= iif(a=b, 'a e b são iguais', 'a e b são diferentes');
  ShowMessage(resultado);

end;

end.



Aqui criamos apenas duas versões, uma com generics (que funcionará para tudo) e uma com strings. Poderíamos criar com variants ou qualquer outra coisa.

Uma alteração interessante que pode ser feita também é declarar os argumentos/parâmetros das funções como const, assim eles não poderão ser temporariamente modificados por expressões lambda ou código dentro do corpo de IIF.


function iif(const condicao: Boolean; const ValorVerdadeiro, ValorFalso: T): T;

{...}

function iif(const condicao: Boolean; const ValorVerdadeiro, ValorFalso: T): T; 
begin
  if condicao then
    Result := ValorVerdadeiro
  else
    Result := ValorFalso;
end;


Espero que seja útil para todos. Pelo menos saudosista é ;)

segunda-feira, 18 de outubro de 2010

Forward Declarations

Imagine que você está escrevendo uma biblioteca de funções estáticas em um arquivo .pas e precisa declarar o cabeçalho de uma função para que ela possa ser usada abaixo globalmente, mas você não quer colocar o cabeçalho na seção interface para que a função não seja publicada para todas as units, você quer manter ela em "segredo" na seção de implementation.

Você pode dizer: "simples, basta declarar a função inteira acima e pronto." Mas e se essa função precisa usar outra que também será declarada dessa maneira e vice-versa?

A solução é foward declaration. Tudo bem, não se usa muito hoje em dia, ainda mais em POO, mas segue abaixo um exemplo de como você pode declarar uma função adiando a sua real implementação "mais para baixo" mesmo na seção implementation:

unit uSomaForward;

interface

function SomaPublica(x, y: Integer): Integer;

implementation


function SomaPrivada(x,y: Integer): Integer; forward;

function SomaPublica(x, y: Integer): Integer;
begin
  Result := SomaPrivada(x, y);
end;


function SomaPrivada(x,y: Integer): Integer;
begin
  Result := x+y;
end;


end.
 

Esse você testa com:

ShowMessage(IntToStr(SomaPublica(5,9))); 

Vamos fazer uma brincadeira mais complexa: todo mundo sabe fazer uma função fatorial recursiva certo? E se dividíssemos a função em duas recursivas circulares que hora chama uma e hora chama outra? Lógico que não teremos isso na vida real, mas você pode ter dois métodos um fazendo chamada ao outro de maneira circular por n motivos, desde tratamento de arvores de menus até mesmo descuido e desatenção. Isso não vem ao caso, queremos apenas demonstrar como as foward declarations podem ajudar a resolver quebra-cabeças de funções auto referenciaveis que ainda não foram declaradas. Sem elas não seria possível uma função chamar a outra sem os cabeçalhos, experimente.

A função fatorial dependente de duas recursivas ficaria assim:

unit uInterdependentes;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

function FatorialChamanoFuncoesRecursivasInterDependentes(numero: integer):integer;



implementation

   function InterDependente(contador: integer): integer forward;
   function DependenteInter(contador: integer): integer forward;

   function FatorialChamanoFuncoesRecursivasInterDependentes(numero: integer):integer;
   begin
        Result :=  InterDependente(numero);
   end;

   function InterDependente(contador: integer): integer;
   begin
        if contador <= 1 then
        begin
             Result := 1;
        end
        else
        begin
            Result := contador * DependenteInter(contador-1);
        end;
   end;

   function DependenteInter(contador: integer): integer;
   begin
        if contador <= 1 then
        begin
             Result := 1;
        end
        else
        begin
            Result := contador * InterDependente(contador-1);
        end;
   end;

end.  
E você pode testar assim:
     ShowMessage(IntToStr(FatorialChamanoFuncoesRecursivasInterDependentes(5)));  //retornará 120

Até a próxima ;)

sexta-feira, 15 de outubro de 2010

Joque asteroids destruindo elementos de qualquer página web

Nem sei dizer ao certo como encontrei essa página http://erkie.github.com/, talvez em algum tweet perdido de alguém.

Até saiu matéria sobre o assunto no UOL Tecnologia. Trata-se de um script que, incorporado à uma página qualquer ou adicionado na barra de favoritos do seu browser, cria uma pequena nave, estilo asteroids, para destruir os elementos da página. Dá para destruir tudo, até ficar só o body. Só não dá pra destruir imagens em background via CSS.

Se você quiser colocar no seu blog basta colocar um link com esse código no evento onclick:


javascript:var s = document.createElement('script');s.type='text/javascript';document.body.appendChild(s);s.src='http://erkie.github.com/asteroids.min.js';void(0);

Exemplo

O clássico jogo asteroids foi lançado pela ATARI em 1979 nos EUA e em 1981(?) no Brasil. O esquema de inércia parece ter sido inspirado no ainda mais mitológico SpaceWar para PDP-1 criado pelo cientista da computação Steve "Slug" Russell no MIT. Vale destacar que Steve Russell deu inúmeras e essenciais contribuições à programação como a conhecemos hoje. Vale a pena pesquisar a respeito de seu trabalho.

Bom, é isso, divirta-se destruindo meu blog ;)



Veja mais: Download Squad

Truques usando API do windows

Usando a API do windows podemos executar um programa externo, colocar a janela dele dentro da janela do nosso programa e capturar  texto de uma janela ou widget qualquer desse programa.

Qualquer programa feito e usando a api do windows pode ser usado dessa forma. Nossa cobaia oficial será a calculadora do windows, mas onde você ver calculadora pode ler "qualquer programa nativo win32, como msn, bloco de notas etc".

O exemplo foi feito em lazarus mas funciona perfeitamente em qualquer versão do Delphi. Tenha a unit Windows no seu uses.




procedure TForm1.btCapturarClaculadoraClick(Sender: TObject);
var
   //vamos dar apenas 5 tentativas pois pode ser que a "calculadora" não existe
   tentativas: integer;
   HandleCalc, HandleCaixaTexto: HWND;  //handles da calculatora
   TamanhoVisor: integer;
   TextoVisor: string;
begin
     //executamos a "calculadora" ou qualquer que seja o programa externo
     ShellExecute(Handle, nil,  'c:\windows\system32\calc.exe', nil, nil, SW_SHOWNORMAL);

     //enquanto não encontrarmos a janela do programa externo DENTRO da janela do nosso programa ou não for atingido o núimero de tentativas nós tentaremos colocar a janela do programa "calculadora" dentro da nossa janela
     while  (FindWindowEx(Handle, 0, 'SciCalc', 'Calculadora') = 0) or (tentativas < 5) do
     begin
          //FindWindowEx procura uma janela dentro de outra e FindWindow apenas procura uma janela
          HandleCalc:= FindWindow('SciCalc', 'Calculadora');
          Windows.SetParent(HandleCalc, Handle);
          Inc(tentativas);
          //um sleep para dar uma pausa de 1 ms
          Sleep(1);
     end;

     if HandleCalc > 0 then
     begin
          //obtem o primeiro filho da janela da calculadora
          HandleCaixaTexto := GetWindow(HandleCalc, GW_CHILD);
          //obtem o tamanho do texto na caixa
          TamanhoVisor:=SendMessage(HandleCaixaTexto, WM_GETTEXTLENGTH, 0, 0) + SizeOf(Char)*1;
          //seta o tamanho desse texto
          SetLength(TextoVisor, TamanhoVisor);
          //obtem o texto gravando-o em  TextoVisor. Integer(TextoVisor) é o mesmo que o endereço do array onde estão os chars da string
          SendMessage(HandleCaixaTexto, WM_GETTEXT, TamanhoVisor, Integer(TextoVisor));
          //mostraremos o texto da calculadora, como está recém iniciada será 0
          ShowMessage(Trim(TextoVisor));
     end;
end; 

Lembre-se que fechar seu programa não fechará a "calculadora" que está aberta. Para isso você deve fechar a calculadora antes ou fechá-la no gerenciador de tarefas.

Em um outro post veremos como podemos gerenciar e fechar tarefas usando a API do windows.

Have fun :)

Lentidão ao acessar máquinas da rede windows

Se você enfrentar lentidão ao acessar a rede do windows, por exemplo uma demora fora do normal para listar as máquinas da rede, isso pode ser causado por uma tentativa do windows de mostrar as tarefas agendadas em cada máquina.

Como essa função é muito lenta e a maioria das pessoas não usa isso podemos remover isso pelo registro do windows.

Altere o registro por sua conta e risco, pois um passo em falso aqui pode ocasionar em mal funcionamento do sistema.

Vá no Botão Iniciar --> Executar (ou pressione window + r )
Digite: REGEDIT e pressione OK.

No vá abrindo as chaves até encontrar HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/Current Version/Explorer/RemoteComputer/NameSpace
Procure pela chave {D6277990-4C6A-11CF-8D87-00AA0060F5BF} e apague-a.

Chave a ser apagada
Apague apenas esta. Pronto, agora você pode visualizar os micros da rede e pastas compartilhadas mais rapidamente.

sexta-feira, 8 de outubro de 2010

Gerador / Validador de CPF e CNPJ no lazarus

Diferentemente do RG um CPF (ou CNPJ) é um identificador único. Um CPF identifica uma pessoa única no Brasil. É uma boa prática associar contas de usuário com seu CPF. Seguro para o usuário, pois diminui o risco de alguém se passar por ele, e seguro para quem presta algum serviço, pois pode identificar um usuário infrator.

Lógico que essa não deveria nem de longe ser a única medida de segurança e identificação de um usuário, mas já é um começo.

Quando você coloca o CPF como campo obrigatório em um formulário de cadastro no seu sistema torna-se difícil testar esse formulário porque você precisa validar o CPF para ver se é correto, mas também precisa de CPF's válidos e únicos para usar nos seus testes. O que fazer?

Pensando nisso coloquei esse gerador de CPF e CNPJ que você pode ver na barra lateral do blog, e fiz esse programinha em lazarus para gerar e/ou validar CPF's e CNPJ's. Você pode copiar esse algoritmo para validar documentos nos seus cadastros.

Funcionamento do CPF:

O CPF é formado por 11 dígitos (excetuando-se as pontuações e separadores), 9 do número do documento e 2 de verificação.

O 10° dígito é baseado em um algoritmo feito com os 9 dígitos precedentes, e o 11° é calculado usando-se os 9 primeiros mais o 10° recém calculado. Ou seja, se seu CPF é 74241980503 (ainda não chegamos nesse número espero) isso quer dizer que fazendo o referido algoritmo com 742419805 você encontrará o 0 e fazendo com 7424198050 você encontrará o 3.

O algoritmo consiste em 3 passos:
1) multiplicar, da esquerda para a direita, cada dígito por um valor n que começará com 10 e decrescerá (n-1 com n nunca sendo < 2) a cada casa para a direita, acumulando-se (somando-se) cada resultado. Por exemplo, o CPF que mencionamos iniciaríamos o calculo assim:
(7 x 10)+(4 x 9)+(2 x 8)+(4 x 7)+(1 x 6)+(9 x 5)+(8 x 4)+(0 x 3)+(5 x 2)  que seria 70+36+16+28+6+45+32+0+10=243
2) Obter o resto da divisão dessa soma por 11, ou seja 243 mod 11 = 1
3) Subtrair o resultado de 11, ou seja, 11 - 1. Se o resultado for maior que 9 colocamos 0. Então 11-1 = 10, portanto 0. Há alguns que verificam se o módulo por 11 é menor que 2, se for o resultado do dígito é 0, senão será 11 subtraindo-se  o módulo. Tanto faz a ordem dos fatores, dá no mesmo.

O 11° dígito obtem-se da mesma maneira, porém começando-se a multiplicação por 11, então se até agora temos 7424198050X devemos fazer:

(7 x 11) + (4 x 10) + (2 x 9) + (4 x 8) + (1 x 7 ) + (9 x 6) + (8 x 5) + (0 x 4) + (5 x 3 ) + (0 x 2) = 283
283 mod 11 = 8
11-8 = 3, por isso o último dígito é 3.

Criamos a seguinte função para calcular o DV do CPF, supondo-se que os 9 primeiros dígitos são dados:


function TfrmDocumentos.CalculaDVCPF(cpf: string): string;
var
   digitos: array [1..11] of integer;
begin

     digitos[1] := StrToInt(cpf[1]);
     digitos[2] := StrToInt(cpf[2]);
     digitos[3] := StrToInt(cpf[3]);
     digitos[4] := StrToInt(cpf[4]);
     digitos[5] := StrToInt(cpf[5]);
     digitos[6] := StrToInt(cpf[6]);
     digitos[7] := StrToInt(cpf[7]);
     digitos[8] := StrToInt(cpf[8]);
     digitos[9] := StrToInt(cpf[9]);

     digitos[10] := 11 -
          (((digitos[1] * 10)+
          (digitos[2]  * 9)+
          (digitos[3]  * 8)+
          (digitos[4]  * 7)+
          (digitos[5]  * 6)+
          (digitos[6]  * 5)+
          (digitos[7]  * 4)+
          (digitos[8]  * 3)+
          (digitos[9]  * 2)) mod 11);

     if digitos[10] > 9 then
        digitos[10] := 0;

     digitos[11] := 11 -
          (((digitos[1] * 11)+
          (digitos[2]  * 10)+
          (digitos[3]  * 9)+
          (digitos[4]  * 8)+
          (digitos[5]  * 7)+
          (digitos[6]  * 6)+
          (digitos[7]  * 5)+
          (digitos[8]  * 4)+
          (digitos[9]  * 3)+
          (digitos[10] * 2)) mod 11);

     if digitos[11] > 9 then
        digitos[11] := 0;

     Result := IntToStr(digitos[10])+IntToStr(digitos[11]);

end;  

Para gerar números randômicos de 9 dígitos, para serem passadas para a função acima, usamos:

function TfrmDocumentos.GeraCPF: string;
var
   iNum: integer;
begin
     Randomize;
     iNum:=random(999999999);
     Result := ZeroEsquerda(IntToStr(iNum), 9);
end;  

A função ZeroEsquerda serve para completar uma string numérica com zeros a esquerda até a quantidade de dígitos definidas no segundo parâmetro e é definida como a seguir:


function TfrmDocumentos.ZeroEsquerda(num: string; tamanho: integer): string;
var i: integer;
    tamAtual: integer;
begin
     Result := num;
     tamAtual:= tamanho - Length(num);
     for i := 1 to tamAtual do
         Result := '0'+Result;
end; 

As funções abaixo são usadas para limpar um CPF ou CNPJ se ele possui caracteres de pontuação, ou para colocar os caracteres de pontuação nas posições corretas de acordo com o tipo de documento.


//elimina caracteres não numéricos da string
function TfrmDocumentos.Limpa(documento: string): string;
var
   i: integer;
begin
     for i := 1 to Length(documento) do
         if documento[i] in ['0'..'9'] then
            Result := Result+documento[i];
end;

//insere as pontuações nas posições corretas
function TfrmDocumentos.Mascara(documento: string): string;
var
   i: integer;
   temp: string;
begin

     temp := Limpa(documento);
     if Length(temp) = 11 then
     begin
               Insert('.', temp, 4);
               Insert('.', temp, 8);
               Insert('-', temp, 12);
     end
     else
     begin
          Insert('.', temp, 3);
          Insert('.', temp, 7);
          Insert('/', temp, 11);
          Insert('-', temp, 16);
     end;

     Result := temp;
end;  

Para gerar um CPF utilizamos:

procedure TfrmDocumentos.btGeraCPFClick(Sender: TObject);
var
   sCPF: string; //9 primeiros digitos
begin
  sCPF:=GeraCPF;
  //edit com o cpf completo
  txtGCPF.Text := sCPF+CalculaDVCPF(sCPF)
end;  

Para verificar se um CPF é válido obtemos os 9 primeiros dígitos, separamos dos dois posteriores e calculamos os DV's para esses 9 dígitos. Se eles forem iguais aos dois últimos dígitos que extraímos então o CPF é válido.


procedure TfrmDocumentos.btValidaCPFClick(Sender: TObject);
var
   temp, sCpf, sDv: string;
begin
     temp := Limpa(txtVCPF.Text);
     sCpf:=Copy(temp, 1, 9);
     sDv := Copy(temp, 10, 2);
     if (Length(temp) = 11) and (CalculaDVCPF(sCpf) = sDv)  then
     begin
        lbMsg.Caption := 'Cpf Válido';
        lbMsg.Font.Color:=clGreen;
     end
     else
     begin
        lbMsg.Caption := 'Cpf Inválido';
        lbMsg.Font.Color:=clRed;
     end;
end;   


A mesma lógica se aplica com o CNPJ. A única diferença é que o CNPJ se divide em três blocos:
O número (8 primeiros dígitos) o número da filial ou famoso "mil ao contrário/mil contra" que são os 4 posteriores e os dois últimos são o DV.

Para calcular o dígito 13 multiplicamos os 12 primeiros por 5,4,3,2,9,8,7,5,6,4,3,2 e depois somamos as parcelas, retiramos o resto da divisão por 11, subtraimos este resultado de 11 e tudo que for maior que 9 passa a ser 0, não for maior que 9 permanece.

Para calcular o 14° dígito multiplicamos os 13 primeiros, incluindo aí o primeiro DV, por 6,5,4,3,2,9,8,7,6,5,4,3,2 e repetimos o processo.

Esse é o algoritmo em lazarus:

function TfrmDocumentos.CalculaDVCNPJ(cnpj: string): string;
var
   digitos: array [1..14] of integer;
begin

     digitos[1] := StrToInt(cnpj[1]);
     digitos[2] := StrToInt(cnpj[2]);
     digitos[3] := StrToInt(cnpj[3]);
     digitos[4] := StrToInt(cnpj[4]);
     digitos[5] := StrToInt(cnpj[5]);
     digitos[6] := StrToInt(cnpj[6]);
     digitos[7] := StrToInt(cnpj[7]);
     digitos[8] := StrToInt(cnpj[8]);
     digitos[9] := StrToInt(cnpj[9]);
     digitos[10] := StrToInt(cnpj[10]);
     digitos[11] := StrToInt(cnpj[11]);
     digitos[12] := StrToInt(cnpj[12]);

     digitos[13] := 11 - ((

          (digitos[1] * 5)+
          (digitos[2] * 4)+
          (digitos[3] * 3)+
          (digitos[4] * 2)+
          (digitos[5] * 9)+
          (digitos[6] * 8)+
          (digitos[7] * 7)+
          (digitos[8] * 6)+
          (digitos[9] * 5)+
          (digitos[10] * 4)+
          (digitos[11] * 3)+
          (digitos[12] * 2)

          ) mod 11);


     if digitos[13] > 9 then
        digitos[13] := 0;

     digitos[14] := 11 - ((

          (digitos[1] * 6)+
          (digitos[2] * 5)+
          (digitos[3] * 4)+
          (digitos[4] * 3)+
          (digitos[5] * 2)+
          (digitos[6] * 9)+
          (digitos[7] * 8)+
          (digitos[8] * 7)+
          (digitos[9] * 6)+
          (digitos[10] * 5)+
          (digitos[11] * 4)+
          (digitos[12] * 3)+
          (digitos[13] * 2)

          ) mod 11);


     if digitos[14] > 9 then
        digitos[14] := 0;

     Result := IntToStr(digitos[13])+IntToStr(digitos[14]);
end;   

Respectivamente estes são os códigos para gerar, criar e validar CNPJ:

function TfrmDocumentos.GeraCNPJ: string;
var
   iNum: Int64;
begin
     Randomize;
     iNum:=random(999999999999);
     Result := ZeroEsquerda(IntToStr(iNum), 12);
end;  

procedure TfrmDocumentos.btGeraCNPJClick(Sender: TObject);
var
   sCNPJ: string;
begin
  sCNPJ:=GeraCNPJ;
  if cbMascara.Checked then
     txtGCNPJ.Text := Mascara(sCNPJ+CalculaDVCNPJ(sCNPJ))
  else
     txtGCNPJ.Text := sCNPJ+CalculaDVCNPJ(sCNPJ);
end;  

procedure TfrmDocumentos.btValidaCNPJClick(Sender: TObject);
var
   temp, sCNPJ, sDv: string;
begin
     temp := Limpa(txtVCNPJ.Text);
     sCNPJ:=Copy(temp, 1, 12);
     sDv := Copy(temp, 13, 2);
     if (Length(temp) = 14) and (CalculaDVCNPJ(sCNPJ) =  sDv)  then
     begin
        lbMsg.Caption := 'CNPJ Válido';
        lbMsg.Font.Color:=clGreen;
     end
     else
     begin
        lbMsg.Caption := 'CNPJ Inválido';
        lbMsg.Font.Color:=clRed;
     end;
end;   



Como essas regras e fórmulas são rígidas, bem como os tamanhos e posições dos documentos, optei por usar um vetor de tamanho fixo e armazenar cada valor em sua posição em vez de usar um loop for. Assim eu economizo algumas variáveis temporárias de somatório e módulo, efetuo as somas e divisões uma única vez e a transformação de str para int apenas uma vez, e as transformações de int para str apenas nos dois últimos dígitos, os DV.

Espero ter esclarecido como funciona essa validação.

Você pode baixar o programa aqui.

domingo, 3 de outubro de 2010

Cannot implicitly convert type 'string' to 'System.Net.Mail.MailAddress'

Se você recebeu a mensagem acima ao compilar uma aplicação C# saiba que a string "nome@dominio.com.br" não é um e-mail válido, ou pelo menos não podemos afirmar que seja. Ela não é do tipo MailAddress.


O tipo MailAddress é um tipo de dado especial para se trabalhar com e-mails que deve ser inicializado por se transformar um string em um e-mail. 


Se você tentou fazer:


MailMessage m = new MailMessage();
m.From = "nome@dominio.com.br";



Faça:


MailMessage m = new MailMessage();
m.From = new System.Net.Mail.MailAddress("nome@dominio.com.br"); //esta é um contrutor de classe que recebe uma string como parâmetro.



Essa dica foi tirada do site http://www.csharptalk.com/  nesse post.


Em outra ocasião falaremos mais sobre o tipo MailAddress.


Espero que tenha sido de ajuda ;)

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)