C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

別スレッドでDoDragDropが動かない

[トピック内 17 記事 (1 - 17 表示)]  << 0 >>

■94173 / inTopicNo.1)  別スレッドでDoDragDropが動かない
  
□投稿者/ Tom (1回)-(2020/03/23(Mon) 18:54:59)

分類:[C#] 

開発環境:VisualStudio 2017
使用言語:C#

ファイル名が表示されているリストボックスからエクスプローラーにD&Dすると、ファイルコピーする機能をもつプログラムを作っています。
しかしファイルコピーしている間はプログラムが操作受け付けません。
そのため、D&D部分を別スレッドにしようとしましたがうまくいきません。

極端な話、テスト用の下記のコードで
 DoDragDrop("あいう", DragDropEffects.All);
メモ帳にD&Dしたら「あいう」って表示するだけものが別スレッドにすると動きません。

なお、
動作のトリガーはMouseMoveイベントです。
別スレッドでの呼び出し自体は問題なく動作しています。

賢者の方、アドバイスを頂けませんでしょうか?
引用返信 編集キー/
■94175 / inTopicNo.2)  Re[1]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (94回)-(2020/03/23(Mon) 20:11:18)
No94173 (Tom さん) に返信
> しかしファイルコピーしている間はプログラムが操作受け付けません。
> そのため、D&D部分を別スレッドにしようとしましたがうまくいきません。

UI にかかわる部分を別スレッドにするといろいろ不都合が出ます。
ファイルコピーする部分を別スレッドにしてはいかがでしょう。

引用返信 編集キー/
■94176 / inTopicNo.3)  Re[2]: 別スレッドでDoDragDropが動かない
□投稿者/ Hongliang (973回)-(2020/03/23(Mon) 21:38:23)
ファイルコピーする部分はExplorerの担当なので…。

https://docs.microsoft.com/ja-jp/windows/win32/shell/datascenarios#dragging-and-dropping-shell-objects-asynchronously
非同期D&Dのソース側を実装するには、D&D対象のIDataObjectにIDataObjectAsyncCapabilityを追加で実装させる必要があります。
ちょっとNuGetやwebを漁りましたが、ライブラリとして公開されているのは見つからず、実装サンプルを示してくれているのも以下のページぐらいしか見つかりませんでした。しかもC++/CLI。
https://dev.activebasic.com/egtra/2014/09/14/678/
引用返信 編集キー/
■94178 / inTopicNo.4)  Re[3]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (95回)-(2020/03/24(Tue) 02:12:03)
2020/03/24(Tue) 02:16:41 編集(投稿者)
No94176 (Hongliang さん) に返信
> ファイルコピーする部分はExplorerの担当なので…。
あっ!なるほど。

> https://dev.activebasic.com/egtra/2014/09/14/678/

こんな感じのインターフェイスを定義して DataObject を継承したクラスに実装すればいけるようです。

using System;
using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;

[ComImport()]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("3D8B0590-F691-11d2-8EA9-006097DF5BD4")]
public interface IDataObjectAsyncCapability
{
    void SetAsyncMode([MarshalAs(UnmanagedType.Bool)] bool fDoOpAsync);

    [return: MarshalAs(UnmanagedType.Bool)]
    bool GetAsyncMode();

    void StartOperation(ComTypes.IBindCtx pbcReserved);

    [return: MarshalAs(UnmanagedType.Bool)]
    bool InOperation();

    void EndOperation(int hResult, ComTypes.IBindCtx pbcReserved, [MarshalAs(UnmanagedType.U4)] uint dwEffects);
}

引用返信 編集キー/
■94187 / inTopicNo.5)  Re[4]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (2回)-(2020/03/24(Tue) 12:24:54)
KOZさま
Hongliangさま

アドバイスありがとうございます。
内容が高度なのでちょっと理解できるまで、とりあえずお礼のお返事だけ先にいたします。

理解できなかったらまた質問いたしますので、お暇な時にでも更にアドバイスいただけると幸いです。
引用返信 編集キー/
■94189 / inTopicNo.6)  Re[5]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (96回)-(2020/03/24(Tue) 14:04:33)
2020/03/24(Tue) 14:05:12 編集(投稿者)
No94187 (Tom さん) に返信

こんなクラスを作って

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;

[ComVisible(true)]
class AsyncDataObject : DataObject, IDataObjectAsyncCapability
{
    bool _InOperation = false;
    bool _AsyncMode = true;

    public void SetAsyncMode([MarshalAs(UnmanagedType.Bool)] bool fDoOpAsync) {
        _AsyncMode = fDoOpAsync;
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    public bool GetAsyncMode() {
        return _AsyncMode;
    }

    public void StartOperation(IBindCtx pbcReserved) {
        _InOperation = true;
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    public bool InOperation() {
        return _InOperation;
    }

    public void EndOperation(int hResult, IBindCtx pbcReserved, [MarshalAs(UnmanagedType.U4)] uint dwEffects) {
        _InOperation = false;
    }
}

このように使います。

using System;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main() {
        var f = new Form();
        f.MouseDown += F_MouseDown;
        Application.Run(f);
    }

    private static void F_MouseDown(object sender, MouseEventArgs e) {
        var data = new AsyncDataObject();
        var filePaths = new System.Collections.Specialized.StringCollection();
        filePaths.Add(@"E:\work\bigdata.txt");
        data.SetFileDropList(filePaths);
        ((Form)sender).DoDragDrop(data, DragDropEffects.All);
    }
}

C++/CLI のサンプル(https://gist.github.com/egtra/7a3ca22888b970a364f1)は
ファイル実体がない場合に、保存するファイルの内容を MemoryStream に格納して渡す方法です。

この場合、DoDragDrop メソッドが終了しても転送(コピーもしくは移動)は続くので MemoryStream を破棄してはいけません。
転送が完了したら EndOperation が呼ばれるので、その時に破棄すると良いでしょう。

引用返信 編集キー/
■94209 / inTopicNo.7)  Re[6]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (3回)-(2020/03/25(Wed) 12:02:56)
KOZさま

詳細なコードまで作っていただき、ありがとうございます!

しかしながらわたくしのスキルが追いついておらず、恥を忍んで追加の質問をいたします。

>class AsyncDataObject : DataObject, IDataObjectAsyncCapability
の「IDataObjectAsyncCapability」の部分が

エラー CS0246 型または名前空間の名前 'IDataObjectAsyncCapability' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)。

とエラーになります。
IDataObjectAsyncCapabilityが定義されているusingやアセンブリ参照を探したのですが、どうしてもわかりませんでした。
わたくし、そもそもなにかを勘違いしているような気がしてならないのですが、アドバイスをいただけませんでしょうか?



引用返信 編集キー/
■94211 / inTopicNo.8)  Re[7]: 別スレッドでDoDragDropが動かない
□投稿者/ 魔界の仮面弁士 (2637回)-(2020/03/25(Wed) 12:21:12)
No94209 (Tom さん) に返信
> IDataObjectAsyncCapabilityが定義されているusingやアセンブリ参照を探したのですが、どうしてもわかりませんでした。

No94178 に書かれていますよ。
引用返信 編集キー/
■94217 / inTopicNo.9)  Re[8]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (5回)-(2020/03/25(Wed) 16:29:05)
魔界の仮面弁士さま
KOZさま

No94178 に書かれていますよ。
大変失礼しました!
No94178の内容を追加したところ、無事動作し(Formの任意の個所からエクスプローラにドロップしたらコピー開始され)ました。

しかし、KOZさまのサンプルはFormのMouseDownイベントがトリガーとなっていたのですが、わたしくのプログラムではListViewからのMouseMoveがトリガーなのです。
そのため、No94189 のF_MouseDown()の内容をListViewのMouseMoveイベント関数に配置し実行したところ、
 ((Form)sender).DoDragDrop(data, DragDropEffects.All);
の行で下記のエクセプションが発生しました。
 System.InvalidCastException
 HResult=0x80004002
 Message=型 'System.Windows.Forms.ListView' のオブジェクトを型 'System.Windows.Forms.Form' にキャストできません。

おそらくclass AsyncDataObjectかpublic interface IDataObjectAsyncCapabilityの中でListViewでも使用できる指定が必要と思ったのですが、お手上げ状態になりました。

再三の質問で恐縮ではありますが、なにとぞお知恵をお貸しください。
引用返信 編集キー/
■94219 / inTopicNo.10)  Re[9]: 別スレッドでDoDragDropが動かない
□投稿者/ 魔界の仮面弁士 (2640回)-(2020/03/25(Wed) 17:00:56)
No94217 (Tom さん) に返信
> そのため、No94189 のF_MouseDown()の内容をListViewのMouseMoveイベント関数に配置し実行したところ、
>  ((Form)sender).DoDragDrop(data, DragDropEffects.All);
> の行で下記のエクセプションが発生しました。


((Control)sender).DoDragDrop(data, DragDropEffects.All);
にしてみてください。


イベント引数 sender は、「そのイベントが発生しているオブジェクト」を指します。

button1_Click なら、sender は button1 と同義になりますし、
form1_MouseDown なら、sender は form1 と同義になりますし、
listView1_MouseMove なら、sender は listView1 と同義になります。


なので、
 listView1.DoDragDrop(data, DragDropEffects.All);
とか
 ((ListView)sender).DoDragDrop(data, DragDropEffects.All);
でも同じ結果が得られると思います。
引用返信 編集キー/
■94220 / inTopicNo.11)  Re[9]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (100回)-(2020/03/25(Wed) 17:07:36)
No94217 (Tom さん) に返信
>  ((Form)sender).DoDragDrop(data, DragDropEffects.All);
> の行で下記のエクセプションが発生しました。
>  System.InvalidCastException
>  HResult=0x80004002
>  Message=型 'System.Windows.Forms.ListView' のオブジェクトを型 'System.Windows.Forms.Form' にキャストできません。

イベントの Sender には、イベントが発生したコントロールのインスタンスが入っています。
ListView においてはイベントの Sender には ListView が入ってくるので Form にキャストできません。

キャストって何?ってことであれば「C# キャスト」でネット検索するとわかると思います。
引用返信 編集キー/
■94222 / inTopicNo.12)  Re[10]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (6回)-(2020/03/25(Wed) 18:18:24)
魔界の仮面弁士さま
KOZさま

動きました!
わたくしだけでは絶対に実現できませんでした。
本当にありがとうございました!

ただ、ファイルコピー中に更にファイルコピーをしようとすると反応しませんでした。
これができれば当初考えていた機能を100%完璧なのですが、ファイルコピーを複数同時進行させることは可能でしょうか?

完全におんぶにだっこ状態でお知恵をお借りしていますが、なにとぞご教授くださると幸いです。
引用返信 編集キー/
■94224 / inTopicNo.13)  Re[11]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (101回)-(2020/03/26(Thu) 08:10:32)
No94222 (Tom さん) に返信
> ただ、ファイルコピー中に更にファイルコピーをしようとすると反応しませんでした。

プログラムを整理して、現象が発生する最低限のコードを提示してください。
(整理している最中にバグをみつけてしまうかもしれませんが)

AsyncDataObject は改変していなければ提示する必要はありません。
引用返信 編集キー/
■94248 / inTopicNo.14)  Re[12]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (7回)-(2020/03/26(Thu) 19:25:06)
KOZさま
> プログラムを整理して、現象が発生する最低限のコードを提示してください。
> (整理している最中にバグをみつけてしまうかもしれませんが)
>
> AsyncDataObject は改変していなければ提示する必要はありません。


現在のコードはこんな感じであります。
**************************************************
using いろいろ
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace hogehoge
{
public partial class Form1
{
void Form1_Load(object sender, EventArgs e)
{
//ListViewFはファイル名一覧のリストボックス
ListViewF.MouseMove += new MouseEventHandler(ListViewF_MouseMove);
}
void ListViewF_MouseMove(object sender, MouseEventArgs e)
{
実際のコードはここにマウスドラッグ判定の遊び計測の判定が入っています
(そもそもMouseMoveじゃなくてMousuDragならそんな判定いらなかったかも…)

//ここからがコピー(or移動)開始処理
string[] FlistSelect = FlistSelectList1();//選択ファイルのフルパスリスト一覧
DataObject dataObj = new DataObject(DataFormats.FileDrop, FlistSelect);//フルパスリスト一覧からDataObjectを作成する
var data = new AsyncDataObject();
var filePaths = new System.Collections.Specialized.StringCollection();
((ListView)sender).DoDragDrop(dataObj, DragDropEffects.All);
}
}


[ComImport()]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("3D8B0590-F691-11d2-8EA9-006097DF5BD4")]
public interface IDataObjectAsyncCapability
{
教えていただいたコードそのまま
}
[ComVisible(true)]
class AsyncDataObject : DataObject, IDataObjectAsyncCapability
{
教えていただいたコードそのまま
}
}
**************************************************
よろしくお願いいたします。
引用返信 編集キー/
■94249 / inTopicNo.15)  Re[13]: 別スレッドでDoDragDropが動かない
□投稿者/ KOZ (102回)-(2020/03/26(Thu) 20:21:36)
No94248 (Tom さん) に返信
>             //ここからがコピー(or移動)開始処理
>             string[] FlistSelect = FlistSelectList1();//選択ファイルのフルパスリスト一覧
>             DataObject dataObj = new DataObject(DataFormats.FileDrop, FlistSelect);//フルパスリスト一覧からDataObjectを作成する
>             var data = new AsyncDataObject();
>             var filePaths = new System.Collections.Specialized.StringCollection();
>             ((ListView)sender).DoDragDrop(dataObj, DragDropEffects.All);

AsyncDataObject を使っていないのですが・・・

引用返信 編集キー/
■94250 / inTopicNo.16)  Re[14]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (8回)-(2020/03/26(Thu) 23:33:58)
KOZさま

すみません、なにか勘違いしたのか前のコードを張ってしまいました。
正しくは下記のコードです。
    //ここからがコピー(or移動)開始処理
    var data = new AsyncDataObject();
    var filePaths = new System.Collections.Specialized.StringCollection();
    string[] FlistSelect = FlistSelectList1();
    for (int cnt = 0; cnt < FlistSelect.Length; ++cnt)
    {
        filePaths.Add(FlistSelect[cnt]);
    }
    data.SetFileDropList(filePaths);
    ((ListView)sender).DoDragDrop(data, DragDropEffects.All);

引用返信 編集キー/
■94251 / inTopicNo.17)  Re[15]: 別スレッドでDoDragDropが動かない
□投稿者/ Tom (9回)-(2020/03/26(Thu) 23:40:45)
KOZさま

なんかコードをいじった時におかしくしていたのが原因だったようです。
1つ前のコードでコピー中に更にコピー追加できました!

お騒がせして申し訳ありませんでした。
そして、最後までアドバイスしてくださり、本当にありがとうございました。

またハマってしまったときにはお知恵をお借りするかもしれませんが、よろしくお願いいたします。

つきましては当件は解決済みとさせていただきます。
ありがとうございました。

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ