- ブラウザのURL欄やリンクからのドラッグ&ドロップを受け取り、URLとページタイトルを取得する。
ドロップ時に送られてくるデータ形式
とりあえず、現状使っているブラウザのURL欄からのドラッグ&ドロップを受け取って、取得可能なデータ形式をログ出力してみた(2017.08現在)。
- Firefox
* FileContents * FileGroupDescriptor * FileGroupDescriptorW * UniformResourceLocator * UniformResourceLocatorW * System.String * UnicodeText * Text application/x-moz-custom-clipdata text/html text/x-moz-url HTML Format DragContext DragImageBits
- Chrome
* FileContents * FileGroupDescriptorW * UniformResourceLocator * UniformResourceLocatorW * System.String * UnicodeText * Text text/x-moz-url DragContext DragImageBits
- IE11
* FileContents * FileGroupDescriptor * FileGroupDescriptorW * UniformResourceLocator * UniformResourceLocatorW * System.String * UnicodeText * Text Shell IDList Array IESiteModeToUrl
* を付けたデータは、少なくとも上記3ブラウザでは共通。ブラウザからドラッグされたURLデータであるか否かは UniformResourceLocator
か UniformResourceLocatorW
の有無で判別するのが定石らしい。URLは Text
で簡単に取得できる。
// ドラッグ物がフォーム上に入った際のイベント
private void Form_DragEnter( object sender, DragEventArgs e )
{
// ブラウザからのURLデータなら受け入れる
if ( e.Data.GetDataPresent( "UniformResourceLocator" ) ||
e.Data.GetDataPresent( "UniformResourceLocatorW" ) )
{ e.Effect = DragDropEffects.Link; }
else
{ e.Effect = DragDropEffects.None; }
}
// ドロップ時イベント
private void Form_DragDrop( object sender, DragEventArgs e )
{
if ( e.Data.GetDataPresent( "UniformResourceLocator" ) ||
e.Data.GetDataPresent( "UniformResourceLocatorW" ) )
{
// 上記形式で GetData すると変換が面倒なので、Text で取ると楽
if ( e.Data.GetDataPresent( DataFormats.Text ) )
{ var url = e.Data.GetData( DataFormats.Text ) as string; }
}
}
問題はページタイトルで、上記データの内容をそれぞれ実際に GetData()
で取得してみたところ、ページタイトルを含むのは FileGroupDescriptor(W)
と text/x-moz-url
くらい。
形式 | C#の型 | 内容 | タイトル |
---|---|---|---|
FileContents |
MemoryStream | ショートカットファイル の内容(ANSI文字列) |
× |
FileGroupDescriptor |
MemoryStream | ショートカットファイル名 ( FILEGROUPDESCRIPTOR ) |
含む |
FileGroupDescriptorW |
MemoryStream | ショートカットファイル名 ( FILEGROUPDESCRIPTOR ) |
含む |
UniformResourceLocator |
MemoryStream | URL (ANSI文字列) | × |
UniformResourceLocatorW |
MemoryStream | URL (UTF16文字列) | × |
System.String |
string | URL | × |
UnicodeText |
string | URL | × |
Text |
string | URL | × |
HTML Format |
string | リンク部分のHTML情報 | △ |
text/html |
MemoryStream | リンク部分のHTML (UTF16文字列) |
△ |
text/x-moz-url |
MemoryStream | Firefox独自のURL情報 (UTF16文字列) |
含む |
ちなみに、ブラウザのURL欄からドラッグした場合には表示中のページタイトルが取得できるが、ページ内のリンクをドラッグした場合は、そのリンク部分の文言が取得できるだけで、リンク先のページタイトルを取得できる訳ではない。
表の△マークについては、ページ内のリンクをドラッグした場合にはリンク部分の文言が取得できるが、URL欄からドラッグした場合にはページタイトルは取得できず、使えない。
FileGroupDescriptor(W)
からページタイトル取得
URLのドラッグデータには、インターネットショートカットを作るために、ショートカットファイル(.url形式)の内容そのもの FileContents
と、ファイル名情報 FileGroupDescriptor(W)
が添付されてる。
ショートカットファイルの内容にはページタイトルは含まれてないが、ファイル名がほぼページタイトルなのでそれを取得する。ただし ファイル名である以上、ファイル名として使えない文字は省かれてるし、長さ制限もあるので、完全なページタイトルではない。
FileGroupDescriptor(W)
の内容は Win32API の FILEGROUPDESCRIPTOR
構造体だが、C# では直接アクセスできないので、構造体内のファイル名の部分、先頭から76Byte目より後を直接取得する。正直キモイが仕方なし。
ファイル名は FileGroupDescriptor
なら デフォルトのANSI文字コード(※UTF-8ではない)、FileGroupDescriptorW
なら UTF16 で格納されている。ANSI文字コードでは自国語以外のページタイトルを正常に扱えないので、FileGroupDescriptorW
を優先的に取得する。
// DragEventArgs.Data からURLとページタイトル取得
public bool
GetUrlInfo(
IDataObject data,
out string url,
out string title)
{
url = title = null;
if ( data.GetDataPresent( DataFormats.Text ) )
{ url = data.GetData( DataFormats.Text ) as string; }
if ( url == null )
{ return false; }
// Unicode の FileGroupDescriptorW から優先的に取得
return (
GetTitle( data, out title, "FileGroupDescriptorW", Encoding.Unicode ) ||
GetTitle( data, out title, "FileGroupDescriptor", Encoding.Default ) );
}
private bool
GetTitle(
IDataObject data,
out string title,
string dataFormat,
Encoding encoding)
{
title = null;
// FILEGROUPDESCRIPTOR が MemoryStream で格納されてる
if ( !data.GetDataPresent( dataFormat ) )
{ return false; }
var stream = data.GetData( dataFormat ) as System.IO.MemoryStream;
if ( (stream == null) || !stream.CanSeek )
{ return false; }
// FILEDESCRIPTOR[0].cFileName まで移動
var offsetFileName = 76;
if ( stream.Length < offsetFileName )
{ return false; }
stream.Seek( offsetFileName, IO.SeekOrigin.Current );
// とりあえず全部文字列化
var buffer = new byte[stream.Length - offsetFileName];
stream.Read( buffer, 0, buffer.Length );
title = encoding.GetString( buffer );
// 末尾のヌル文字と拡張子を排除
var term = title.Length;
for ( int i = 0; i < title.Length; i++ )
{
if ( title[i] == '\0' )
{
term = Math.Min( term, i );
break;
}
else if ( title[i] == '.' )
{ term = i; }
}
title = title.Substring( 0, term );
return true;
}
text/x-moz-url
からページタイトル取得
Firefox からのURLのドラッグデータには、独自のデータ形式 text/x-moz-url
が含まれている。内容は UTF16 の文字列で、1行目がURL、2行目がページタイトル。
FileGroupDescriptor(W)
とは違って完全なページタイトルを取得できるし、なんでか Chrome でも採用されてるので、可能ならこちらからページタイトルを取得した方が正確。
// DragEventArgs.Data からURLとページタイトル取得
public bool
GetUrlInfo(
IDataObject data,
out string url,
out string title)
{
url = title = null;
if ( data.GetDataPresent( DataFormats.Text ) )
{ url = data.GetData( DataFormats.Text ) as string; }
if ( url == null )
{ return false; }
if ( data.GetDataPresent( "text/x-moz-url" ) )
{
var stream = data.GetData( "text/x-moz-url" ) as IO.MemoryStream;
if ( stream != null )
{
// UTF16で文字列化
var buffer = new byte[stream.Length];
stream.Read( buffer, 0, buffer.Length );
var text = Encoding.Unicode.GetString( buffer );
// 末尾にヌル文字が付いてるので行分割のついでに省く
var lines = text.Split( new char[] { '\n', '\r', '\0' },
StringSplitOptions.RemoveEmptyEntries );
// 一応、URLの整合チェック
if ( (lines.Length >= 2) && (lines[0] == url) )
{
title = lines[1];
return true;
}
}
}
return false;
}