Evernoteへメモを追加する。(添付ファイル付)

今回は、添付ファイルと共にメモを追加してみます。前回のソースにListView1, ImageList1, Splitter1を追加します。添付ファイルは、ExplorerからのDrag&Dropで取得します。ExplorerからのターゲットをMemo1としているため、Mr.XRAYさんところのSubClassコンポーネントを使わせて頂いています。(フォームをターゲットとして、WM_DROPFILESで処理するとサブクラスは必要はないです。)

Design


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
  Vcl.ToolWin, Vcl.ComCtrls, Vcl.ImgList, SubClassUnit;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Title: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    Button2: TButton;
    ListView1: TListView;
    Splitter1: TSplitter;
    ImageList1: TImageList;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    SubClass1: TSubClass;
    procedure SubClass1MessageAfter(Sender: TObject;
      var Message: TMessage);
    procedure DropFiles(H: THandle);
    procedure MakeTempFile(FileName: String);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses ShellAPI,
     EncdDecd,            // BASE64 Encording
     IdHashMessageDigest; // MD5値

const
  Temp = 'Evernote.enex';
  S1 = '<?xml version="1.0" encoding="UTF-8"?>'+
       '<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export.dtd">'+
       '<en-export>';  // エクスポートしたファイルには、その時の日時や
                       // EverNoteのバージョンが入っていますが、
                       // なくても動作しましたので、消しました。

  // 複数ある場合は、<note></note>までを繰り返す。
  S2 = '<note>';

  // ここにタイトル <title>タイトル</title>

  S3 = '<content>' +
       '<![CDATA[' +
       '<?xml version="1.0" encoding="UTF-8"?>' +
       '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'+
       '<en-note style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">';

  // ここに本文・添付ファイル

  // e.g.)
  // 1行目文字-ファイル1-2行目文字-ファイル2、ファイル3-3行目文字の場合
  // <div>1行目文字</div>
  // <en-media hash="ファイル1のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <div>2行目文字</div>
  // <div>
  // <en-media hash="ファイル2のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <en-media hash="ファイル3のMD5値" style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>
  // <div>3行目文字</div>
  // </div>
  F1 = '<en-media hash=';
  F2 = ' style="cursor: default; vertical-align: middle;" type="application/octet-stream"/>';

  S4 = '</en-note>' +
       ']]>' +
       '</content>';

  // ここに作成日時 <created>20111001T042516Z</created>
  // ここにタグ <tag>タグ1</tag> <tag>タグ2</tag>

  S5 = '<note-attributes>';

  // ここにURL <source-url>http://delphi-fan</source-url>

  S6 = '</note-attributes>';

  // ここに添付ファイル本体 - 複数ある場合は、<resource></resource>の繰り返し
  // <resource><data encoding="base64">
  //  ファイルデータ(BASE64でエンコード)
  // </data>
  // <mime>application/octet-stream</mime>
  // <resource-attributes>
  // <source-url>file://ファイル名(パス付)</source-url>
  // <file-name>ファイル名(パス無)</file-name>
  // </resource-attributes>
  // </resource>

  F3 = '<resource><data encoding="base64">';

  // ここにデータ

  F4 = '</data>';
  F5 = '<mime>application/octet-stream</mime>';
  F6 = '<resource-attributes>';
  F7 = '<source-url>file://';

  // ここにファイル名(パス付)

  F8 = '</source-url>';

  F9 = '<file-name>';

  // ここにファイル名(パス無)

  F10 = '</file-name>';
  F11 = '</resource-attributes>';
  F12 = '</resource>';

  S7 = '</note>';
  S8 = '</en-export>';


// Evernoteエクスポートファイル (ENEX)の作成
procedure TForm1.MakeTempFile(FileName: String);

  // UTCの日時-「年月日T時分秒Z」という文字列にします。
  function GetCreateDateTime: String;
  var
    UTC: TSystemTime;
  begin
    GetSystemTime(UTC);
  with UTC do
    Result := Format('%.2d%.2d%.2dT%.2d%.2d%.2dZ',
      [wYear,wMonth,wDay,wHour,wMinute,wSecond]);
  end;

  // ファイルをBASE64文字列で返します。
  function GetBody(const FileName: String): String;
  var
    MS: TMemoryStream;
  begin
    MS := TMemoryStream.Create;
    try
      MS.LoadFromFile(FileName);
      Result := EncodeBase64(MS.Memory, MS.Size);
    finally
      MS.Free;
    end;
  end;

  // ファイルのMD5値を取得します。
  function GetMD5(const FileName : String) : String;
  var
    idHMD5 : TIdHashMessageDigest5;
    FS : TFileStream;
  begin
    idHMD5 := TIdHashMessageDigest5.Create;
    FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    try
      // Evernoteエクスポートファイル (ENEX)では小文字が使われています。
      // 大文字のままで動作しましたが、一応元ファイルに合わせています。
      Result := AnsiLowerCase(idHMD5.HashStreamAsHex(FS));
    finally
      FS.Free;
      idHMD5.Free;
    end;
  end;

var
  SL1, SL2: TStringList;
  I: Integer;
  S: String;
begin
  SL1 := TStringList.Create;
  SL2 := TStringList.Create;
  try
    SL2.Delimiter := ',';
    SL2.StrictDelimiter := True;

    SL1.Add(S1);
    SL1.Add(S2);

    // タイトル
    SL1.Add('<title>'+Edit1.Text+'</title>');
    SL1.Add(S3);

    // 本文 1行目から<div></div>でいいみたい
    for I := 0 to Memo1.Lines.Count -1 do
      SL1.Add('<div>'+Memo1.Lines[I]+'</div>');

    // 添付ファイル - このサンプルでは、本文の最後にまとめて追加します。
    for I := 0 to ListView1.Items.Count -1 do
    begin
      S := ListView1.Items[I].SubItems[0]; // ファイル名
      SL1.Add(F1+'"'+GetMD5(S)+'"'+F2); // MD5
    end;

    SL1.Add(S4);

    // 作成日時
    SL1.Add('<created>'+GetCreateDateTime+'</created>');

    // タグ
    SL2.CommaText := Edit2.Text;
    for I := 0 to SL2.Count -1 do
      SL1.Add('<tag>'+SL2[I]+'</tag>');
    SL1.Add(S5);

    // URL
    if Trim(Edit3.Text) <> '' then
      SL1.Add('<source-url>' + Edit3.Text + '</source-url>');
    SL1.Add(S6);

    // ファイルデータ
    for I := 0 to ListView1.Items.Count -1 do
    begin
      S := ListView1.Items[I].SubItems[0]; // ファイル名
      SL1.Add(F3+GetBody(S)+F4+F5+F6);     // ファイルデータ
      SL1.Add(F7+S+F8);
      SL1.Add(F9+ExtractFileName(S)+F10+F11+F12);
    end;

    SL1.Add(S7);
    SL1.Add(S8);
    SL1.SaveToFile(FileName, TEncoding.UTF8);
    Sleep(50); // おまじない
  finally
    SL2.Free;
    SL1.Free;
  end;
end;

// ファイルを作成してEvernoteに読み込ませます。
procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: String;
  Param1, Param2: String;
begin
  FileName := ExtractFilePath(Application.ExeName) + Temp;
  MakeTempFile(FileName);
  if FileExists(FileName) then
  begin
    Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
    Param2 := 'importNotes /s ' + FileName + ' /n ' + Edit4.Text;
    ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
  end;
end;

// 同期
procedure TForm1.Button2Click(Sender: TObject);
var
  Param1, Param2: String;
begin
  Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
  Param2 := 'syncDatabase';
  ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
end;

// 添付ファイルは、ExplorerからのDrag&Dropにて取得します。
procedure TForm1.FormCreate(Sender: TObject);
begin
  //ListView1.
  // サブクラスの設定
  SubClass1:= TSubClass.Create(Self);
  SubClass1.TargetControl := Memo1;
  SubClass1.OnMessageAfter := SubClass1MessageAfter;
  DragAcceptFiles(Memo1.Handle, True);
end;

procedure TForm1.SubClass1MessageAfter(Sender: TObject; var Message: TMessage);
begin
  case Message.Msg of
    WM_DROPFILES: DropFiles(Message.WParam);
  end;
end;

// 添付ファイル
procedure TForm1.DropFiles(H: THandle);

  // ファイルに関連付けられたアイコンを取出し、ImageListに追加します。
  function GetIconToImageList(FileName: String): Integer;
  var
    Icon : TIcon;
    SHFileInfo: TSHFileInfo;
  begin
     Icon:= TIcon.Create;
     try
       SHGetFileInfo(PChar(FileName), 0, SHFileInfo, SizeOf(SHFileInfo),
         SHGFI_SMALLICON or SHGFI_ICON);
       Icon.Handle := SHFileInfo.hIcon;
       Result := ImageList1.AddIcon(Icon);
     finally
       Icon.Free;
     end;
  end;

 // 実行ファイル、リンクファイルとフォルダは、添付ファイルにしない。
 function IsCorrectFileAsAttachment(FileName: String): Boolean;
 var
   S: String;
 begin
   S := UpperCase(ExtractFileExt(FileName));
   Result := (S <> '.LNK') or (S <> '.EXE') or (not FileExists(FileName));
 end;

var
  I, Count: Integer;
  FileName: array [0..255] of Char;
  Files: TStringList;
  S: String;
  LI: TListItem;
begin
  Files := TStringList.Create;
  try
    // ドロップされたファイルの取得
    Count := DragQueryFile(H, Cardinal(-1), nil, 0);
    for I:=0 to Count-1 do
    begin
      DragQueryFile(H, I, FileName, SizeOf(FileName)-1);
      Files.Add(FileName);
    end;
    DragFinish(H);

    // ListViewに追加します。
    for I := 0 to Files.Count - 1 do
    begin
      S := Files[I];
      if IsCorrectFileAsAttachment(S) then
      begin
        LI := ListView1.Items.Add;
        LI.Caption := ExtractFileName(S);
        LI.SubItems.Add(S);
        LI.ImageIndex := GetIconToImageList(S);
      end;
    end;
  finally
    Files.Free;
  end;
end;

end.


実行するとこんな感じ

Runtime



Evernoteではこんな感じ

Evernote




サブクラス用コンポーネント
Delphi Library [Mr.XRAY]
SubClassUnit
http://homepage2.nifty.com/Mr_XRAY/Halbow/Notes/N004.html

|

Evernoteへメモを追加する。

Evernoteに自分で作成したメモを簡単に追加できればいいなと思っていたのですが、EvernoteのAPIは、ThriftなのでDelphiから直接扱うことはできません。だからずっとあきらめていたのですが、先ほどENScriptというものを見つけました。

Evernote
Windowsでの開発
http://www.evernote.com/about/intl/jp/developer/windows.php


Evernoteがインストールされていることが前提条件なのですが、Evernote.exeをパラメーター付でわざわざ起動させるのなら、そのままEvernote使えばいいと思います。しかしENScriptならDelphiから直接メモを追加できそうな感じなので試してみました。

今回createNoteではなく、importNotesを使っているのは、どちらにしても書き込んだメモをファイルとして渡すのであれば、パラメーターが少ないimportNotesの方がすっきりすると考えたからです。 Evernoteエクスポートファイル (ENEX) の仕様は、単純なメモを出力させて、それに基づいて作成していますので、必ずしも正しいとは言えないかも知れません。

実行するとこんな感じ

Memotoevernote1


const
  Temp = 'Evernote.enex';
  S1 = '<?xml version="1.0" encoding="UTF-8"?>'+
       '<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export.dtd">'+
       '<en-export>';  // エクスポートしたファイルには、その時の日時や
                       // EverNoteのバージョンが入っていますが、
                       // なくても動作しましたので、消しました。

  // 複数ある場合は、<note></note>までを繰り返す。
  S2 = '<note>';

  // ここにタイトル <title>タイトル</title>

  S3 = '<content>' +
       '<![CDATA[' +
       '<?xml version="1.0" encoding="UTF-8"?>' +
       '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'+
       '<en-note style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">';

  // ここに本文

  S4 = '</en-note>' +
       ']]>' +
       '</content>';

  // ここに作成日時 <created>20111001T042516Z</created>
  // ここにタグ <tag>タグ1</tag> <tag>タグ2</tag>

  S5 = '<note-attributes>';

  // ここにURL <source-url>http://delphi-fan</source-url>

  S6 = '</note-attributes>' +
       '</note>';


  S7 = '</en-export>';


// Evernoteエクスポートファイル (ENEX)の作成
procedure TForm1.MakeTempFile(FileName: String);

  // UTCの日時-「年月日T時分秒Z」という文字列にします。
  function GetCreateDateTime: String;
  var
    UTC: TSystemTime;
  begin
    GetSystemTime(UTC);
  with UTC do
    Result := Format('%.2d%.2d%.2dT%.2d%.2d%.2dZ',
      [wYear,wMonth,wDay,wHour,wMinute,wSecond]);
  end;

var
  SL1, SL2: TStringList;
  I: Integer;
begin
  SL1 := TStringList.Create;
  SL2 := TStringList.Create;
  try
    SL2.Delimiter := ',';
    SL2.StrictDelimiter := True;

    SL1.Add(S1);
    SL1.Add(S2);

    // タイトル
    SL1.Add('<title>'+Edit1.Text+'</title>');
    SL1.Add(S3);

    // 本文
    for I := 0 to Memo1.Lines.Count -1 do
      if I = 0 then
        SL1.Add(Memo1.Lines[I])
      else
        SL1.Add('<div>'+Memo1.Lines[I]+'</div>');

    SL1.Add(S4);

    // 作成日時
    SL1.Add('<created>'+GetCreateDateTime+'</created>');

    // タグ
    SL2.CommaText := Edit2.Text;
    for I := 0 to SL2.Count -1 do
      SL1.Add('<tag>'+SL2[I]+'</tag>');
    SL1.Add(S5);

    // URL
    if Trim(Edit3.Text) <> '' then
      SL1.Add('<source-url>' + Edit3.Text + '</source-url>');
    SL1.Add(S6);
    SL1.Add(S7);
    SL1.SaveToFile(FileName, TEncoding.UTF8);
    Sleep(50); // おまじない
  finally
    SL2.Free;
    SL1.Free;
  end;
end;


// ファイルを作成してEvernoteに読み込ませます。
procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: String;
  Param1, Param2: String;
begin
  FileName := ExtractFilePath(Application.ExeName) + Temp;
  MakeTempFile(FileName);
  if FileExists(FileName) then
  begin
    Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
    Param2 := 'importNotes /s ' + FileName + ' /n ' + Edit4.Text;
    ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
  end;
end;

// 同期
procedure TForm1.Button2Click(Sender: TObject);
var
  FileName: String;
  Param1, Param2: String;
begin
  Param1 := 'C:\Program Files\Evernote\Evernote\ENScript';
  Param2 := 'syncDatabase';
  ShellExecute(Handle, 'OPEN', PWideChar(Param1), PWideChar(Param2), nil, SW_HIDE);
end;

Evernoteでは、こんな感じ

Memotoevernote2

ノートブックにないノートブックを指定した場合、ローカルノートブックとして新規作成されます。(Delphi_Newは、新規ノートブック)



Memotoevernote3

リストにあるノートブックの場合は、そこにメモが追加されています。(Delphi2は作成済みノートブック)



このサンプルのように、わざわざ入力用ボックスを分けるのであれば、Evernoteを使っているのと変わらないですが、メモの1行目はタイトル、2行目はタグというようにすれば入力も楽でメモとして使えるのではないかと思います。

※このプログラムでは、処理に失敗してもエラーメッセージが表示されません。ENScriptは、エラーを起こすと実行されたままになることがあり、その場合、タスクマネージャーから終了させて下さい。

|

その他のカテゴリー

ADO | ADT | API | ArrayList | ASP.NET | BDE | BDP.NET | BdpConnection | Borland Developer Studio 2006 | CAPICOM | class | ClipBoard | CodeEditor | Convert.ToString | Custom component | DBExpress | Delphi 2005 | Delphi 2006 | Delphi 2007 | Delphi XE2 | Delphi7 | Delphi8 | Device Driver | Dialog | Docking | DocuWorks | Docuworks SDK | Drag&Drop | Evernote | EXCEL | Firebird | FireMonkey | Game | General | Generics | Google Earth COM API | Google Maps | Google SketchUp | Graphic | IDE | Imm | Indy | InstallAware Express6 | InterBase Admin | JWW | Microsoft SQL Server | MyBase | OnMouseDown | Oracle XE | Paradox | PreviewHandler | PrintDialog | PrintPreviewDialog | PropertyGrid | PSDファイル | Ribbon Controls | RichTextBox | Servers | SubClass | TAction | TActionList | TAnimate | TButton | TCategoryButtons | TClientDataSet | TComboBox | TComboBoxEx | TCustomEdit | TDBGrid | TDockTabSet | TDrawGrid | TEdit | TExcelApplication | TFont | TForm | third party | TImage | TLabel | TList | TListBox | TListView | TMemo | TOpenDialog | TOutlookApplication | TPageControl | TPanel | TRichEdit | TShellResources | TStringGrid | TTabControl | TToolBar | TToolButton | TTreeView | TWebBrowser | Update | VCL Styles | WinInet | XE2 | XPman | オープン配列パラメータ | グループ化 | トランスレーションマネージャー | ファイル処理 | ファイル名処理 | 動的配列 | 投票 | 文字列処理 | 日本語入力 | 暗号 | | 音声合成利用