- PR -

Shell IDList Arrayについて

1
投稿者投稿内容
笊頭刹那
ベテラン
会議室デビュー日: 2005/10/17
投稿数: 55
お住まい・勤務地: オーストラリア
投稿日時: 2005-12-02 06:19
最近C#に限界を感じ始めている刹那です、こんにちは。

C言語を知らないとC#に変換ができないので勉強しなきゃとは思っているのですが日本語の本が手に入らないので後回し後回しとなっています(汗。

さて、いまShell IDList Arrayについて調べています、ドラッグ&ドロップでファイルへのリンクを作りたいのです。

で、いままで調べたことで分かったことは

Shell IDList ArrayとはCIDAとITEMIDLISTからなるデータである。
[CIDA][ITEMIDLIST0][ITEMIDLIST1][ITEMIDLIST2]...[ITEMIDLISTn]

c#に渡されたものはMemoryStreamとなっている、よって上記のデータをbyte配列かしMemoryStreamとしてDataObjectに渡してやればできるはず。

CIDAの構造体は
uint cidl
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1)]
uint[] aoffset

ITEMIDLISTの取得方法は
・IShellFolder.PareseDisplayName
・SHParseDisplayName
・ILCreateFromPath(ドキュメント無し)
・自作でITEMIDを作ってまわす。
のどれか。

ITEMIDLIST0には親フォルダのPIDLsがそれ以外のITEMIDLISTには親フォルダからの相対的な位置のPIDLsが含まれている。

aoffset[0]の値は親フォルダITEMIDLISTへのポインタそれ以外はその他のもののポインタ、もしaoffset[0]が0ならばデスクトップへのポインタ
(だからaoffset[0]を0としてITEMIDLISTに絶対的な位置を入れてやっても動く気がするんだけど、そこが違うのかほかが違うのか、動かない、というかどんな状況であれ動いていたら質問しにきませんが…汗)

その他いろいろ副産物など。


そこでいくつか質問なのですがaoffsetとはなんでしょうか?
MSDNには
コード:
typedef struct _IDA{
    UINT cidl;
    UINT aoffset[1];
}CIDA,*LPIDA


となっているので
aoffset = new uint[1];
aoffset[0] = ...
かと思ったのですが、英文を読んでみると
-------------------------------------------------
An array of offsets, relative to the begining of this structure. The array contains cidl + 1 elements. The first element of aoffset contains an offset to the fully qualified PIDL of a parent folder. If this PIDL is empty, the parent folder is the desktop. Each of the remaining elements of the array contains an offset to one of the PIDLs to be transferred. All of these PIDLs are relative to the PIDL of the parent folder.
--------------------------------------------------
と、あくまで配列として書いてあります。
ですので
--CIDA--
uint cidl
uint[1] aoffset (要素数1のuint配列)
--------
[CIDA][aoffset1][aoffset2]...[aoffsetn]

と[CIDA]データの後に独自でuint配列を書き込むべきなのか

--CIDA--
uint cidl
uint[n] aoffset(要素数nのuin配列)
--------
[CIDA]
と[CIDA]データのaoffset内にすべて書き込むべきなのか分かりません、C言語での
uint[1] aoffset
の意味を教えていただきたいです。


また、相対的な位置を取得するとのことなので
コード:
// Create parent folder pidl
IntPtr ipShellFolder = IntPtr.Zero;
IntPtr ipParentFolderPIDL = IntPtr.Zero;
ipParentFolderPIDL = Win32Api.ILCreateFromPath(Path.GetDirectoryName(files[0]));

// SHBindToParent
IntPtr ptrParent = IntPtr.Zero;
ShellApi.SHGetDesktopFolder(out ptrParent);
//			IntPtr pidlRelative = IntPtr.Zero;
//			ShellLib.ShellApi.SHBindToParent(
//				ipParentFolderPIDL,
//				ShellLib.ShellGUIDs.IID_IShellFolder,
//				out ptrParent,
//				ref pidlRelative);
// Create IShellFolder object
Type shellFolderType = ShellLib.ShellFunctions.GetShellFolderType();
Object obj =Marshal.GetTypedObjectForIUnknown(
	ptrParent,shellFolderType);
ShellLib.IShellFolder ishellParent = (ShellLib.IShellFolder)obj;

ishellParent.BindToObject(ipParentFolderPIDL,IntPtr.Zero,
	ShellGUIDs.IID_IShellFolder,out ptrParent);

obj =Marshal.GetTypedObjectForIUnknown(
	ptrParent,shellFolderType);
ishellParent = (ShellLib.IShellFolder)obj;
---------------------------------------------------------------------
IShellFolderのBindToObjectの宣言
// Retrieves an IShellFolder object for a subfolder.
// Return value: error code, if any
[PreserveSig]
Int32 BindToObject( 
	IntPtr pidl,				// Address of an ITEMIDLIST structure (PIDL) that identifies the subfolder.
	IntPtr pbc,					// Optional address of an IBindCtx interface on a bind context object to be 
	/*object pbc,*/
	// used during this operation.
	Guid riid,					// Identifier of the interface to return. 
	out IntPtr ppv);			// Address that receives the interface pointer.


としたのですが、BindToObjectの場所で「オブジェクト参照がオブジェクトインスタンスに設定されていません」と起こられます、多分IntPtr.Zeroの位置でオブジェクトをつくろうとがんばっているんだと思うのですが、オプショナル引数なのでnullを渡したいのですが…ちなみに object pbc としてnullを渡すと動いたように見えて全然別なところで同じエラーが発生しました。

以上の理由からSHBindToObjectを使ってみたのですが、SHGetPathFromIDListで作成された相対位置のITEMIDLISTを見てみても空白です(ただ、相対位置なので空白表示されるのかとも思いましたが、依然としてShell IDList Arrayとして機能していません。)

数日間悩んでいろいろやってみているのですが、デスクトップへのショートカットしか作成できませんでした(ITEMIDLISTに0を渡すとそうなった)
ほかは
「ショートカットを作成できませんでした、ディスクがいっぱい出ないかうんたらかんたら」
「DDEなんたらをどうたらこうたらしてOLE1がうんたらかんたら」
「シーン」(ドラッグしたりコピペしても何も起きない)
はてはExplorerが落ちたりなど、さっぱり進みません。

答えをいただくのはいままでの努力が散ってしまう気がするのでヒントをいただけないでしょうか、とりあえずいまやっていることが間違っているのかいないのか…など。

一応全コード載せておきます(長いですorz)加えて試行錯誤のごみがあちらこちらに散らばっています(これでも何度も書き直した後の結果なので綺麗なほうなのですが…汗)
コード:
// ※ 長すぎるのでIShellFolderやShellApiなどの物については省略させていただきました。
[StructLayout(LayoutKind.Sequential)]
public struct CIDA
{
	/// <summary>
	///  Number of PIDLs that are being transferred, not counting the parent folder.
	/// </summary>
	public uint cidl;

	/// <summary>
	/// An array of offsets, relative to the beginning of this structure. The array contains
	/// cidl+1 elements. The first element of aoffset contains an offset to the fully-qualified
	/// PIDL of a parent foloder. If this PIDL is empty, the parent folder is the desktop.
	/// Each of the remaining elements of the array contains an offset to one of the PIDLs to be 
	/// transferred. ALL of these PIDLs are relative to the PIDL of the parent folder.
	/// </summary>
	[MarshalAs(UnmanagedType.ByValArray, SizeConst=1)]
	public uint[] aoffset;
}
public unsafe static object[] CreateITEMIDLISTFromFileNames(string[] files)
{
	// Create parent folder pidl
	IntPtr ipShellFolder = IntPtr.Zero;
	IntPtr ipParentFolderPIDL = IntPtr.Zero;
	ipParentFolderPIDL = Win32Api.ILCreateFromPath(Path.GetDirectoryName(files[0]));

	// SHBindToParent
	IntPtr ptrParent = IntPtr.Zero;
	ShellApi.SHGetDesktopFolder(out ptrParent);
//			IntPtr pidlRelative = IntPtr.Zero;
//			ShellLib.ShellApi.SHBindToParent(
//				ipParentFolderPIDL,
//				ShellLib.ShellGUIDs.IID_IShellFolder,
//				out ptrParent,
//				ref pidlRelative);
	// Create IShellFolder object
	Type shellFolderType = ShellLib.ShellFunctions.GetShellFolderType();
	Object obj =Marshal.GetTypedObjectForIUnknown(
		ptrParent,shellFolderType);
	ShellLib.IShellFolder ishellParent = (ShellLib.IShellFolder)obj;

	ishellParent.BindToObject(ipParentFolderPIDL,IntPtr.Zero,
		ShellGUIDs.IID_IShellFolder,out ptrParent);

	obj =Marshal.GetTypedObjectForIUnknown(
		ptrParent,shellFolderType);
	ishellParent = (ShellLib.IShellFolder)obj;


	// Copy buffer
	object[] apidl = new object[files.Length+1];
	byte[] buffer = new byte[Win32Api.ILGetSize(ipParentFolderPIDL)];
	Marshal.Copy(ipParentFolderPIDL,buffer,0,buffer.Length);
	apidl[0] = buffer;
	for(int i=0;i<files.Length;i++)
	{
		string file = files[i];
		// Create relative file pidl to parent folder
		uint pchEaten = 0,pdwAttributes = 0;
		IntPtr ipRelativePIDL = IntPtr.Zero;
		ishellParent.ParseDisplayName(IntPtr.Zero,IntPtr.Zero,
			file,ref pchEaten,out ipRelativePIDL,ref pdwAttributes);
		// Test
		System.Text.StringBuilder sb = new System.Text.StringBuilder(1024);
	
		// Get ITEMIDLIST buffer
		buffer = new byte[Win32Api.ILGetSize(ipRelativePIDL)];
		Marshal.Copy(ipRelativePIDL,buffer,0,buffer.Length);
		// Free ITEMIDLIST
		Win32Api.ILFree(ipRelativePIDL);
		// Copy buffer
		apidl[i+1] = buffer;
	}
	// Free the ITEMLIST
	Win32Api.ILFree(ipParentFolderPIDL);
	// Free the IShellFolder object
	Marshal.ReleaseComObject(ishellParent);
	return apidl;
}

public static byte[] CreateShellIDListArray(string[] files)
{
	int sizeOfCIDA,sizeOfRemainingOffsets,sizeOfITEMIDLIST;

	IntPtr ipGlobal = IntPtr.Zero;
	object[] ITEMIDLISTs = null;

	// Get ITEMIDLIST from files
	ITEMIDLISTs = CreateITEMIDLISTFromFileNames(files);

	// Calculate size
	sizeOfCIDA = Marshal.SizeOf(typeof(CIDA));
	sizeOfRemainingOffsets = ITEMIDLISTs.Length * IntPtr.Size;
	sizeOfITEMIDLIST = 0;
	for(int i=0;i<ITEMIDLISTs.Length;i++)
	{
		byte[] b = (byte[])ITEMIDLISTs[i];
		sizeOfITEMIDLIST+=b.Length;
	}

	// Alloc and get pointer of global memory
	ipGlobal = Marshal.AllocHGlobal(sizeOfCIDA + sizeOfRemainingOffsets + sizeOfITEMIDLIST);

	// Build structure in global memory
	CIDA cida = new CIDA();
	cida.cidl = (uint)(ITEMIDLISTs.Length+1);
	cida.aoffset = new uint[cida.cidl];
	cida.aoffset[0] = (uint)(sizeOfCIDA + sizeOfRemainingOffsets);
	int offset = 0;
	for(int i=0;i<ITEMIDLISTs.Length; i++)
	{
		cida.aoffset[i]=(uint)(sizeOfCIDA + sizeOfRemainingOffsets + offset);
		offset +=((byte[])ITEMIDLISTs[i]).Length;
	}
	cida.aoffset[cida.cidl-1] = 0;

	Marshal.StructureToPtr(cida,ipGlobal,true);

//			int offset = ((byte[])ITEMIDLISTs[0]).Length;
//			int[] remainingOffsets = new int[ITEMIDLISTs.Length];
//			for(int i=0;i<ITEMIDLISTs.Length-1; i++)
//			{
//				remainingOffsets[i]=(int)(sizeOfCIDA + sizeOfRemainingOffsets + offset);
//				offset +=((byte[])ITEMIDLISTs[i+1]).Length;
//			}
//			remainingOffsets[ITEMIDLISTs.Length-1]=0;
//
//			Marshal.Copy(remainingOffsets,0,(IntPtr)(ipGlobal.ToInt32() + sizeOfCIDA),remainingOffsets.Length);

	// Copy to ITEMIDLISTs
	offset = ipGlobal.ToInt32() + sizeOfCIDA + sizeOfRemainingOffsets;
	for(int i=0;i<ITEMIDLISTs.Length;i++)
	{
		byte[] b = (byte[])ITEMIDLISTs[i];
		Marshal.Copy(b,0,(IntPtr)offset,b.Length);
		offset+=b.Length;
	}

	byte[] result = new byte[sizeOfCIDA + sizeOfRemainingOffsets + sizeOfITEMIDLIST];
	Marshal.Copy(ipGlobal,result,0,result.Length);
	return result;
}


_________________
seed of weed
自作したソフトの公開および刹那が難しい・めんどくさい・覚えられないと思った特殊なC#Tipsを公開しています。
笊頭刹那
ベテラン
会議室デビュー日: 2005/10/17
投稿数: 55
お住まい・勤務地: オーストラリア
投稿日時: 2005-12-02 08:55
できました、ドラッグされたデータをバイナリで解析しながらひとつずつ微調整したらできました。

週末忙しいのでそれが終わったら同じ問題で悩んでいる方のために解説自分のサイトに作ります、解説作ったら書き込みますね。

では、お騒がせしました
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-02 14:38
こんにちは、笊頭刹那さん。お久しぶりです。

引用:

笊頭刹那さんの書き込み (2005-12-02 08:55) より:

週末忙しいのでそれが終わったら同じ問題で悩んでいる方のために解説自分のサイトに作ります、解説作ったら書き込みますね。



Shell系の機能って、COMインターフェイスで提供されていることが多いんですよね。
Shell Interfaces

私はC++からCOMインターフェイスを操作するのは、結構得意としているほうなんですが
C#からCOMインターフェイスを使ったことは、まだありません。
なので、笊頭刹那さんの解説記事を期待して待っております。


まあ、多分この辺を勉強すればいいんでしょうね?
Runtime Callable Wrapper(RCW)
まだ具体的に使ったことないので、ピンときていないのですが。

COM Callable Wrapper(CCW)の方は、
実際に利用してみて、こんなもんなんだなと理解しているんですけれども。
笊頭刹那
ベテラン
会議室デビュー日: 2005/10/17
投稿数: 55
お住まい・勤務地: オーストラリア
投稿日時: 2005-12-07 13:37
http://seedofweed.rental.allinoneserver.net/notebook/csharp0017.htm

解説(?)つくりました、残念ですがCOMまったく使ってません(笑。

C#からCOM使う方法は

http://seedofweed.rental.allinoneserver.net/notebook/csharp0009.htm

ここで使用しています、はっきり言ってかなり複雑で僕は一人でこれを組めといわれたら無理です(汗。

では。
_________________
seed of weed
自作したソフトの公開および刹那が難しい・めんどくさい・覚えられないと思った特殊なC#Tipsを公開しています。
1

スキルアップ/キャリアアップ(JOB@IT)