SATOXのシテオク日記

~ふもっふ、ふもふも~

C#(libclang)でCソース解析

人類は誰しも2つの種類に分類される。

C言語ソースファイルを解析したいか解析したくないかである。

自分は前者。

……というわけで、C#C言語のソースを解析しようと、数ヶ月じゃ済まないくらい試行錯誤した末、自力ではかなり難しいという結論に達しまして、素直にlibclangを利用することにしました。 正直、C言語解析なんて余裕だぜと舐めていたのですが、一朝一夕でできるもんじゃないんですよね。libclang.dllのバイナリサイズ144MBが物語っています。

というお話。

ごちゃごちゃ説明はせず、シンプルに情報だけまとめておきます。

nuget Package

ClangSharpというlibclangラッパーモジュールを使用しました。

  • ClangSharp Ver.16.0.0
  • libClangSharp.runtime.win-x64 Ver.16.0.6

ClangSharpはマネージドラッピングされているわけではなく、少しユーティリティがあるくらいの薄いモジュールなのでunsafe属性が必要です。

サンプルコード

以下、解析関数のサンプル。 いろいろな解析方法があると思いますが、VisitChildren関数を使用しています。

using System.Diagnostics;
using ClangSharp.Interop;

public unsafe void AnalyzeCSourceFile(String filePath)
{
    var index = CXIndex.Create();
    var errCode = CXTranslationUnit.TryParse(
        index, 
        filePath,
        new String[0],
        Array.Empty<CXUnsavedFile>(),
        CXTranslationUnit_Flags.CXTranslationUnit_None,
        out var translationUnit);
    if (errCode != CXErrorCode.CXError_Success) {
        throw new Exception($"CXTranslationUnit.TryParse failed, errCode = {errCode.ToString()}");
    }

    // Specify a callback function to analyse
    translationUnit.Cursor.VisitChildren(this.VisitChildrenCb, new CXClientData());
    
    clang.disposeTranslationUnit(translationUnit);
    clang.disposeIndex(index);
}

private unsafe CXChildVisitResult VisitChildrenCb(CXCursor cursor, CXCursor parent, void* client_data)
{
    cursor.Location.GetFileLocation(out var cxVisitFilePath, out var line, out var column, out var offset);

    Debug.WriteLine($"{cxVisitFilePath}({line},{column}): {cursor.KindSpelling}, {cursor.DisplayName}");

    return CXChildVisitResult.CXChildVisit_Continue;
}

VisitChildren関数に渡されたVisitChildrenCbコールバック関数に、ASTと呼ばれる解析要素単位で解析結果がコールされます。

プリプロセス前の情報が必要な場合は、下記フラグに変更すると、より詳しい情報が取得できます。

  • CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord

なにしろいろいろな解析フラグがあって難しい。

clang: Translation unit manipulation

出力例

test.h(5,16): StructDecl, HeaderStructTag
test.h(8,3): TypedefDecl, HeaderStruct
test.h(10,9): TypedefDecl, bool
test.h(12,14): EnumDecl, SceneStateTag
test.h(19,3): TypedefDecl, SceneState
test.c(8,16): StructDecl, StructTestTag
test.c(15,3): TypedefDecl, StructTest
test.c(17,24): TypedefDecl, StructTestHn
test.c(19,5): VarDecl, g_global
test.c(21,12): VarDecl, s_static
test.c(25,5): FunctionDecl, GlobalFunc(int, long)
test.c(35,12): FunctionDecl, staticFunc()

Cソースからヘッダがインクルードされている場合、ヘッダ側の定義も列挙される。(この例ではtest.cからtest.hがインクルードされてる)

VisualStudioのデバッグ出力にこのようなファイル名形式で出力すると、ダブルクリックでソースの該当箇所にジャンプして表示できるので便利。

Cソースの文字コードシフトJISの場合、文字化けする

CソースがシフトJISの場合、取得した文字が化けます。

その場合は、文字列をバイト配列で取得し任意の文字テキストエンコーディング変換してあげる。

private unsafe static String SJISCXStringToString(CXString csString)
{
    var pCString = clang.getCString(csString);

    if (pCString is null) {
        return String.Empty;
    }
    var span = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte *)pCString);
    var ret = Encoding.GetEncoding("Shift-JIS").GetString(span.ToArray());

    return ret;
}

どうもCXCursorなどから取得するCXString型は解放されないようなので、変換後にはclang.disposeString()で解放してあげる必要があると思う。

X(Twitter)ロゴを肉球アイコンに変えるブラウザ拡張機能つくった

自作のPochitter!がこれまで利用できたAPI廃止により使えなくなり、あまつさえ愛すべき「Twitter」という名称の変更。そして愛すべき鳥のアイコンが「𝕏」アイコンに……そんななんとも言えないできごとがあった2023年7月24日(頃)。

「𝕏」なんてちっとも愛嬌ないですよね。

そして、𝕏なんて付けるのはちょっと安易で中二病臭がしますよねぇ、SATO𝕏さん。

……。

XPawPadつくったよ

それはさておき、なにか面白いことをやりたい衝動に駆られ、「https://twitter.comサイトの𝕏ロゴを肉球アイコンに変更するだけ」Chrome/Firefox拡張機能を作ったのでした(多分1時間くらい)。

Chrome(Edge)版、Firefox版がそれぞれオフィシャルサイトで公開されてます。

Chrome 拡張機能(Edgeでも利用化)

Firefox Add-Ons

青い鳥アイコンに戻す拡張機能

ところで、元の青い鳥アイコンに戻す拡張機能が人気ですよね……。

実は、開発中は青い鳥に戻すようにしてたのですが、恐らく「みんな作るだろう」と思ってそうしませんでした。

案の定、調べてみるだけで「X Be Gone」「GoodbyeX」「restore-twitter-icon」「twitter_icon_x_to_bird」などがあるようです。

 

ちなみに、ブラウザ拡張機能の開発はメモ帳1つあれば作れるので、お勧めです。

Welcome to the Chrome Extension Manifest V3 - Chrome Developers

作るのがめんどくさいよう、という方はブラウザ拡張機能のネタをSATOXに教えてくださいw 気が向いたら作ってみますよ~。

C#でXMLにXSLTをかます

以前は結構使う機会があったと思うのですが、最近はどうだろう。XSL Transformations(XSLT)をC#で行いたい機会があって調べたのでメモ。

ちなみに、XSLTとはXMLに記述されたデータをスクリプトなどなしに思い通りの出力形式に変換する仕組み。 必要な情報だけ取り出したり、ページングするためにデータ範囲を決めたりして、主にHTMLに変換したりします。

以下、XMLXSLTを入力としてXMLを出力する関数のサンプル。

static void TransformXslt(String inputXmlFilePath, String inputXsltFilePath, String outputXmlFilePath)
{
     // xmlの読み込み
     var xmlDoc = new XmlDocument();
     xmlDoc.Load(inputXmlFilePath);

     // xsltの読み込み
     var xslt = new XslCompiledTransform();
     var xsltSettings = new XsltSettings() {
        EnableDocumentFunction = true,  // XSLT有効
        EnableScript = true,             // スクリプトブロック有効
     };
     xslt.Load(inputXsltFilePath, xsltSettings, null);

     // 出力XML Writer生成
     var writerSetting = new XmlWriterSettings() {
        Indent = true,            // インデント有効
        IndentChars = "     ", // インデントタブ文字列
     };
     using (var writer = XmlTextWriter.Create(outputXmlFilePath, writerSetting)) {
        // xmlをxsltで変換
        xslt.Transform(xmlDoc, null, writer);
     }
}

www.w3schools.com

メモ:C# WebDriverを使ってウェブサイト(html)をPDF出力

急に必要になったのでメモ。 C# Slenium.WebDriver(chrome)を使ったPDFレンダリング出力方法についてのメモ。

  1. プロジェクトにnugetで以下をインストール
  2. urlをPDF出力(outputPdf)する関数PrintToPdfは以下
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

public void PrintToPdf(String url, String outputPdf)
{
    // WebDriver作成
    var options = new ChromeOptions();
    options.AddArgument("--headless"); // ヘッドレス
    var driverService = ChromeDriverService.CreateDefaultService();
    driverService.HideCommandPromptWindow = true; // コマンドプロンプト非表示

    using (var driver = new ChromeDriver(driverService, options)) {
        // サイトを開く
        driver.Navigate().GoToUrl(url);
        // 印刷設定
        var printOptions = new PrintOptions() {
            Orientation = PrintOrientation.Portrait,
            OutputBackgroundImages = true,
        };
        //printOptions.AddPageToPrint(5); // ページ設定
        // 印刷
        var print = driver.Print(printOptions);
        print.SaveAsFile(outputPdf); // PDF出力

        driver.Quit();
    }
}

コマンドプロンプトを非表示にしてますが、タイミングによって一瞬表示されちゃうのがイマイチ。

https://www.selenium.dev/ja/

Pochitter! を非公開しました!

「公開しました!」とは言うけども、

「非公開しました!」って言いませんよね。

(そりゃあ名詞じゃなくて動詞だから)

 

さて、Twitter社をイーロン・マスク氏が2022年10月に440億ドルで買収。赤字続きで多くの従業員を抱えていた多くの従業員を解雇。

今後のTwitter社の業績がどうなるかはこれからですが、まぁそんなことは一般のユーザーにはそんなに関係がないわけですが、個人的にはおおいに関係ありました。

Twitter APIの有償化とPochitter!

Twitter APIが2023年3月末?から有償化され、無料で使えるAPIはtweetsとusers/meのみでPochitter!は作れないし1日に叩ける回数もお試し程度。

  • tweets 50リクエスト/1日(アプリ), 50リクエスト/1日(ユーザー)
  • users/me 25リクエスト/1日(ユーザー)

月に100ドルを支払うとAPIは使えますが、年間約16万円払うのは普通に考えて無理すぎる。

 

というわけで、Pochitter!はTwitter API有償化に伴い動作しなくなり、終了となります。

すでにベクターさんと自サイトではPochitter!の公開を終了してます。

これまで色々ありました

これまでベクターさんや窓の杜さんで公開されたり、雑誌に掲載されたんですよね。

Pochitter!を利用してくださった方々、ありがとうございました!

Pochitter! Ver.2.72を公開しました!

こんにちは、ねこ動画ばかり観てるSATOXです。

ああ、猫と暮らしたい。

 

さて。

Pochitter! Ver.2.72を公開しました!

今回、ちょっとした機能追加要望をいただきまして、Pochitter!を更新しました。

.NET Framework 4.6.2に対応していたのですが、2022年4月26日にMicrosoftさんがサポートを終了したとのことで、更新したかったんですよね。

今回、サポート継続中の.NET Framework 4.8対応となっています。

Pochitter! Ver.2.72(右クリックメニューから保存、またはVectorさんから)

AirPods Proの無償交換修理で1.3万円取られそうになった話

長いことAirPods Proというイヤホンを使っているんですけど、右側から「ちりちりちりちり」というノイズが入るようになってしまいました。

めっちゃ気になるクリックノイズ。

要するに、壊れてしまったわけです。

 

特に落としたりして衝撃を与えたような事はなく、おそらく最近のファームウェア更新のタイミングからおかしくなってしまったようです。

なんとか治らないかと思い、Appleのサポートページを調べてみたところ、こんな内容が…。

まさにこんな感じの不具合。

というわけで、Appleの正規ストアへ修理の予約をしてAirPods Proを持ち込みました。

 

チェックしてもらったところ、案の定、無償修理サービスプログラムが適用内で無償交換(右側)してくれるとのこと。

んじゃあ「交換お願いします」と話が進んだのですが、聞くところ特に気にならなかった「左側」にも問題があり、「左側の交換をするなら1.3万円かかります、どうしますか?」と言われました。

まぁ「正常じゃない」と言われたまま使い続けるのも気持ちが悪いんで、お金を払って両方交換しても良かったんですが、そろそろ「新型AirPods Pro」が出るような噂もあって、”どうせならお金はそっちに当てたい!” と思い、有償交換は断りました。

 

さて。

このとき交換在庫がなく、後日連絡をもらって引き取りに行くことになったのですが、交換時、衝撃の説明が…。

 

「左右両方、新品に交換しておきました(無償)」

 

……結果、お金を払わなくても両方新品に交換された事実。

1.3万円、支払わなくてよかった……。

 

適当なことを書きますが、AirPodsって左右のペアリングの都合もあって、修理部品として片方だけ交換するってのは面倒なんじゃなかろうか。