2022年9月22日木曜日

C#の Properties.Settings.Default が実際にどこに保存されるか忘備録

C# の Properties.Settings.Default 言語機構として用意していただけるのは有難いのですが、ここに環境系の設定を書いておくとインストールされたマシンの環境によって設定を変えたいとか出てくるんですよ。
ところが、こいつ、一体どこのファイルの設定を見てるねん???(#^ω^)ピキピキ
ってキレそうになる仕様なわけです。

という事で、.Net の古いバージョンでは、下記コードで導き出される場所に初期値が書かれています。
   System.Reflection.Assembly asm =System.Reflection.Assembly.GetExecutingAssembly();
   textBox1.Text = asm.GetName().Version.ToString(); // バージョン番号を取り出す
   // config のデフォルト値が記述されたファイルの場所
   textBox2.Text =  ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath;
.Net の仕様が変わり、現在は
   // Foo.exe.config 場合によっては Foo.dll.config 
   // ここは作成したプロジェクトの実行物の名前に依存します。
   textBox2.Text =  Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Foo.exe.config";
はてさて、ではコードで、これらの要素を変更した値はどこに書き込まれるのか?
いつも大変お世話になります。Stackoverflow さまによると
Where are the Properties.Settings.Default stored? によると
c:\users\USER\AppData\Local\COMPANY\APPLICATION.exe_Url_LOOKSLIKESOMEKINDOFHASH\VERSION\user.config
なんだそうで、How know LOOKSLIKESOMEKINDOFHASH? とは、ごもっともな質問。私も知りたい。
これも Stackoverflow にありました。
How to get hash value in user.config path?
(ノ`□´)ノ⌒┻━┻

2022年8月24日水曜日

IISのResponse Header を無効化 忘備録

PowerShell で、IIS の Server ヘッダや、X-ASPNET-Version ヘッダを消去、TRACEメソッドの無効化がしたくて、いろいろ試してみたが、やれ、静的コンテンツではなく、Exe経由のコンテンツのヘッダが消えないとか、ひどい目にあったので忘備録。

IIS ReWrite モジュールが必要です。

管理者権限で、下記スクリプトを実行します。
スクリプトだけでは完結しないので、コメントも読んでください
#
# PowerShell は BOM付き UTF-8 のエンコーディングで保存する事
#  さもなくば、「文字列に終端記号" がありません」という
#  エラーに悩まされます。(BOMの仕様必要?)
#
#
# X-ASPNET-VERSION を無効にする送信書き換えルールを
#   指定された PSPath に追加する
#
function global:AddXASPNetVersionRule {
  param ( $PSPath )

  echo "$PSPath の X-ASPNET-VERSION ヘッダを無効にします"

  $chk = Get-WebConfigurationProperty `
    -Filter "//rewrite/outboundRules/rule[@name='REMOVE_X-ASPNET-VERSION']" `
    -PSPath $PSPath `
    -Name name 

  if( $chk -eq $null ) {
    Add-WebConfigurationProperty `
      -Filter "//rewrite/outboundRules" `
      -PSPath $PSPath `
      -Name "Collection" `
      -Value @{name="REMOVE_X-ASPNET-VERSION"}

    Set-WebConfiguration `
      -Filter "//rewrite/outboundRules/rule[@name='REMOVE_X-ASPNET-VERSION']/match" `
      -PSPath $PSPath `
      -Value @{serverVariable="RESPONSE_X-ASPNET-VERSION";pattern=".+"}

    Set-WebConfiguration `
      -Filter "//rewrite/outboundRules/rule[@name='REMOVE_X-ASPNET-VERSION']/action" `
      -PSPath $PSPath `
      -Value @{type="Rewrite"}
    echo "  ...処理完了..."
  } else {
    echo "  ...処理済み skip..."
  }
}

#
# SERVER-VERSION を無効にする送信書き換えルールを
#   指定された PSPath に追加する
#
function global:AddServerVersionRule {
  param ( $PSPath )

  echo "$PSPath の Server ヘッダを無効にします"

  $chk = Get-WebConfigurationProperty `
    -Filter "//rewrite/outboundRules/rule[@name='REMOVE_SERVER-VERSION']" `
    -PSPath $PSPath `
    -Name name

  if( $chk -eq $null ) {
    Add-WebConfigurationProperty `
      -Filter "//rewrite/outboundRules" `
      -PSPath $PSPath `
      -Name "Collection" `
      -Value @{name="REMOVE_SERVER-VERSION"}

    Set-WebConfiguration `
      -Filter "//rewrite/outboundRules/rule[@name='REMOVE_SERVER-VERSION']/match" `
      -PSPath $PSPath `
      -Value @{serverVariable="RESPONSE_SERVER";pattern=".+"}
    
    Set-WebConfiguration `
      -Filter "//rewrite/outboundRules/rule[@name='REMOVE_SERVER-VERSION']/action" `
      -PSPath $PSPath `
      -Value @{type="Rewrite"}
    echo "  ...処理完了..."
  } else {
    echo "  ...処理済み skip..."
  }
}

#
# サーバ変数 RESPONSE_X-ASPNET-VERSION を追加する
#     X-ASPNET-VERSION: ヘッダとして利用される変数
#
$asv = Get-WebConfigurationProperty `
       -filter "//rewrite/allowedServerVariables/add[@name='RESPONSE_X-ASPNET-VERSION']"`
       -name name

if ($asv -eq $null)
{
  echo "サーバ変数 RESPONSE_X-ASPNET-VERSION を追加します"
  Add-WebConfigurationProperty `
    -Filter "//rewrite/allowedServerVariables" `
    -PSPath "IIS:\" `
    -Name "Collection" `
    -Value @{name="RESPONSE_X-ASPNET-VERSION"}

}

#
# サーバ変数 RESPONSE_SERVER を追加する
#      SERVER: ヘッダとして利用される変数
#
$asv2 = Get-WebConfigurationProperty `
       -filter "//rewrite/allowedServerVariables/add[@name='RESPONSE_SERVER']"`
       -name name

if ($asv2 -eq $null)
{
  echo "サーバ変数 RESPONSE_SERVER を追加します"

  Add-WebConfigurationProperty `
    -Filter "//rewrite/allowedServerVariables" `
    -PSPath "IIS:\" `
    -Name "Collection" `
    -Value @{name="RESPONSE_SERVER"}
}

#
# X-ASPNET-VERSION ヘッダを無効にする
#

  AddXASPNetVersionRule( "IIS:\" )

  AddXASPNetVersionRule( "IIS:\sites\Default Web Site" )

  # IIS-Admin上で手動追加するとエラーにならないが、コードで
  # 実行するとエラーになる不思議仕様
  #AddXASPNetVersionRule( "IIS:\sites\Default Web Site\foo" )

  # Application で無効化するために必要?
  # 仕様が不明すぎて草
  AddXASPNetVersionRule( "MACHINE/WEBROOT/APPHOST" )

#
# SERVER-VERSION ヘッダを無効にする
#
  AddServerVersionRule( "IIS:\" )

  AddServerVersionRule( "IIS:\sites\Default Web Site" )

  # IIS-Admin上で手動追加するとエラーにならないが、コードで
  # 実行するとエラーになる不思議仕様
  #AddServerVersionRule( "IIS:\sites\Default Web Site\foo" )

  # Application で無効化するために必要?
  # 仕様が不明すぎて草
  AddServerVersionRule( "MACHINE/WEBROOT/APPHOST" )


#
# IISの Server Header を無効化します。
#   静的コンテンツだけに効果があるため気休め
#
  echo "  IIS のサーバヘッダを無効化します  "
  echo "   ※ Application に対しては効果がありません"
  Set-WebConfigurationProperty `
    -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.webServer/security/requestFiltering" `
    -name "removeServerHeader" `
    -value "True"


#
# IISの TRACE を無効化します。
#   静的コンテンツだけに効果があるため気休め
#
  echo "  IIS のTRACE メソッドを無効化します  "
  echo "   ※ Application に対しては効果がありません"
  echo "   コマンドで対応できる範囲は静的コンテンツのみです"
  echo "   要求フィルター>HTTP動詞>Trace;false をIIS設定から手動で行う必要があります"
#
#  要求フィルター HTTP動詞 TRACE をfalseにする
#
# エラーで動作しなくなるので、コマンドによる、このやり方はダメ
#  Add-WebConfigurationProperty `
#    -pspath "IIS:\" `
#    -filter "/system.webServer/security/requestFiltering/verbs" `
#    -name '.' -value @{verb='TRACE';allowed='False'}
#
  $colls = Get-IISConfigSection -CommitPath 'Default Web Site' -SectionPath 'system.webServer/security/requestFiltering' 
    #echo "colls : $colls"
  $verbs = Get-IISConfigCollection -ConfigElement $colls -CollectionName 'verbs'
    #echo "verbs : $verbs"
  try {
    New-IISConfigCollectionElement -ConfigCollection $verbs -ConfigAttribute @{ 'verb'='TRACE';'allowed'=$false }
    Stop-IISCommitDelay
  } catch {
    # 取得して、値を設定するコードが動作しない。(関数の仕様がわからない)
    #  echo " set trace "
    #  $trace = Get-IISConfigCollectionElement -ConfigCollection $verbs -ConfigAttribute @{ 'verb'='TRACE' }
    #  echo "trace : $trace.allowed $trace.verb"
    #  Set-IISConfigAttributeValue -ConfigElement $trace -AttributeName 'allowed' -AttributeValue $false
  }

#
# Web設定を無効にしてもアプリケーションからのヘッダが無効になるとは言ってない
# 気休め(無い方がまし)
#
#$rule3 = Get-WebConfigurationProperty `
#          -filter "/system.webServer/httpProtocol/customHeaders/add[@name='X-Powerd-By']" `
#          -Name name
#
#if ($rule2 -eq $null)
#{
#  echo "clear X-Powered-By"
#  Clear-WebConfiguration "/system.webServer/httpProtocol/customHeaders/add[@name='X-Powered-By']"
#}

2022年7月29日金曜日

apache maven で Jni DLL を梱包・備忘録

肝は、resources セクションの resource に対して、filtering=false を指定する事。
この filtering を true にすると、対象リソースをテキストファイルと見なして、文字列の置換が実行されてしまう。
dll ファイルは、バイナリで、utf-8の文字コード規則に収まらないから、false にしないとエラーになってしまう。

後は、Bundle-NativeCode に dll ファイルを resource セクションで指定したディレクトリからの相対パスで ; により列挙すればOK

  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
      <resource>
        <directory>src/main/extlib</directory>
        <filtering>false</filtering>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven.compiler.plugin.version}</version>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>${maven.bundle.plugin.version}</version>
        <extensions>true</extensions>
        <configuration>
          <instructions>
            <Build-Number>${buildId}</Build-Number>
            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
            <Bundle-ContactAddress>${sdk.contact.address}</Bundle-ContactAddress>
            <Bundle-Version>${project.version}</Bundle-Version>
            <Bundle-NativeCode>native/myJni.dll;native/libssl-1_1-x64.dll;native/zlib1.dll</Bundle-NativeCode>
            <Export-Package/>
            <Private-Package>jp.co.foo.bar</Private-Package>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>

java のコードで、loadLibraryを記述する


  private static boolean tryLoadLibrary(String libname) {
    try {
      System.loadLibrary(libname);
      return true;
    } catch(Exception err) {
      err.printStackTrace();
    }
    return false;
  }

  // Bundle-NativeCode の dll を読み込む。Platformにより拡張子が異なるため
  // Java のコードでは、.dll .so といった名称まで指定しなくても良い。
  public static void tryLoadLibraries() {
    tryLoadLibrary("native/libssl-1_1-x64");
    tryLoadLibrary("native/zlib1");
    tryLoadLibrary("native/myJni");
  }
  
  // Load JNI
  static {
    // 自動でライブラリがロードされるはずなのだが、
    // この初期化が行われるより先に、JNI の関数が呼ばれる事態が
    // 発生する事がある。よって、クリティカルな場所から重複して
    // tryLoadLibraries をコールしても大丈夫なように実装している。
    tryLoadLibraries();
  }

2022年5月18日水曜日

WIX で VCRuntime のインストール忘備録

そろそろ VCのRUNTIMEをVisual Studio 2019に移行しとかないと、コンパイルできないライブラリが増えてきたかな?と思いまして、2015 から 2019 に引き上げました。
WIXを使ったインストーラで、2019用のVC runtime のマージモジュールを探したが見つかりません。
ほぇ?と思って検索したらマージモジュールを使用したコンポーネントの再配布に書いてありました。

Visual Studio 2019 以降、Visual C++ 再頒布可能ファイルのマージ モジュールは非推奨です
Visual Studio 2019 以降、Visual C++ 再頒布可能ファイルのマージ モジュールは非推奨です
Visual Studio 2019 以降、Visual C++ 再頒布可能ファイルのマージ モジュールは非推奨です
ふぁ???
という事で、VC Runtime がインストールされているかどうかチェックが必要になりました
Wix per user installer to detect the Visual C++ 2015 Redistributableにチェック方法がありました。StackOverflowさまさまです
How to check if the Microsoft Visual C++ Runtime is installedにも詳細が記述されてましたが、こちらだとサービスパックが追加される毎に対応が必要そうで、ちょっと重たい。

という事で、
<Product ...>
  ...
vc runtime 2015 x86 のインストール状況プロパティ
  <!-- C++ 2015 -->
  <Property Id="CPPRUNTIME2015X86" Secure="yes">
    <RegistrySearch Id="mfc140x86_23026" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{74d0e5db-b326-4dae-a6b2-445b9de1836e}" Type="raw" />
    <RegistrySearch Id="mfc140x86_24215" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{e2803110-78b3-4664-a479-3611a381656a}" Type="raw" />
  </Property>
vc runtime 2017 x86 のインストール状況プロパティ
  <!-- C++ 2017 -->
  <Property Id="CPPRUNTIME2017X86" Secure="yes">
    <RegistrySearch Id="mfc1416x86" Root="HKCR" Key="Installer\Dependencies\VC,redist.x86,x86,14.16,bundle" Type="raw" />
  </Property>
vc runtime 2019 x86 のインストール状況プロパティ
  <!-- C++ 2019 -->
  <Property Id="CPPRUNTIME2019X86" Secure="yes">
    <?foreach CPPRUNTIMEVERSIONPREFIX in 21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40?>
      <RegistrySearch Id="mfc14$(var.CPPRUNTIMEVERSIONPREFIX)x86" Root="HKCR" Key="Installer\Dependencies\VC,redist.x86,x86,14.$(var.CPPRUNTIMEVERSIONPREFIX),bundle" Type="raw" />
    <?endforeach ?>
  </Property>
vc runtime 2019 x86 がインストールされていないと、インストールを中止する条件の追加
  <!-- インストール時にC++ 2019 Runtime(x86) がインストールされているかチェック -->
  <Condition Message="Microsoft Visual C++ 2019 (x86) Redistributable missing">
    <![CDATA[Installed Or CPPRUNTIME2019X86]]>
  </Condition>
vc runtime 2015 x64 のインストール状況プロパティ
  <!-- C++ 2015 -->
  <Property Id="CPPRUNTIME2015X64" Secure="yes">
    <RegistrySearch Id="mfc140x64_23026" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{e46eca4f-393b-40df-9f49-076faf788d83}" Type="raw" />
    <RegistrySearch Id="mfc140x64_24215" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{d992c12e-cab2-426f-bde3-fb8c53950b0d}" Type="raw" />
  </Property>
vc runtime 2017 x64 のインストール状況プロパティ
  <!-- C++ 2017 -->
  <Property Id="CPPRUNTIME2017X64" Secure="yes">
    <RegistrySearch Id="mfc1416x64" Root="HKCR" Key="Installer\Dependencies\VC,redist.x64,amd64,14.16,bundle" Type="raw" />
  </Property>
vc runtime 2019 x64 のインストール状況プロパティ
  <!-- C++ 2019 -->
  <Property Id="CPPRUNTIME2019X64" Secure="yes">
    <?foreach CPPRUNTIMEVERSIONPREFIX in 21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40?>
      <RegistrySearch Id="mfc14$(var.CPPRUNTIMEVERSIONPREFIX)x64" Root="HKCR" Key="Installer\Dependencies\VC,redist.x64,amd64,14.$(var.CPPRUNTIMEVERSIONPREFIX),bundle" Type="raw" />
    <?endforeach ?>
  </Property>
vc runtime 2019 x64 がインストールされていないと、インストールを中止する条件の追加
  <!-- インストール時にC++ 2019 Runtime(x64) がインストールされているかチェック -->
  <Condition Message="Microsoft Visual C++ 2019 (x64) Redistributable missing">
    <![CDATA[Installed Or CPPRUNTIME2019X64]]>
  </Condition>
vc runtime 2010 x64 のマージモジュールを追加
  <Directory Id="System64Folder">
   <!-- VC100 ATL Runtime -->
   <Merge Id="Microsoft_VC100_ATL_x64.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_ATL_x64.msm" />
   <Merge Id="Microsoft_VC100_CRT_x64.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_CRT_x64.msm" />
   <Merge Id="Microsoft_VC100_MFCLOC_x64.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_MFCLOC_x64.msm" />
   <Merge Id="Microsoft_VC100_MFC_x64.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_MFC_x64.msm" />
   <Merge Id="Microsoft_VC100_OpenMP_x64.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_OpenMP_x64.msm" />
  </Directory>
vc runtime 2010 x86 のマージモジュールを追加
  <Directory Id="SystemFolder">
      <!-- VC100 ATL Runtime -->
      <Merge Id="Microsoft_VC100_ATL_x86.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_ATL_x86.msm" />
      <Merge Id="Microsoft_VC100_CRT_x86.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_CRT_x86.msm" />
      <Merge Id="Microsoft_VC100_MFCLOC_x86.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_MFCLOC_x86.msm" />
      <Merge Id="Microsoft_VC100_MFC_x86.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_MFC_x86.msm" />
      <Merge Id="Microsoft_VC100_OpenMP_x86.msm" Language="0" DiskId="1" SourceFile="C:/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC100_OpenMP_x86.msm" />
  </Directory>
vc runtime 2010 x64 と x86 のマージモジュールを参照
  <Feature ...>
      <!-- VC10 runtime -->
      <MergeRef Id="Microsoft_VC100_ATL_x64.msm" />
      <MergeRef Id="Microsoft_VC100_CRT_x64.msm" />
      <MergeRef Id="Microsoft_VC100_MFCLOC_x64.msm" />
      <MergeRef Id="Microsoft_VC100_MFC_x64.msm" />
      <MergeRef Id="Microsoft_VC100_OpenMP_x64.msm" />
      <!-- VC10 runtime -->
      <MergeRef Id="Microsoft_VC100_ATL_x86.msm" />
      <MergeRef Id="Microsoft_VC100_CRT_x86.msm" />
      <MergeRef Id="Microsoft_VC100_MFCLOC_x86.msm" />
      <MergeRef Id="Microsoft_VC100_MFC_x86.msm" />
      <MergeRef Id="Microsoft_VC100_OpenMP_x86.msm" />
  </Feature>
  ...
</Product>
というように対応できそうです。

2022年5月13日金曜日

Windows DLL x86とx64で異なる関数名調整の備忘録

AESのDLLをビルドしていて、x64でビルドしたものは素直に利用できるのにx86でビルドしたものを利用しようとするとLNK2019: _aes_encrypt_key が見つからないというリンクエラーになってしまった。
test_lib.exe のソースを見ると
#ifdef  AES_MODES

    fn->fn_test_align = (g_talign*)GetProcAddress(h_dll, etad_name);
    fn->fn_mode_reset = (g_reset*)GetProcAddress(h_dll, eres_name);
    fn->fn_ecb_enc = (g_enc1*)GetProcAddress(h_dll, ecbe_name);
    fn->fn_ecb_dec = (g_dec1*)GetProcAddress(h_dll, ecbd_name);
    fn->fn_cbc_enc = (g_enc2*)GetProcAddress(h_dll, cbce_name);
    fn->fn_cbc_dec = (g_dec2*)GetProcAddress(h_dll, cbcd_name);
    fn->fn_cfb_enc = (g_enc3*)GetProcAddress(h_dll, cfbe_name);
    fn->fn_cfb_dec = (g_enc3*)GetProcAddress(h_dll, cfbd_name);
    fn->fn_ofb_cry = (g_enc3*)GetProcAddress(h_dll, ofb_name);
    fn->fn_ctr_cry = (g_enc4*)GetProcAddress(h_dll, ctr_name);

    if(   !fn->fn_mode_reset || !fn->fn_test_align
       || !fn->fn_ecb_enc || !fn->fn_ecb_dec
       || !fn->fn_cbc_enc || !fn->fn_cbc_dec
       || !fn->fn_cfb_enc || !fn->fn_cfb_dec
       || !fn->fn_ofb_cry || !fn->fn_ctr_cry )
       ok = 0;
#endif
と、DLLを LoadLibrary した後で、GetProcAddress により関数のアドレスを取得し、呼び出すという事をしていた。
で、名前の定義は aestst.h に記述されており、x86 と x64 で切り替えている。
#if defined( _WIN64 )

#define gt_name             "aes_init"
#define ek_name128          "aes_encrypt_key128"
#define ek_name192          "aes_encrypt_key192"
#define ek_name256          "aes_encrypt_key256"
#define ek_name             "aes_encrypt_key"
#define eb_name             "aes_encrypt"
#define dk_name128          "aes_decrypt_key128"
#define dk_name192          "aes_decrypt_key192"
#define dk_name256          "aes_decrypt_key256"
#define dk_name             "aes_decrypt_key"
#define db_name             "aes_decrypt"

#define etad_name           "aes_test_alignment_detection"
#define eres_name           "aes_mode_reset"
#define ecbe_name           "aes_ecb_encrypt"
#define ecbd_name           "aes_ecb_decrypt"
#define cbce_name           "aes_cbc_encrypt"
#define cbcd_name           "aes_cbc_decrypt"
#define cfbe_name           "aes_cfb_encrypt"
#define cfbd_name           "aes_cfb_decrypt"
#define ofb_name            "aes_ofb_crypt"
#define ctr_name            "aes_ctr_crypt"

#else

#define gt_name             "_aes_init@0"
#define ek_name128          "_aes_encrypt_key128@8"
#define ek_name192          "_aes_encrypt_key192@8"
#define ek_name256          "_aes_encrypt_key256@8"
#define ek_name             "_aes_encrypt_key@12"
#define eb_name             "_aes_encrypt@12"
#define dk_name128          "_aes_decrypt_key128@8"
#define dk_name192          "_aes_decrypt_key192@8"
#define dk_name256          "_aes_decrypt_key256@8"
#define dk_name             "_aes_decrypt_key@12"
#define db_name             "_aes_decrypt@12"

#define etad_name           "_aes_test_alignment_detection@4"
#define eres_name           "_aes_mode_reset@4"
#define ecbe_name           "_aes_ecb_encrypt@16"
#define ecbd_name           "_aes_ecb_decrypt@16"
#define cbce_name           "_aes_cbc_encrypt@20"
#define cbcd_name           "_aes_cbc_decrypt@20"
#define cfbe_name           "_aes_cfb_encrypt@20"
#define cfbd_name           "_aes_cfb_decrypt@20"
#define ofb_name            "_aes_ofb_crypt@20"
#define ctr_name            "_aes_ctr_crypt@24"

#endif
なるほど、だから x64 ではリンクエラーにならず、x86ではリンクエラーになったのだ。
x86 と x64 でマングリングの名前が違う事に自分も苛ついてはいました。
def ファイルに alias 指定できないの???と思って調べたら、ありました。
EXPORTS
alias=function_name
としてやれば良かったようです。
という事で、リンクエラーになった x86 用のビルドだけ以下のdefファイルを適用するようにしました。
LIBRARY		Aes

EXPORTS
   _aes_init=_aes_init@0
   _aes_encrypt_key128=_aes_encrypt_key128@8
   _aes_encrypt_key192=_aes_encrypt_key192@8
   _aes_encrypt_key256=_aes_encrypt_key256@8
   _aes_encrypt_key=_aes_encrypt_key@12
   _aes_encrypt=_aes_encrypt@12
   _aes_decrypt_key128=_aes_decrypt_key128@8
   _aes_decrypt_key192=_aes_decrypt_key192@8
   _aes_decrypt_key256=_aes_decrypt_key256@8
   _aes_decrypt_key=_aes_decrypt_key@12
   _aes_decrypt=_aes_decrypt@12

   _aes_test_alignment_detection=_aes_test_alignment_detection@4
   _aes_mode_reset=_aes_mode_reset@4
   _aes_ecb_encrypt=_aes_ecb_encrypt@16
   _aes_ecb_decrypt=_aes_ecb_decrypt@16
   _aes_cbc_encrypt=_aes_cbc_encrypt@20
   _aes_cbc_decrypt=_aes_cbc_decrypt@20
   _aes_cfb_encrypt=_aes_cfb_encrypt@20
   _aes_cfb_decrypt=_aes_cfb_decrypt@20
   _aes_ofb_crypt=_aes_ofb_crypt@20
   _aes_ctr_crypt=_aes_ctr_crypt@24
こうする事で、見事にLNK2019を回避する事ができました。
今まで、やり方がわからなくて、Delphi 用 DLL には序数で指定したりと、大変だったんですよね。

2022/06/29 追記:
bigtypes.h に
#ifndef RETURN_VALUES
#  define RETURN_VALUES
#  if defined( DLL_EXPORT )
#    if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
#      define VOID_RETURN    __declspec( dllexport ) void __stdcall
#      define INT_RETURN     __declspec( dllexport ) int  __stdcall
#    elif defined( __GNUC__ )
#      define VOID_RETURN    __declspec( __dllexport__ ) void
#      define INT_RETURN     __declspec( __dllexport__ ) int
#    else
#      error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
#    endif
#  elif defined( DLL_IMPORT )
#    if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
#      define VOID_RETURN    __declspec( dllimport ) void __stdcall
#      define INT_RETURN     __declspec( dllimport ) int  __stdcall
#    elif defined( __GNUC__ )
#      define VOID_RETURN    __declspec( __dllimport__ ) void
#      define INT_RETURN     __declspec( __dllimport__ ) int
#    else
#      error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
#    endif
#  elif defined( __WATCOMC__ )
#    define VOID_RETURN  void __cdecl
#    define INT_RETURN   int  __cdecl
#  else
#    define VOID_RETURN  void
#    define INT_RETURN   int
#  endif
#endif
と定義されていて、x64は呼び出し規約が異なるので問題なく動作するが、x86で利用する場合は利用する側で
DLL_IMPORT をプリプロセッサの定義に追加しないと、コールスタックが破壊されて、よくわからないエラーになる
尚、aes.dll を使った dll を書きたい場合は、DLL_EXPORT や DLL_IMPORT といった汎用的な定義で判別していると、定義がバッティングするので、AES_DLL_EXPORT と言った定義に変更した方が良いと思う。できれば、DLL_IMPORT は定義しなくても AES_DLL_EXPORT が定義されていなかったら、利用する側のコードだと判別できるので、このマクロの書き方は改良した方が良いかな?

もうひとつ、vsyasm の -f オプションが Win32 ではなく win32 しか認識しないので、
vsyasm.props ファイルを修正する
...
      <CommandLineTemplate Condition="'$(Platform)'=='Win32'">"$(YASM_PATH)"vsyasm.exe -Xvc -f win32 [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
      <CommandLineTemplate Condition="'$(Platform)'!='Win32'">"$(YASM_PATH)"vsyasm.exe -Xvc -f $(Platform) [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
...

2022年4月25日月曜日

oracle x64 と x86 なクライアント環境を切替え(windows)忘備録

Oracle が面倒くさいのである。

請負の開発をしていると、このプロジェクトは32bitアプリで、このプロジェクトは64bitアプリで、という事は、普通にあります。
で、Oracle は2つ同時に両立しないので、両立させる環境の構築についての忘備録

  • x64版のOracle Clientをインストール
  • 環境変数 ORACLE_HOME を ORACLE_HOME_X64 に変更
  • インストール先の名前を _x64 付きに変更
  • x86版のOracle Clientをx64と同じフォルダ名(_x64なし)にインストール
  • インストール先の名前を _x86 付きに変更
  • インストール先の名前に _x86 のリンクディレクトリを作成する
  • 切替用のバッチファイルを作成

x64 用バッチのサンプル
cd %ORACLE_HOME%
cd ..
rmdir client_1
mklink /D client_1 client_1_x64

x86 用のバッチのサンプル
cd %ORACLE_HOME%
cd ..
rmdir client_1
mklink /D client_1 client_1_x86

これで、x64 と x86 を切替えできる。切り替えた後は、Windows を再起動

尚、リンクディレクトリの作成は、mklink /D により行う

2022年3月31日木曜日

datetime の 曖昧なフォーマットに対応する忘備録

日付のフォーマットと制約が厳しい。
わりとありそうな日付の表記ゆれをパースするグラマーを構築してみた。
#include <boost/spirit/include/qi.hpp>  // boost::spirit::qi を利用します
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <iostream>
#include <string>


#include <boost/date_time.hpp>

namespace pt = boost::posix_time;
namespace qi = boost::spirit::qi;


struct timeinfo {
  int16_t   year_;
  uint16_t  month_;
  uint16_t  day_;
  uint16_t  hour_;
  uint16_t  minute_;
  uint16_t  second_;
  //uint32_t  millisec_;
  std::string millisec_;
};

BOOST_FUSION_ADAPT_STRUCT(
  timeinfo,
  (int16_t,     year_)
  (uint16_t,    month_)
  (uint16_t,    day_)
  (uint16_t,    hour_)
  (uint16_t,    minute_)
  (uint16_t,    second_)
  //(uint32_t,    millisec_)
  (std::string,  millisec_)
)

template <typename Iterator, typename Skipper>
struct time_grammar : qi::grammar<Iterator, timeinfo(), Skipper> {
  qi::rule<Iterator, timeinfo(), Skipper>  start_;
  qi::int_parser<int16_t,10,4,4> int4_;
  qi::uint_parser<uint16_t,10,2,2> uint2_;
  qi::rule<Iterator, std::string(), Skipper> numeric_;

  time_grammar() : time_grammar::base_type(start_, "time_grammar") {
    numeric_ = *qi::char_('0','9');
    start_ = (
        int4_ 
        >> -(qi::lit('/') | qi::lit('-')) >> uint2_
        >> -(qi::lit('/') | qi::lit('-')) >> uint2_
        //>> (qi::lexeme[qi::lit(' ')] | qi::lit('T'))
        >> -(qi::lit('T'))
        >> uint2_
        >> -(qi::lit(':')) >> uint2_
        >> -(qi::lit(':')) >> uint2_
        >> (
          //-( (qi::lit('.') | qi::lit(',')) >> qi::uint_)
          -( (qi::lit('.') | qi::lit(',')) >> numeric_)
          | qi::attr(0)
        )
        >> -(qi::lit('Z'))
    );
  }
};



int main() {
  
  std::string input[] = {
    "20210412T123455Z",
    "20210412T123455,987654321Z",
    "20210412T123455.987654321Z",
    "2004-03-21 12:45:33",
    "2004/03/21 12:45:33",
    "23.09.2004 04:12:21",
    "2003-02-11"
  };

  time_grammar<std::string::iterator, qi::standard::space_type> myg;
  for( int i = 0; i < sizeof(input)/sizeof(input[0]); i++ ) {
    timeinfo ti;
    qi::phrase_parse( input[i].begin(), input[i].end(), myg, qi::standard::space, ti );
    std::cout << "--------------" << std::endl;
    std::cout << ti.year_ << std::endl;
    std::cout << ti.month_ << std::endl;
    std::cout << ti.day_ << std::endl;
    std::cout << ti.hour_ << std::endl;
    std::cout << ti.minute_ << std::endl;
    std::cout << ti.second_ << std::endl;
    std::cout << ti.millisec_ << std::endl;
  }
  return 0;
}
2023/06/02 追記: lexeme がうまく動作しないのでスペースを無視した。millisec が ,020Z と指定された場合 2 に変換されてしまうので std::string で扱うようにした。バグでした。

2022年3月28日月曜日

boost::log windows で /dev/null 忘備録

boost::log でデバッグ用ログの埋め込みを行っていて、リリース時には取り除きたい場合の措置。
これだと、nul デバイスに対して出力する処理が入るので、負荷を取り除くという面では、よろしくないコード

ポイントは、
Windows では nul というファイルが /dev/null に相当する
nul というファイルは、どこにでも存在している扱いになるので、追加書き込みモードで開く必要がある点

#include <boost/date_time.hpp>

#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>

namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;
namespace sinks = boost::log::sinks;
namespace expr  = boost::log::expressions;

...

//#define DEBUG_LOG_FILE  "c:/logs/%Y_%m_%d.log"

...

  boost::log::add_file_log(
#ifdef DEBUG_LOG_FILE
    keywords::file_name = DEBUG_LOG_FILE,
#else
    keywords::file_name = "nul",
    keywords::open_mode = std::ios_base::out | std::ios_base::app,
#endif
    keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
    keywords::format = 
    (
        expr::stream
            << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
            << ": <" << logging::trivial::severity
            << "> " << expr::smessage
    ),
    keywords::auto_flush = true
  );
  BOOST_LOG_TRIVIAL(debug) << "this is a debug output.";

...

2022年1月24日月曜日

Vcpkg autolink を無効にする忘備録

vcpkg で、AutoLinkが有効になっている、明後日の方向のライブラリにリンクしてしまい、不可解なエラーに悩まされます。

なので、AutoLink を無効にしたい。

*.vcxprj を開きます。
PropertyGroup を検索し、以下の設定を追加します。
  <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='UnicodeRelease|x64'">
    <VcpkgAutoLink>false</VcpkgAutoLink>
  </PropertyGroup>
  <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='UnicodeRelease|Win32'">
    <VcpkgAutoLink>false</VcpkgAutoLink>
  </PropertyGroup>

UnicodeRelease|x64 等は、自分のビルド構成と合わせるようにしましょう。
以上。

vcpkg BuildTool を古いバージョンに変更備忘録

書いておかないと、忘れて調べ直す事になるので・・・
vcpkg でビルドをすると 最新バージョンのVisual Studio が使用されるようになって、それでは困る場合があるので、その設定の仕方。

vcpkg/triplets ディレクトリ下のファイルに
arm64-windows.cmake
arm-uwp.cmake
x64-linux.cmake
x64-osx.cmake
x64-uwp.cmake
x64-windows.cmake
x64-windows-static.cmake
x86-windows.cmake
とファイルが並んでいます。
その x64-windwos.cmake と x86-windows.cmake に対して
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_PLATFORM_TOOLSET "v140")
set(VCPKG_DEP_INFO_OVERRIDE_VARS "v140")
というように VCPKG_PLATFORM_TOOLSET, VCPKG_DEP_INFO_OVERRIDE_VARS を追加し、TOOLSET のバージョンを指定します。
以上。

なんか、一時的にブロガーのポストができなくなって、前のポストを一旦削除して、作り直しました。

C++データを16進表記相互変換

ちょくちょくデータを暗号化して16進数表記文字列に変換して保存し、保存された16進表記文字列をデータに復元する事をやるので、忘備録として記録する事に。こういう、ちょっとしたものでも、書こうと思ったら時間を取られる。
#include <string>
#include <iostream>

static int atob( int c ) {
  static const unsigned char btbl[] = {
    // 0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  0
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  1
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  2
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05,  0x06, 0x07,  0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  3
    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,  0x0F, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  4
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  5
    0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,  0x0F, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  6
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  7
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  8
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  9
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  A
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  B
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  C
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  D
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //  E
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00   //  F
  };
  return btbl[ c ];
}


//! 16進文字列から unsigned char 配列への変換
/*
  @note 16進文字列は、0-9, A-F のみを利用した表記で
    1バイトを2文字で表記しているものとする。16進を示す 0x 等の
    接頭辞は含まれていない。
*/
void hexstr2str(
  const std::string& src,
  std::string& dst
) {
  dst.resize( (src.size() + 1) / 2 );
  for(size_t i = 0; i < src.size(); i++) {
    if( (i & 1) != 0 ) {
      dst[ i / 2 ] |= atob( src[i] );
    } else {
      dst[ i / 2 ] = atob( src[i] ) << 4;
    }
  }
}

//! unsigned char 配列から 16進文字列への変換
/*!
  @note 16進文字列は、0-9, A-F のみを利用した表記で
    1バイトを2文字で表記したものに変換される。16進を示す 0x 等の
    接頭辞は含まない。
*/
void str2hexstr(
  const std::string&  src,
  std::string&        dst
) {
  static const char atbl[] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
  };
  dst.resize( src.size() * 2 );
  for( size_t i = 0; i < src.size(); i++ ) {
    dst[i*2  ] = atbl[ ((src[i] >> 4) & 0x0F) ]; // & 0x0F しないと算術シフトでアウトでした
    dst[i*2+1] = atbl[ (src[i] & 0x0F) ];
  }
}

int main() {
  std::string src = "Hello world!";
  std::string hex, dst;
  std::cout << src << std::endl;
  str2hexstr( src, hex );
  std::cout << hex << std::endl;
  hexstr2str( hex, dst );
  std::cout << dst << std::endl;
  return 0;
}

結果

Hello world!
48656C6C6F20776F726C6421
Hello world!
2022/01/14 追記: str2hexstr のコメントに書いたバグ修正をしました

2022年1月7日金曜日

JNI GetMethodID に指定するタイプ文字列 忘備録

JNI で Java method id を取得する GetMethodID 関数に渡す文字列のドキュメントが見つからないので、忘備録として記す事に

まず、Primitive Type の表

タイプ文字列Primitive Type
V void
Z boolean
B byte
C char
S short
I int
J long
F float
D double


クラスのタイプ文字列は、
 Ljava/lang/String;
というように L で始まり、class URIを書いて ; で閉じる

配列の場合は、
 [I
というように [ で始まり、タイプ文字列を書く
上記の場合 int [] に対応する

関数表記は
()V
([Ljava/lang/String;)I
というように、 ( 引数 ) 返り値 というように表記する
以上