2012-03-31
自己解凍ファイルの様に実行可能ファイルにいろいろ埋め込む
今や、自己解凍Fileなんて温床みたいなもんで使うことは減りましたね。ただ、圧縮はともかくとして、作成済みの実行可能Fileにいろいろねじ込んでやるというのは便利です。例えばJavaの.jarを埋め込んでやり、実行可能FileでVMを起動しその.jarを起動してあげるとか。
さて、作成済みの実行可能Fileに他の情報を埋め込む方法は2つあります。
2.自力で実行可能ファイルにひねりこむ。
1.については、Resourceの種類にRT_RCDATAを指定し以下の関数を使用します。
・BeginUpdateResource
・EndUpdateResource
・UpdateResource
多少制限が有ったり、localeがどうのとか有ったりしますが、使い方やらそのあたりは、
この辺で調べてください。 リソース
で、次に2.の方法です。実行可能Fileの構成をちょっと知っておく必要が有りますが、実に簡単です。
図1
図1をみてください。Exe Fileと書いた凡例があります。この凡例と同じ色の枠線で囲んだ範囲が、実行可能FileのFormatです。大雑把に別けていますが先頭から順に3種類の領域に解れています。さらにこの3種類、Headerは、DOS用とPE用という2つに別れ、Section Header, Sectionは情報の種類に応じて必要なだけ複数に別れています。PEという言葉が出ましたが、Portable Executableと言ってWindows用実行可能Fileの事です。このPE Headerを持つ実行可能File形式をPE Formatと呼んだりします。
図2
さて、このPE Format、実はPE Formatがちゃんと維持されていれば後ろに追加情報を仕込むことができます。
図2のUser Dataという凡例と同じ色の領域がそれです。(User Dataは説明用で公式な名前じゃありません)
PE FormatにSection Headerという部分が有ります。Section Headerは、続くSectionの位置や領域幅、その他属性というのを持っているのですが、どうやらWindowsはこのSection Headerに書いていないSectionについては無視するようになっているようです。本物のSectionの後ろに1GB情報を追記しようが実行できなくなるという事は有りません。ただし、無視はするんですが、主記憶に読み込むかどうかも考えずに読み込んでくれてしまうので、1GB追加分すべて読み込んでしまいます。主記憶に読み込まれるのが邪魔だと思う場合は、Section Headerを弄るかResource APIを使いましょう。
図3
具体的に、どう書き込むかですが、Cで有ればfopenで情報を埋め込む実行可能Fileを開き、書き込み位置を実行可能File末尾に移動。その後ひたすら情報を書き込んでいくだけです。改行やらNULLが問題になる事は有りません。なんでも書き込めます。
さて追加情報を書き込むと、追加情報を読み込むときがどこから始まるのか解りません。こうしなければいけないという理由は有りませんが、図3でいうExtension Sizeの様に実行可能Fileの末尾に追加情報の領域幅を記述する固定幅の領域を作っておくと便利です。読み込みの際、読み込み位置を実行可能File末尾に移動し、Extension Sizeから領域幅を取得、領域幅分Fileの先頭に戻り、領域幅分User Extension領域を読み込みます。
追加情報位置の読み取り方法ですが、若干違う方法として、Extension Sizeの領域にUser Extension領域が始まる実行可能File上の位置を書き込んでやるという方法もあります。
状況に合わせて、どんな方法を使うか考えてみてください。
2012-03-26
見た目ほど役に立たないstatic関数によるprotected通信
C++に限らず、C++の血を引く言語ならできるしょうもない小ネタです。
まぁ、別にstatic関数である必要もないんですがね。
他のclassのprotected関数って呼べませんよね。当然ですが、自分の根底classのprotected関数なら自由に呼ぶことが出来ます。さて根底classが公開しているprotected関数ですが、根底classの一部ですので、同じ根底classのobjectなら無制限に操作できます。これを応用すると、ある根底classから派生class同士でしか使えない独自の通信経路を確立する事が出きるようになります。
/* 根底の定義 */ class Interface { int Value(void) const = 0; protected: int TellValue( const Interface &interface ) { return interface.Value(); } }; /* 派生の定義 */ class Delived: public Interface { /* Interfaceの派生classだけに値を公開 */ int Value(void) const { return 0; } public: /* Interfaceから派生していればDelivedのobjectじゃ無くてもValueをのぞき放題 */ void Peek( const Interface &base ) { printf( "他のObjectのValueをこっそりチラ見:(%d)\n", TellValue(base) ); } };
一見便利そうな気はしますが、publicが若干不便になっただけで大した有り難味はありません。
javascriptによるOOらしい字句解析
var // 解析結果格納object result = new this.arrayBuilder(), stab = new this.stabBuilder(), // 解析条件表 pattern = [ new match( result, new this.identify() ), new match( result, new this.number() ), new match( stab, new this.comment() ), new match( result, new this.operator( [ "(", ")", "{", "}", ",", ";", "*", "=", "+", "-", "/", "%", "&", "#" ] ) ), new match( result, new this.string() ), new match( stab , new this.skip( [ "\n", " ", "\t" ] ) ) ]; try { // 字句解析実行 this.lex( pattern, 'int main(int,char**){ 100; /* comment */ int arg100 = "cmment"; }' ); var // 解析結果を配列化 resultSet = result.create(); // 配列に格納した字句を標準出力に出力 for( var i in resultSet ) { println( resultSet[i] ); } } catch( exception ) { if( exception instanceof this.unmatchException ) { println ( "構文ミス" + " 見つかった位置:" + exception.point() + " 文字目" + " 見つかった不正な文字[" + exception.symbol() + "]" ); } else { println( exception.message ); } }
2012-03-25
javascriptによるOOらしさを目指した字句解析(コピペ用)
某掲示板に載せて、構造はいいがぱっと見分かり辛い、むしろ関数型では?などといい感じの批判をいただいたネタです。どこかで見たことが有ると思ったら、それを書いたのは私かもしれません。
自宅外で自鯖にアクセスすること無く、コピペして簡単に改変使用できるようメモ。
初めに使用例
var // 解析結果格納object result = new this.arrayBuilder(), stab = new this.stabBuilder(), // 解析条件表 pattern = [ new match( result, new this.identify() ), new match( result, new this.number() ), new match( stab, new this.comment() ), new match( result, new this.operator( [ "(", ")", "{", "}", ",", ";", "*", "=", "+", "-", "/", "%", "&", "#" ] ) ), new match( result, new this.string() ), new match( stab , new this.skip( [ "\n", " ", "\t" ] ) ) ]; try { // 字句解析実行 this.lex( pattern, 'int main(int,char**){ 100; /* comment */ int arg100 = "cmment"; }' ); var // 解析結果を配列化 resultSet = result.create(); // 配列に格納した字句を標準出力に出力 for( var i in resultSet ) { println( resultSet[i] ); } } catch( exception ) { if( exception instanceof this.unmatchException ) { println ( "構文ミス" + " 見つかった位置:" + exception.point() + " 文字目" + " 見つかった不正な文字[" + exception.symbol() + "]" ); } else { println( exception.message ); } }
実行結果
int main ( int , char * * ) { 100 ; int arg100 = "cmment" ; }
字句解析機構
/** * 字句解析条件漏れに対する例外情報 * * 引数1: 条件漏れが発生した解析対象文字の位置 * 引数2: 条件漏れを起こした解析対象文字 */ this.unmatchException = function( badPoint, badSymbol ) { this.point = function() { return badPoint; } this.symbol = function() { return badSymbol; } } /** * 字句照合 * * 字句解析対象の文字列に対し、字句ごとに * pattern配列に格納した字句照合器のanalizeを * 一通り適用していく。一つの字句でanalizeに * 成功すると、他の照合器での照合がまだでも * その字句の照合は切り上げる。 * * 字句照合器には、.analize( 解析対象文字列, 解析位置 )の * 実装が求められる。.analizeは戻り値として、引数の解析位置を * そのまま返すか、読み取った字句の長さを解析位置に加算して * 返す必要が有る。 * * 引数1: 字句照合器の配列 * 引数2: 字句解析対象文字列 */ this.lex = function( pattern, source ) { var match, oldPoint, newPoint, i; for( var j = 0; j < source.length; ) { for( i in pattern ) { newPoint = pattern[i].analize( source, j ); oldPoint = j; if( newPoint != j ) { j = newPoint; break; } } if( oldPoint == newPoint ) throw new unmatchException( j, source[j] ); } } /** * Builder用字句照合器 * * 照合器の一例。conditionに指定した照合条件に * 適合する字句をresultに詰め込む。Builder用と * 書いてはいるがresultは、下記の2つを * 実装していれば何でもよい。どちらも値は返さない。 * * ・字句の1文字追加 * .append( 字句中の1文字 ) * * ・字句1個分がappendされた時の通知 * .flush() * * 照合条件には下記の3個を実装する必要が有る。 * すべてtrueかfalseを返す。 * * ・字句開始条件(一致すればtrue) * .beginMatch( 解析対象文字列, 解析位置 ) * * ・字句終了条件(一致すればtrue) * .endMatch( 解析対象文字列, 解析位置 ) * * ・字句終了条件に一致した文字列を字句に含めるか(含めるならtrue) * .endSeek() * * 引数1: 照合結果格納用Builder * 引数2: 照合条件 */ this.match = function( result, condition ) { /** * lexに求められる.analizeの実装 * * 戻値: 解析位置に照合条件に一致した字句の長さを加えた値 * 引数1: 解析対象文字列 * 引数2: 解析位置 */ this.analize = function( source, i ) { if( !condition.beginMatch( source, i ) ) return i; do { result.append( source[i] ); ++i; } while( !condition.endMatch( source, i ) ) if( condition.endSeek() ) { result.append( source[i] ); ++i; } result.flush(); return i; } }
比較補助関数
/** * 範囲検査 * * targetに指定した値がbeginとendの間か調べる。 * * 戻値: targetがbeginとendにあればtrue、そうでなければfalse * 引数1: 検査対象の値 * 引数2: 検査範囲の下限 * 引数3: 検査範囲の上限 */ this.betweenChar = function( target, begin, end ) { return begin <= target && target <= end; } /** * 集合一致検査 * * 検査対象の値が、patternに指定した * 配列の中に含まれているか検査する。 * * 戻値: 配列に検査対象の値が含まれて入ればtrue * 引数1: 検査対象の値 * 引数2: 検査基準となる値集合 */ this.interSection = function( target, pattern ) { for( var i in pattern ) { if( target == pattern[i] ) return true; } return false; }
字句解析条件(必要に応じて追加)
/** * 識別子照合条件 */ this.identify = function() { this.beginMatch = function( source, i ) { return betweenChar( source[i], "A", "z" ); } this.endMatch = function( source, i ) { return !( betweenChar( source[i], "A", "z" ) || betweenChar( source[i], "0", "9" ) ); } this.endSeek = function() { return false; } } /** * 数値照合条件 */ this.number = function() { this.beginMatch = function( source, i ) { return betweenChar( source[i], "0", "9" ); } this.endMatch = function( source, i ) { return !( betweenChar( source[i], "A", "z" ) || betweenChar( source[i], "0", "9" ) ); } this.endSeek = function() { return false; } } /** * 文字列照合条件 */ this.string = function() { this.beginMatch = function( source, i ) { return '"' == source[i]; } this.endMatch = function( source, i ) { return '\\' != source[i - 1] && '"' == source[i]; } this.endSeek = function() { return true; } } /** * comment照合条件 */ this.comment = function() { this.beginMatch = function( source, i ) { return "/" == source[i] && "*" == source[i + 1]; } this.endMatch = function( source, i ) { return "*" == source[i - 1] && "/" == source[i]; } this.endSeek = function() { return true; } } /** * 記号照合条件 */ this.operator = function( pattern ) { this.beginMatch = function( source, i ) { return interSection( source[i], pattern ); } this.endMatch = function( source, i ) { return true; } this.endSeek = function() { return false; } } /** * 無視する字句の条件 */ this.skip = function( pattern ) { this.beginMatch = function( source, i ) { return interSection( source[i], pattern ); } this.endMatch = function( source, i ) { return !interSection( source[i], pattern ); } this.endSeek = function() { return false; } }
結果取得用(必要に応じて追加)
/** * 字句解析結果を並べた配列作成器 */ this.arrayBuilder = function() { var torken = "", array = []; this.append = function( charctor ) { torken += charctor; } this.flush = function() { array.push( torken ); torken = ""; } this.create = function() { var current = array; array = []; return current; } } /** * 不要な値を捨てるための擬似Builder */ this.stabBuilder = function() { this.append = function( charctor ){} this.flush = function(){} }
2012-03-18
Visual Studioとか使わないObject共有(Out Process Server登録)メモ
今はもう全く使う機会が無いかもしれないCOM機構のメモです。
とりあえず、メモ帳とCompilerだけで作れる雛形程度の内容です。
COMとして使用する実行File登録用.batの記述例。
@echo off
rem Classの入ったexeのパス
set COMMAND_PATH=Z:\home\xxxx\example.exe
rem 自作ClassのGUID IFactoryClassから派生したClassのIDを指定する
set CLS_ID={a53e8594-70cc-11e1-a9b5-001fd0556301}
rem 文字列による識別値 とりあえずXxxx.Yyyy形式にしとけばいい
set PROG_ID=Example.LocalServer
rem 補足説明 何をいれようがいいらしい
set COMMENT=Example
set BASE_PATH=HKEY_CLASSES_ROOT\CLSID\%CLS_ID%
set CURRENT_PATH=reg add "%BASE_PATH%\LocalServer32" /t REG_SZ /d "%COMMAND_PATH%"
echo %CURRENT_PATH%
%CURRENT_PATH%
set CURRENT_PATH=reg add "%BASE_PATH%\ProgID" /t REG_SZ /d "%PROG_ID%"
echo %CURRENT_PATH%
%CURRENT_PATH%
set BASE_PATH=HKEY_CLASSES_ROOT\%PROG_ID%
set CURRENT_PATH=reg add "%BASE_PATH%" /t REG_SZ /d "%COMMENT%"
echo %CURRENT_PATH%
%CURRENT_PATH%
set CURRENT_PATH=reg add "%BASE_PATH%\CLSID" /t REG_SZ /d "%CLS_ID%"
echo %CURRENT_PATH%
%CURRENT_PATH%
Objectを提供する。なんにも仕事しないOut Process Serverの定義例。
mingwを使う場合は、こんな感じでCompileします。
$i686-pc-mingw32-g++ -lole32 -luuid main.cpp
cl ole32.lib uuid.lib main.cpp
#include <windows.h> #include <objbase.h> // CLSID辞書。こいつらは.hppに置いておく。あまり行儀のいい実装ではない。 template<class type> class IdMap; // CLSID辞書を引くためのKeyとして空のclassを用意しておく // ついでにInterfaceの定義を書いたりする struct ILocalServer: public ::IClassFactory { }; // ILocalServerのCLSID定義 template<> class IdMap<ILocalServer> { static const ::CLSID &Id(void) { //Registryに書き込んだ例の値を、構造体表記に書き換えて記述する static CLSID id = { 0xA53E8594u, 0x70CCu, 0x11E1u, {0xA9u, 0xB5u, 0x00u, 0x1Fu, 0xD0u, 0x55u, 0x63u, 0x01u} }; return id; } }; /********************** こっから.cppに記述 *********************/ #include "CLSID辞書の入った.hpp" // Factory本体。これが実行Process間で共有される。 class LocalServer: public ::ILocalServer { ::ULONG count; public: // 参照Countの加算処理 STDMETHOD_( ::ULONG, AddRef )(void) { return ++count; } // 参照Countの減算と解放処理 STDMETHOD_( ::ULONG, Release )(void) { // 本来はdeleteすべきなんだろうけど今回は無くても動くので放置 /* if( 0 > --count ) { delete this; } */ --count; return count; } LocalServer(void): count( 0 ) { AddRef(); } virtual ~LocalServer(void) { } // InterfaceのCast。dynamic_castみたいなものらしい。(違う気がする) STDMETHOD( QueryInterface )( const ::IID &id, void **result ) { if( ::IsEqualIID( id, ::IID_IUnknown ) ) { AddRef(); *result = static_cast< ::IUnknown* >( this ); return S_OK; } if( ::IsEqualIID( id, ::IID_IClassFactory ) ) { AddRef(); *result = static_cast< ::IClassFactory* >( this ); return S_OK; } return E_NOINTERFACE; } // 実行File内の他のCOM Objectを作成する STDMETHOD( CreateInstance )( ::IUnknown*, const ::IID&, void** ) { // 動かすつもりが無いので常にエラー return E_NOINTERFACE; } // このclassの解放を制限するとき呼ばれる STDMETHOD( LockServer )( ::BOOL ) { // こっちも動かすつもりがないので常にエラー return E_FAIL; } }; int main() { ::DWORD register_code; ::LocalServer server; // COM初期化 Exがつく分呼び出しより豪華 ::CoInitializeEx( NULL, COINIT_MULTITHREADED ); /* LocalServerのObjectを公開。ちゃんとRegistry登録しておけばいつでも 呼べるようになる。Processは自動起動されるので起動しっぱなしにしておく必要もない。 ただし、Processは起動後なにもしないとすぐ死ぬので、Socketで止めるなり、 Eventで止めるなり、GetMessageで止めるなり、何らかの方法で待機させる必要が有る。 */ if ( FAILED ( ::CoRegisterClassObject ( ::IdMap<ILocalServer>::Id(), static_cast< ::IClassFactory* >( &server ), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, ®ister_code ) ) ) { // Error時のCOM解放 こんなことせずに例外とDetractorで対処すべき ::CoUninitialize(); return 1; } // LocalServerの登録を外し、他のProcessから見えなくする。 ::CoRevokeClassObject( register_code ); ::CoUninitialize(); return 0; }
Out Process Serverの呼出例
Compile方法は、Out Process Serverと同じ。
$i686-pc-mingw32-g++ -lole32 -luuid main.cpp
#include "CLSID辞書の入った.hpp" int main() { ::IClassFactory *factory = NULL; // COMの初期化Exがなく引数も一つ少ない ::CoInitialize( NULL ); // 上の実行Fileで登録したLocalServerを取得する // factory->Relase();を呼び出してあげるのが作法としては正しい気がする。 // 本来は、このあとfactory->CreateInstance()を呼び出すべきだろうが // 面倒なので放置 if ( FAILED ( ::CoGetClassObject ( ::IdMap<ILocalServer>::Id(), CLSCTX_LOCAL_SERVER, NULL, ::IID_IClassFactory, reinterpret_cast<void**>( &factory ) ) ) ) { ::CoUninitialize(); return 1; } ::CoUninitialize(); return 0; }
2012-03-14
整数同士の割り算結果を、4も5も使わず四捨五入する
前回のネタでふと思い出したのでメモ。
今回は特に解説しません。ほかのやり方も知らないんで
四捨五入でこれ以外のやり方をご存知の方は教えてください。
int x = 3, //割られる値 y = 6, //割る値 result = ( ( x * 2 ) / ( y * 2 ) + 1 ) / 2; //切り捨て前提です
分岐なしで4の倍数になるよう切り上げる方法(Bitmapとかのアレ)
経験が長くても悩む人が多いらしい。なぜだ?
とりあえず答えです。浮動小数は無視です。
それから負の場合も無視です。
/** * 戻り値: 基準値の倍数に切り上げた値 * 引数1: 倍数に切り上げたい値 * 引数2: 基準となる値 */ int Saturation( int value, int radix ) { // 割り算は少数を切り捨てるので掛け算が相殺されない return ( ( value + radix - 1 ) / radix ) * radix; } int radix = 4, // 基準を4にしてみる result = Saturation( 13, radix ); // 16 == result
解ると思いますが少々解説します。
( ( value + radix - 1 ) / radix ) * radix;
まず、この式に実際の引数を割り当ててみます。
( ( 13 + 4 - 1 ) / 4 ) * 4;
つぎに算数の規則を無視して4 - 1を最初に計算します。
( ( 13 + 3 ) / 4 ) * 4;
さて、3という数字が現れました。この3は、1以上の値を足すと
必ず4の1倍以上になります。(基準値 - 1)なんで、基準値以上に
なるのは、当たり前っちゃ当たり前です。
次に、この3を13に足してやるとどうなるか。13は、基準値の4で
割ってやると、1が余ります。この1に対し3を足している事になるので、
計算対象の値に含まれる4の倍数が1つ増える事になるのです。
ここで、もし計算対象の値が12など4の倍数であった場合はどうでしょう。
12のあまりは0です。0 + 3は、4の倍数では有りませんので、
計算対象の値に含まれる4の倍数が増えません。
さて、実際に13に3を足してやります。
( ( 16 ) / 4 ) * 4;
割り算結果が既定で切り捨てとなる言語で割り算してやると・・
( 4 ) * 4
4になりました。あとは掛け算して、割った倍率を元に戻します。すると
16
という事で求める値にたどり着きました。
補足的ですが、実用上最後の掛け算は不要です。
4Byteの倍数で実際領域を確保したり、走査線の幅を計算するなら、
4Byteの型を使えばいいからです。
ということで、現実的に直すと
/** * 戻り値: 基準値の倍数に切り上げた値になる基準値の係数 * 引数1: 倍数に切り上げたい値 * 引数2: 基準となる値 */ int Saturation( int value, int radix ) { // 割り算は少数を切り捨てるので掛け算が相殺されない return ( value + radix - 1 ) / radix; } int radix = 4, // 基準を4にしてみる *array = new int[Saturation( 13, radix )]; delete[] array;
こんな感じになります。


