Mantalog

~ blog of the Mantarou, by the Mantarou, for the Mantarou ~

Visual Studioでコピーした文字列を、文字色を保持してExcelに貼り付けるアドインを作ってみた

Visual Studioでコピーしたソースコードを、文字色を保持してExcelに貼り付けたい場面があると思います。ですが、標準の機能ではできません。テキストだけ貼り付けられてしまいます。ということでExcelアドインを作りました。

RichTextPaste for Excel


ネットで調べると「Productivity Power Tools extension」というVisual Studio拡張機能の「HTMLコピー」を使えばいいらしいのですが、Express Editionでは使えないので今回は作ることにしました。
さらに、Visual Studio 2010 Expressあたりで、コピーした文字列をWordに貼り付けると文字化けするという不具合があったので、それも修正した状態で貼り付けるようになってます。
なお、セルではなくオートシェイプに貼り付けた場合は文字色が保持されるようです。

機能概要

Excelアドインをインストールするだけで機能が有効になります。
Visual Studioソースコードをコピーして、Excelに貼り付ければ勝手に文字色が保持されて貼り付けられます。
f:id:mantarou-tarou:20140226234220p:plain

f:id:mantarou-tarou:20140226234216p:plain

インストール方法

  1. 下記のリンクから「RichTextPaste_***.zip」をダウンロードします。
  2. ダウンロードしたファイルを解凍します。
  3. 全てのファイルに対して「ブロックの解除」を行います。
    やり方は、ファイルを右クリックしてプロパティを開き、「ブロックの解除」をクリックして「OK」をクリックします。面倒ですが1ファイルずつ行ってください。まとめて行うソフトもあるようなので、それを使っても大丈夫です。
    f:id:mantarou-tarou:20140227003459p:plain
  4. 「setup.exe」を実行します。

動作原理

Visual Studioでコピーすると、クリップボードに複数の形式でコピーされます。その形式に「Rich Text」はありますが「HTML」はありません。Excelは、貼り付け時に「HTML」形式のテキストがあれば文字色を保持して貼り付けますが、その形式が無い場合はテキストのみ貼り付けます。(動作を見てみての推測。)
ということで、アドインの動作は、Excelがアクティブになった時にクリップボードをチェックして、「Rich Text」形式があって「HTML」形式が無い場合、「Rich Text」からコンバードして生成した「HTML」形式の情報をクリップボードに追加します。

動作メモ

Excelのアクティブの検知

Excelのアクティブの検知は、以下のようにSetWinEventHookで行っています。
この関数だと、C#でもフックができるようです。

    public class ExcelWindow : IDisposable
    {
        delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        [DllImport("user32.dll")]
        static extern bool UnhookWinEvent(IntPtr hWinEventHook);

        private Excel.Application m_excel;
        private WinEventDelegate  m_eventProc;
        private IntPtr            m_WinEventHook;

        public event EventHandler Activate;

        public ExcelWindow(Excel.Application excel)
        {
            const uint EVENT_SYSTEM_FOREGROUND = 3;

            m_excel        = excel;
            m_eventProc    = new WinEventDelegate(winEventProc); // メンバ変数にコピーしないとガベコレに解放されてエラー
            m_WinEventHook = SetWinEventHook((uint) EVENT_SYSTEM_FOREGROUND,
                                             (uint) EVENT_SYSTEM_FOREGROUND,
                                             (IntPtr) 0,
                                             m_eventProc,
                                             (uint) 0,
                                             (uint) 0,
                                             (uint) 0);
        }

        void winEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            if (hwnd.ToInt32() == m_excel.Hwnd)
            {
                if (Activate != null)
                {
                    Activate(this, new EventArgs());
                }
            }
        }

        #region IDisposable

        public void Dispose()
        {
            m_excel = null;
            UnhookWinEvent(m_WinEventHook);
        }

        #endregion
    }

HTMLテキストをクリップボード用テキストに変換

Copying an HTML-fragment to the Clipboard
http://blog.tcx.be/2005/08/copying-html-fragment-to-clipboard.html

Visual Studio 2010の不具合での文字化け問題

Visual Studio 2010 からソースコードPowerPoint にコピーすると文字化けする
http://ufcpp.wordpress.com/2010/05/11/

Click Once用の証明書の作成

makecert -r -pe -n "CN=mantarou" -sv RichTextPaste.pvk RichTextPaste.cer
pvk2pfx -pvk RichTextPaste.pvk -spc RichTextPaste.cer -pfx RichTextPaste.pfx -po "*********"

ClickOnceマニフェストに署名するためのテスト用の証明書を作成する
http://quickprogram.blogspot.jp/2010/03/vc-clickonce.html

クリップボードの状態の確認

デバッグクリップボードの中身を見たくなったので使いました。

Free Clipboard Viewer
http://www.freeclipboardviewer.com/

ローカル環境のHTML開発で「/」を指定しても「index.html」を開くchrome拡張機能を作ってみた

ローカル環境でHTML開発をしている場合、「/」を指定しているリンクはフォルダを開いちゃって面倒です。ですが「index.html」を開く方法をネットで探しても、Webサーバを立てるという解決策が多く、これはちょっと面倒くさい。ということでファイル名を省略していても「index.html」を補完して開くchrome拡張機能を作ってみました。

機能概要

・リンク先のhtmlファイルがローカル環境で、(※1)
 フォルダ指定している場合、(※2)
 「index.html」のファイルの存在をチェックして、(※3)
 ファイルがある場合は補完してリンク先を開きます。
 ファイルが無い場合は、元のリンク先を開きます。

※1:リンク先が「file://」で始まると判断した場合をローカル環境としています
※2:リンク先が「*.htm」or「*.html」で終わらない場合、フォルダ指定とみなします。
※3:ファイルの存在は、「index.html」→「index.htm」→「default.html」→「default.htm」の順番でチェックします。

インストール方法

  1. 以下からredirectIndex.crxをダウンロードします。
    ダウンロードはここ
  2. redirectIndex.crxを拡張機能画面にドラッグアンドドロップして追加します。
    インストール方法の詳細は、ここのHPがわかりやすかったです。
    Chromeにcrxファイルのインストール - ねとめもー
  3. 「ファイルの URL へのアクセスを許可する」をチェックします。
    f:id:mantarou-tarou:20140210100909p:plain

実装メモ

参考HP1

chrome拡張機能の作り方の基本は、ここで勉強しました。
本当に必要なポイントのみを書いているので、シンプルでわかりやすいです。
http://www.casleyconsulting.co.jp/blog-engineer/chrome/chrome%E6%8B%A1%E5%BC%B5%E6%A9%9F%E8%83%BD%E3%81%AE%E4%BD%9C%E6%88%90%E6%96%B9%E6%B3%95-2/

参考HP2

chrome拡張機能jQueryを使用する場合の"contents_scripts"の"js"の書き方は、ここを参考にしました。
http://d.hatena.ne.jp/ksk_kbys/20111109/1320849314

参考HP3

相対パスから絶対パスを求める方法は、ここを参考にしました。
ローカル環境でも問題なく使えました。
http://www.kaasan.info/archives/1856

参考HP4

ファイルの有無のチェックは、ここを参考にしました。
ローカル環境でも問題なく使えました。
http://stackoverflow.com/questions/5163690/check-if-htm-file-exists-on-local-disk-using-javascript

リンク先の書き換え

リンク先の書き換えは、jQueryを使って以下のようにしました。

$(function() {
    $("a").click(function(){
        $(this).attr('href', 新しいリンク先);
    });
});

リボンからマクロを実行するPowerPointアドインを作成する

「PowerPointで使用しているフォントを集計するマクロを作ってみた」で作ったマクロの解説の続きです。
前回はマクロの内部の説明を行いましたが、今回はリボンから実行できるようにする方法です。

参考HP

このHPを参考にさせていただきました。
http://homepage2.nifty.com/suyamsoft/Ribbon/Excel2010/RibbonEditor/

Office Ribbon Editorのダウンロード

Office Ribbon Editorを使用することで、簡単にリボンをカスタマイズするファイルを作成できます。
ということで、下記のサイトからダウンロードしてインストールします。
http://www.leafcreations.org/

インストールはダウンロードした*.exeを実行して、[Next]を選択していけばOKです。

リボンの設定

Office Ribbon Editorを使って、PowerPoint上に表示するリボンの詳細と、ボタンを押したときに実行するマクロを設定します。

  1. Office Ribbon Editorの起動します。
  2. Office Ribbon Editorで、マクロを作ったPowerPointファイルを開きます。
  3. 画面右の[Document Explorer]からファイル名を右クリックして[Add CustomUI]→[Office 2007]を選択します。
    f:id:mantarou-tarou:20140209053151p:plain
  4. [RibbonX12]をダブルクリックします。
  5. 画面中央の[Code]タブにリボンの情報を書き込みます。
    今回は、ボタンを押してマクロを実行する簡単なものなので、画面左の[Toolbox]の[Samples]タブから[Office 2007]→[Custom Ribbon]を選択して、サンプルを入力します。
    f:id:mantarou-tarou:20140209053540p:plain
  6. 入力されたサンプルを参考に、必要な部分を編集します。
    id:ユニークであれば何でもいいはずです。
    label:画面に表示するテキストです。
    buttonのonAction:後で作成するマクロの関数名にします。
    buttonのimageMso:リボンに表示する画像です。こここれを参照して、表示したい画像の名前を設定します。
  7. 保存します。

これで、Office Ribbon Editorでの作業は終了です。[Callbacks]にマクロを書きたいところですが、なぜか保存できないので、これ以降はPowerPointで作業します。

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
	<ribbon startFromScratch="false">
		<tabs>
			<tab id="aggregateTab" label="Aggregate">
				<group id="aggregateGroup" label="Aggregate Font in Use">
					<button id="aggregateButton" label="Aggregate" imageMso="ExportExcel" size="large" onAction="AggregateFontInUse" />
				</group>
			</tab>
		</tabs>
	</ribbon>
</customUI>

ボタンを押したときに実行する関数の作成

  1. リボンの設定を行ったPowerPointのファイルを開きます。
  2. [Alt + F11]でVBEを開きます。
  3. リボンのボタンを押したときに実行する関数を作成します。
    関数名はxmlの"onAction"で設定した名前にします。
    引数は"IRibbonControl"型にします。
Public Sub AggregateFontInUse(control As IRibbonControl)
    Call aggregateFontInUseSub
End Sub

アドインとして保存

  1. "名前を付けて保存"でファイルの種類を"PowerPoint アドイン (*.ppam)"とすれば完成です。

PowerPointの使用中フォント集計マクロの解説

「PowerPointで使用しているフォントを集計するマクロを作ってみた」で作ったマクロの解説です。

PowerPointマクロの作り方

  1. 「新しいプレゼンテーション」でファイルを新規作成します。
    まあ実際はなんでもいいのですが。
    f:id:mantarou-tarou:20140209022840p:plain
  2. [Alt + F11]を押してVBE(Visual Basic Editor)を開きます。
  3. [VBAProject (プレゼンテーション1)」を右クリック→「挿入」→「標準モジュール」をクリックします。
    f:id:mantarou-tarou:20140209024605p:plain
  4. エディタ部分にマクロを書きます。
    f:id:mantarou-tarou:20140209024834p:plain
  5. 実行したいマクロにカーソルを合わせて[F5]で実行します。
    f:id:mantarou-tarou:20140209025016p:plain

マクロの解説

全てのスライドのすべてのオブジェクトを走査する

Private Sub checkAllSlides()
    Dim sld As Slide
    For Each sld In ActivePresentation.Slides
        Dim sh As Shape
        For Each sh In sld.Shapes
            ' shにオブジェクトが入ってくるので、それを使う
        Next
    Next
End Sub

テキストを持っているオブジェクトからテキストを取得する

プレイスホルダやオートシェイプなどのテキストを持っているオブジェクトからのテキストの取得方法です。
Shape::HasTextFrameでテキストの有無をチェックしてから、TextFrame.TextRange.Textで取得します。

    If sh.HasTextFrame Then
        Debug.Print sh.TextFrame.TextRange.Text
    End If

グラフからテキストを取得する

グラフの各場所のテキストの取得方法です。
Shape::TypeがmsoChartの場合、またはShape::HasChartがTrueの場合がグラフのはずなので、その場合にタイトル、凡例、軸のタイトル、軸のテキストを取得します。

    If sh.Type = msoChart Or sh.HasChart Then
        Dim txt
        ' タイトル
        If sh.Chart.HasTitle Then
            Debug.Print sh.Chart.ChartTitle.Text
        End If
    
        ' 凡例
        If sh.Chart.HasLegend Then
            For Each txt In sh.Chart.SeriesCollection
                Debug.Print txt.Name
            Next
        End If

        ' X軸Y軸
        Dim ax
        For Each ax In sh.Chart.Axes
            ' 軸のタイトル
            If ax.HasTitle Then
                Debug.Print ax.AxisTitle.Text
            End If
            
            '-------------
            ' 軸のテキスト
            '-------------
            ' 項目軸 or 系列軸(3Dグラフのみ)
            If ax.Type = XlAxisType.xlCategory Or _
               ax.Type = XlAxisType.xlSeriesAxis Then
                For Each txt In ax.CategoryNames
                    Debug.Print txt
                Next
                
            ' 数値軸
            ElseIf ax.Type = XlAxisType.xlValue Then
                Dim num As Double
                For num = ax.MinimumScale To ax.MaximumScale Step ax.MajorUnit
                    Debug.Print num
                Next
            End If
        Next
    End If

表からテキストを取得する

表のテキストの取得方法です。
Shape::TypeがmsoTableの場合、またはShape::HasTableがTrueの場合が表のはずなので、その場合に各セルのテキストを取得します。

    If sh.Type = msoTable Or sh.HasTable Then
        Dim col As Column
        Dim c As Cell
        For Each col In sh.Table.Columns
            For Each c In col.Cells
                Debug.Print c.Shape.TextFrame.TextRange.Text
            Next
        Next 
    End If

スマートアートからテキストを取得する

スマートアートからのテキストの取得方法です。
Shape::TypeがmsoSmartArtの場合、またはShape::HasSmartArtがTrueの場合がスマートアートのはずなので、その場合に各ノードのテキストを取得します。
ただし、PowerPoint2010以降にしかHasSmartArtメソッドが無いため、バージョン確認後に詳細をチェックします。

' こっちがチェックしてテキストを取得する処理
    If checkHasSmartArt(sh) Then
        Dim n As SmartArtNode
        For Each n In sh.SmartArt.Nodes
            Debug.Print n.TextFrame2.TextRange.Text
        Next
    End If

' こっちがチェックするための関数
' shの型をShapeに固定するとPowerPoint2010以前でエラーになるので型指定しない
Private Function checkHasSmartArt(sh) As Boolean
    checkHasSmartArt = False
    ' PowerPoint 2010以降のみ
    If Application.Version >= 15 Then
        If sh.Type = msoSmartArt Or sh.HasSmartArt Then
            checkHasSmartArt = True
        End If
    End If
End Function

グループ化されているオブジェクトからテキストを取得する

グループ化されている場合は、グループ内の各オブジェクトに対して再帰的にテキストを取得します。Shape::TypeがmsoGroupの場合がグループです。

    If sh.Type = msoGroup Then
        Dim groupSh As Shape
        For Each groupSh In sh.GroupItems
            ' groupShに対して、再度テキスト取得処理を行う。
        Next
    End If

PowerPointからExcelを操作する

CreateObjectでExcel.Applicationを生成すれば、あとは自由自在です。

    ' Excelの生成
    Dim xlApp
    Set xlApp = CreateObject("Excel.Application")
    ' 画面に表示
    xlApp.Visible = True
    
    ' 新規ワークブック生成
    xlApp.Workbooks.Add

    ' セルを変更
    xlApp.Workbooks(1).Worksheets(1).Cells(1, 1) = "Test"

    ' Excelを解放
    Set xlApp = Nothing

PowerPointで使用しているフォントを集計するマクロを作ってみた

PowerPointの資料で、複数フォントが混在していると格好悪い。
ということで、フォントの混在を発見するために、使用しているフォントを集計して一覧出力するマクロを作成しました。 

作り方に関しては、以下を参照してください。
PowerPointの使用中フォント集計マクロの解説 - Mantalog
リボンからマクロを実行するPowerPointアドインを作成する - Mantalog

使い方

  1. 集計したいPowerPointファイルを開きます。
  2. リボンから「Aggregate」をクリックします。

    f:id:mantarou-tarou:20140209011225p:plain

  3. 集計結果がExcelファイルに出力されます。

    f:id:mantarou-tarou:20140209011258p:plain

インストール方法

  1. aggregateFontInUse.ppamを下記サイトからダウンロードします。
    ダウンロードはここ
  2. ダウンロードファイルをどこかに置きます。
  3. 下記のサイトを参考に、ダウンロードしたマクロを登録します。
    参考サイト
  4. インストールが成功すると、リボンにマクロが登録されます。

    f:id:mantarou-tarou:20140209021416p:plain

「bugspots」のWindows版を作ってみた

もう2年も前ですが、googleのバグ予測アルゴリズムを実装した「bugspots」というツールが発表されました。

本家のWebサイト
https://github.com/igrigorik/bugspots
http://google-engtools.blogspot.jp/2011/12/bug-prediction-at-google.html

日本語での紹介
http://www.publickey1.jp/blog/12/bugspots.html

SVN版を作ってくれた方
http://szk-takanori.hatenablog.com/entry/20120109/p1

気にはなっていたのですが、いままでバグ予測をしたい場面が無かったので放置してました。ですが、ちょっと最近これを使用したくなりましたので、自分用にカスタマイズした
bugspots.net
を作ってみました。

f:id:mantarou-tarou:20140115005254p:plain

 オリジナルからの変更点は以下の通りです。

2年経っても誰も作ってなかったので需要は無いかもしれませんが、現場のQA担当の方など試してみてください。 

簡単な使い方

  1. 「URL」に解析したいリポジトリを入力
  2. 「Execute」ボタンを押下
  3. しばし待つ
  4. 「Maximum」や「Total」が大きいところが怪しいので、
    そのあたりのファイルを調査

使い方

  • URL
    リポジトリのURLを入力します。
    例えば、http://server/svn/trunk/
  • Username、Password
    リポジトリにアクセスするユーザ名とパスワードを入力します。
  • View file list
    後述する「Filter regex」を指定すると、ファイル一覧には解析対象のみが表示されます。全ファイルを表示したい場合、この設定を変更します。
    処理が重くなるため、Disable推奨です。
    - Disable:解析対象のみを表示します
    - All files:解析対象以外の全ファイルを表示します。ただし外部参照は表示しません。
    - All files (including externals):解析対象以外の全ファイルを表示します。外部参照も表示します。
  • Start revision、End revision
    解析対象の開始リビジョン・終了リビジョンを入力します。
    推奨は空欄で、その場合は全てのリビジョンが対象になります。
    ただし「Limit」での制限はかかります。
  • Limit
    解析対象のログの数を入力します。
    空欄の場合は全てのログが対象になります。
    推奨は500です。(本家と同じ)
  • Filter regex
    「Filter check file」が空の場合、入力した正規表現がコミットログにマッチすると解析対象になります。例えば不具合のコミットログには「fixes」or「fixed」が入力されている場合、この「Filter regex」に「fixes|fixed」と入力します。
  • Filter check file
    文字列を改行で区切ったファイルを指定します。
    「Filter regex」で抽出した文字列が、このファイルに書かれている場合に解析対象とします。
    例えばRedmineで管理している場合、「Filter check file」には不具合チケットを改行で並べたファイルを指定します。そして「Filter regex」には「refs #([\d]+)」と入力します。これにより正規表現でコミットログからチケット番号を取得し、そのチケットがファイルに存在する場合に解析対象になります。
  • Testボタン
    正規表現のテストダイアログを表示します。
  • Executeボタン
    bugspotsの解析を行います。
  • Export CSVボタン
    解析結果をcsv出力します。

実装に関するメモ

勉強のためWPFを使用しています。

C#からSVNにアクセスしています。これには「SharpSVN」というライブラリを使用しています。最近のVisual StudioではNuGetで取得できるようです。

TreeなGridには「WPF TreeListView Control」というライブラリを使用してます。Gridの縦線・横線を表示するのに苦戦しましたが、非常に使いやすく便利です。