ffftpのメンテナンスを引き継ぎました。github:sayurin/ffftpで公開しています。メンテナンス方針としてはGUIを大きく変えることはしません。最新のVisual Studioを使用し、動作環境をWindows XP以降としています。この環境で利用できるC++17やWindows APIを使用してコードをリファクタリングしていきます。また外部ライブラリの利用を排し、Windowsが提供する機能を使用します。これによりセキュリティアップデートをWindows Updateに任せることができます。
不具合を見つけた際には github:sayurin/ffftp/issues まで報告いただけたら幸いです。
このブログでは、ffftpで使用しているC++機能やWindows APIなどを順次紹介していきたいと思います。
2018年4月11日水曜日
ffftpのメンテナンスを引き継ぎました
2016年8月21日日曜日
DLLで関数のexport
今更な話題かもしれませんが、あまり知られていないと思うので記事にしておきます。サンプルコードとして
#include <Windows.h> BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) { return TRUE; } extern "C" int __stdcall Add(int a, int b) { return a + b; }
でAdd関数をエクスポートする場合を考えます。
DLLで関数をエクスポートする方法の中でもっともよく使われる方法はモジュール定義(.def)ファイルです。
LIBRARY EXPORTS Add
で作成したファイルをリンカーオプション/DEFで指定します。
この方法の問題点は、定義が分かれてしまい、ソースコードからはどの関数がエクスポートされるか判別できないことです。
そこでソースコード内にエクスポートするかどうかを埋め込む方法が提供されています。__declspec(dllexport)を指定する方法です。
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b) { return a + b; }
__declspec(dllexport)をextern "C"の後~__stdcallの前の辺りに挿入します。
この方法の問題点は、x86においてエクスポートされる関数名が_Add@8(装飾名)となってしまうことです。これは呼び出し規約において呼び出し先関数が引数のスタックを復元するため、復元するサイズを呼び出し元に通知する必要があるからです。
そこでこの問題を解消する別の解決策を模索します。
- モジュール定義ファイルにおいてはエクスポートする関数名を明示することができます。
- リンカーオプション/EXPORTはモジュール定義ファイルとほぼ同様の書式で、ドキュメントにはありませんが同じ書式を使用してエクスポートする関数名を明示できます。
- #pragma commentを使用するとリンカーオプション/EXPORTをソースコード内で指定できます。
- 定義済みマクロ__FUNCTION__と__FUNCDNAME__を使用すると関数名と装飾名が得られます。
以上を総合すると
extern "C" int __stdcall Add(int a, int b) { #pragma comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) return a + b; }
これでモジュール定義ファイルを用いることなく、ソースコード内での記述で、関数をエクスポートすることができまし…たんですが、IntelliSenseがexpected a ')'と警告します。余計なお世話ですがこれも解消しましょう。
- #pragmaはマクロ内で使えないが__pragma()であればマクロで使える。
- IntelliSenseは__EDG__を定義するがコンパイラーは定義しないのでコード分岐できる。
以上を踏まえて最終的に
// ヘッダーなどに #ifdef __EDG__ #define DLLEXPORT #else #define DLLEXPORT __pragma(comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)) #endif // 関数内に extern "C" int __stdcall Add(int a, int b) { DLLEXPORT; return a + b; }
と書くことでスマートに記述できるようになりました。
2016年4月19日火曜日
Windowsの各種Timerの精度について
WindowsはいくつかのTimerを提供しています。
- Multimedia Timer
- Timer Queue
- Threadpool Timer
- 同期オブジェクトを使用するWaitable Timer
- メッセージループを使用するMessage Timer
- Message Timerは分解能が16msなので精度はよくない。
- Multimedia Timerは廃止済みでTimer Queueを推奨している。
- Multimedia TimerはtimeBeginPeriodを呼ぶことで精度が向上する。
2009年12月28日月曜日
Hyper-V with PowerShell
Hyper-V Serverを導入しました。これは機能制限されたWindows Server 2008 Server Coreみたいなものです。Server Coreと同じくローカルではコマンドプロンプトでの管理しかありません。 基本的にはfirewallを必要な分解放して、リモートから管理するわけですが、ここはあえてひねくれて、Hyper-V Server単独でどこまでできるかを試してみました。 デフォルトでは有効になっていませんが、
dism /online /enable-feature /featurename:NetFx2-ServerCore /featurename:MicrosoftWindowsPowerShellとすることでPowerShellが有効になります。 さてこのPowerShell + WMIを使ってHyper-Vは管理できます。 気合いで書いてみました。
# Switch作成 $vsm = gwmi -namespace root\virtualization Msvm_VirtualSwitchManagementService $vs = [wmi]$vsm.CreateSwitch("ExternalNetwork", "ExternalNetwork", 4096).CreatedVirtualSwitch $esp = [wmi]$vsm.CreateSwitchPort($vs, "ExternalNetwork_ExternalPort", "ExternalNetwork_ExternalPort").CreatedSwitchPort $isp = [wmi]$vsm.CreateSwitchPort($vs, "ExternalNetwork_InternalPort", "ExternalNetwork_InternalPort").CreatedSwitchPort $eep = gwmi -namespace root\virtualization Msvm_ExternalEthernetPort #NICが複数ある場合は注意 $vsm.SetupSwitch($esp, $isp, $eep, "ExternalNetwork", "ExternalNetwork") # VM作成、CPU、Memoryまで $vsm = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService $gsd = (([wmiclass]"root\virtualization:Msvm_CirtualSystemGlobalSettingData").CreateInstance() $gsd .ElementName = "web" $vm = $vsm.DefineVirtualSystem($gsd.GetText([Management.TextFormat]::WmiDtd20)).DefinedSystem $sd = $vm.getRelated("Msvm_VirtualSystemSettingData") | select -first 1 $psd = $sd.getRelated("Msvm_ProcessorSettingData") $psd.VirtualQuantity = 2 $msd = $sd.getRelated("Msvm_MemorySettingData") $msd.Limit = $msd.Reservation = $msd.VirtualQuantity = 1024 $vsm.ModifyVirtualSystemResources($vm, (@($psd, $msd) |% {$_.GetText([Management.TextFormat]::WmiDtd20)})) # NIC追加 $vsm = gwmi -namespace root\virtualization Msvm_VirtualSwitchManagementService $guid = [Guid]::NewGuid().ToString() $sp = $vsm.CreateSwitchPort($vs, $guid, $guid).CreatedSwitchPort $ac = (gwmi -namespace root\virtualization Msvm_AllocationCapabilities -filter "ResourceType=10 and ResourceSubType='Microsoft Synthetic Ethernet Port'").__Path.Replace('\', '\\') # 10=EthernetAdapter $esd = [wmi](gwmi -namespace root\virtualization Msvm_SettingsDefineCapabilities -filter "GroupComponent='$ac' and ValueRange=0").PartComponent $esd.Connection = $sp.__Path $esd.ElementName = "Synthetic Ethernet Port" $esd.VirtualSystemIdentifiers = [Guid]::NewGuid().ToString("B") $vsm = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService $vsm.AddVirtualSystemResources($vm, $esd.GetText([Management.TextFormat]::WmiDtd20)) # Disk追加 $im = gwmi -namespace root\virtualization Msvm_ImageManagementService $dir = (gwmi -namespace root\virtualization Msvm_VirtualSystemManagementServiceSettingData).DefaultVirtualHardDiskPath $im.CreateDynamicVirtualHardDisk( (Join-Path $dir "test.vhd"), 127GB ) $ide = ($sd.getRelated("MSVM_ResourceAllocationSettingData")| where {$_.ResourceSubType -eq "Microsoft Emulated IDE Controller" -and $_.Address -eq 0}) $ac = (gwmi -namespace root\virtualization Msvm_AllocationCapabilities -filter "ResourceType=22 and ResourceSubType='Microsoft Synthetic Disk Drive'").__Path.Replace('\', '\\') # 22=Disk $dsd = [wmi](gwmi -namespace root\virtualization Msvm_SettingsDefineCapabilities -filter "GroupComponent='$ac' and ValueRange=0").PartComponent $dsd.Parent = $ide.__Path $dsd.Address = 0 $vsm = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService $drive = [wmi]($vsm.AddVirtualSystemResources($vm, $dsd.GetText([Management.TextFormat]::WmiDtd20)).NewResources | select -first 1) $ac = (gwmi -namespace root\virtualization Msvm_AllocationCapabilities -filter "ResourceType=21 and ResourceSubType='Microsoft Virtual Hard Disk'").__Path.Replace('\', '\\') # 21=StorageExtent $dsd = [wmi](gwmi -namespace root\virtualization Msvm_SettingsDefineCapabilities -filter "GroupComponent='$ac' and ValueRange=0").PartComponent $dsd.Connection = Join-Path $dir "test.vhd" $dsd.Parent = $drive.__Path $vsm.AddVirtualSystemResources($vm, $dsd.GetText([Management.TextFormat]::WmiDtd20)) # DVD追加 $ac = (gwmi -namespace root\virtualization Msvm_AllocationCapabilities -filter "ResourceType=16 and ResourceSubType='Microsoft Synthetic DVD Drive'").__Path.Replace('\', '\\') # 16=DVDDrive $dsd = [wmi](gwmi -namespace root\virtualization Msvm_SettingsDefineCapabilities -filter "GroupComponent='$ac' and ValueRange=0").PartComponent $dsd.Parent = $ide.__Path $dsd.Address = 1 $drive = [wmi]($vsm.AddVirtualSystemResources($vm, $dsd.GetText([Management.TextFormat]::WmiDtd20)).NewResources | select -first 1) $ac = (gwmi -namespace root\virtualization Msvm_AllocationCapabilities -filter "ResourceType=21 and ResourceSubType='Microsoft Virtual CD/DVD Disk'").__Path.Replace('\', '\\') # 21=StorageExtent $dsd = [wmi](gwmi -namespace root\virtualization Msvm_SettingsDefineCapabilities -filter "GroupComponent='$ac' and ValueRange=0").PartComponent $dsd.Connection = (gwmi Win32_CdromDrive -filter "Drive='D:'").DeviceID # D:ドライブ $dsd.Parent = $drive.__Path $vsm.AddVirtualSystemResources($vm, $dsd.GetText([Management.TextFormat]::WmiDtd20))これぐらいでVMが作成できるはず。 ここまで書いてようやく気付きました。VMの画面が見れません。VMにOSがインストールできないわけです…。もちろんP2Vとかすればできますが…。 結局、長いものに巻かれろということで、私もリモート管理することにしました。 というわけで本当に動作確認できていません。
2009年8月13日木曜日
Windows 7 導入
Windows 7を導入しました。Hyper-Vが使いたかったのでWindows Server 2008 R2の方がよかったのですが、Hyper-Vを有効にするとスリープが使えないのは痛いです。サーバーOSだから当たり前ですね。
以降、愚痴です。
今更ながらIRQの競合が起きてる模様?
video card、onboard sound、onboard nicがIRQを共有しているらしく、Windows 7起動時にsoundやnicが認識されない現象が。必ずvide cardは認識される辺り怪しいです。
XP / Vistaでは発生しなかった現象です。またWindows Server 2008 R2でも発生します。
2009年3月22日日曜日
自作ツールのWindows 7対応
対応に向けて調査を始めました。途中で飽きたらそのまま放置になりますが…。
まず今のツールはUIを設計する気がないため設定項目は全てiniを使い、タスクトレイに置いたアイコンのメニューから最低限の操作ができるようになっています。
Windows 7ではタスクトレイはデフォルトで非表示なので困ってしまうため抜本的な見直しを行うことにしました。
Windows Live Messengerがtaskbarを使いこなしているためこれを参考に調査を進めます。まずWindows 7環境ではこんな感じ。
タスクトレイにはアイコンが出なくなります。メインウィンドウの他にもう一つウィンドウが存在するような。そしてタスクバーを右クリックするとこんな感じ。
プログラムから好きなメニューを登録できます。ちなみにこのメニューはスタートメニューにも表示されてこんな感じ。
さてメインウィンドウを閉じるとこんな感じ。
やはり何かいるようです。spyで見ると

とんでもない座標に隠しWindowがいました。これを参考にしてみようと思います。
2009年3月13日金曜日
Windows Server 2008 R2 build 7048
そろそろ2008 R2に環境を移そうとしています。ベータテストと、自作アプリのWindows 7対応のために。
Windows 7ではタスクバーが新しくなり、右下の通知領域がデフォルトで非表示になってしまうので、
アプリケーションの設計を見直す必要が。
で、どうせならHyper-Vとか使いたいので2008 R2です。
build 7048ですがところどころ不安定でした。まだ翻訳がおかしいですし、機能の追加の後の再起動に
失敗したりVirtual Clone Driveのインストールの直後はシャットダウンできなかったり。
ただ、RadeonのバグいドライバでWindows Vistaだとblue screenなのが、2008 R2だとドライバの
再起動を繰り返しつつも耐えてました。(耐えてる間に正常に再起動がかけれた。)
まぁRadeonがわるいんですが。
2008年10月21日火曜日
Data Entity FrameworkとSQL Server Compact 3.5 SP1
Visual Studio 2008 SP1の新機能Data Entity Frameworkを使ってみたくなりました。ついでにずっと気になっていたSQL Server Compact 3.5 SP1と組み合わせて試してみることにしました。
SQL Server Compactはプロセス内で動作する軽量なSQLエンジンです。SQLiteみたいなものと言えばわかりやすいでしょうか。
Wizardに従って作っていきいざ実行すると…
ハンドルされていない例外: System.ArgumentException: 指定されたストア プロバイダが構成内に見つからないか、無効です。何を言っているのかわからない例外になります。
---> System.ArgumentException: 要求された .Net Framework データ プロバイダが見つかりません。これは、インストールされていない可能性があります。
「指定されたストア プロバイダ」とか言われてもWizardの生成したデフォルトそのままだし。「データ プロバイダが見つかりません」と言われてもサーバエクスプローラでもWizardでも表示できてるし…。
いろいろググりましたが見つかりません。ならばまずSQL Server Compact 3.5 SP1を単体で動かしてみましょう…
ハンドルされていない例外: System.DllNotFoundException: DLL 'sqlceme35.dll' を読み込めません: 指定されたモジュールが見つかりません。 (HRESULT からの例外: 0x8007007E)見つからないってどういうこと?
ならばプライベート ファイル ベースの配置をしてみましょう…
ハンドルされていない例外: System.BadImageFormatException: 間違ったフォーマットのプログラムを読み込もうとしました。 (HRESULT からの例外: 0x8007000B)…やっとっわかった気がする。
Microsoft SQL Server Compact 3.5 ReadmeやSQL Server Compact 3.5 と Visual Studioに色々書いてありました。
- SQL Server Compact 3.5 SP1 の 64 ビット リリースは、Microsoft ダウンロード センターからダウンロードできます (Web からの提供のみ)。
- Visual Studio と SQL Server Compact 3.5 で 64 ビット開発を行う場合は、[コンパイラの詳細設定] の [ターゲット CPU] オプションを明示的に [x86] に設定する必要があります。
ええ、私はVista x64で開発してますよ…。Visual Studio自身が32bitで動作していることを忘れてましたよ。普通、開発環境なんだから64bit版もインストール済みと思うよっ!
