|
■No85094 (カンガルー さん) に返信 >>SHFILEOPSTRUCT 構造体のアライメントは、本来は 1 バイトパッキングです。 >>提示 URL の VBA コードだと fFlags の後続メンバーの配置がずれますのでご注意ください。 > これってどういう意味ですか?
紹介した URL で解説していますが、ファイルコピーがユーザによってキャンセルされた場合、 SHFILEOPSTRUCT の fAnyOperationsAborted メンバーに 1 がセットされる仕様なのです。 (先のサンプルでは、キャンセルを判定するようなコードにはなっていないですけれどね)
32bit環境においては、SHFILEOPSTRUCT 構造体のメモリ配置は、 0x00-0x03 → HWND 型 hWnd 0x04-0x07 → UINT 型 wFunc 0x08-0x0B → PCZZTSTR 型 pFrom 0x0C-0x0F → PCZZTSTR 型 pTo 0x10-0x11 → FILEOP_FLAGS 型 fFlags 0x12-0x15 → BOOL 型 fAnyOperationsAborted 0x16-0x19 → LPVOID 型 hNameMappings 0x1A-0x1D → PCTSTR 型 lpszProgressTitle と並んでいます。
ところが VBA は、4バイト単位に再配置して呼び出す仕様です。
0x00、0x04、0x08、0x0C というのは 4 バイト単位の位置に並んでいるので問題ないのですが、 fAnyOperationsAborted が配置されているのは、「0x10-0x13 の後半」と「0x14-0x17 の前半」に またがるメモリ位置になっています。
そこで VBA は、API の呼び出し時に自動的に 2 バイト分調整し、 No85091 の構造体を 0x00-0x03 → Long 型 hWnd 0x04-0x07 → Long 型 wFunc 0x08-0x0B → String 型 pFrom 0x0C-0x0F → String 型 pTo 0x10-0x11 → Integer 型 fFlags 0x12-0x13 → 【2 バイトのパディング】 0x14-0x17 → Long 型 fAnyOperationsAborted 0x18-0x1B → Long 型 hNameMappings 0x1C-0x1F → String 型 lpszProgressTitle のメモリ配置に変換して渡すようになっています。
ところが、API がキャンセル結果を書き込むのは 0x12-0x15 のメモリ位置なので、結果を正しく受け取れなくなるわけです。
受け取れないだけならまだ良いですが、ポインタである lpszProgressTitle に文字列を渡してから呼び出せば、 クラッシュしてしまう可能性すらあるわけです。
一方 VB.NET の場合は、StructLayoutAttibute 属性を付与することで、 このアライメント調整を明示的に指定することができるようになっています。
'SHFileOperationA を呼び出す場合は、CharSet.Ansi をセットします。 'SHFileOperationW を呼び出す場合は、CharSet.Unicode をセットします。 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=1)> _ Public Structure SHFILEOPSTRUCT Public hwnd As IntPtr <MarshalAs(UnmanagedType.U4)> Public wFunc As Integer '(Enum FILEOP As Int32) <MarshalAs(UnmanagedType.LPWStr)> Public pFrom As String <MarshalAs(UnmanagedType.LPWStr)> Public pTo As String <MarshalAs(UnmanagedType.U2)> Public fFlags As Short '(Enum FILEOP_FLAGS As Int16) <MarshalAs(UnmanagedType.Bool)> Public fAnyOperationsAborted As Boolean Public hNameMappings As IntPtr <MarshalAs(UnmanagedType.LPWStr)> Public lpszProgressTitle As String End Structure
|