質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

10791閲覧

TkinterでDnDを実装するには(64bit Window版)

IwaoWatanabe

総合スコア51

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2016/09/29 11:28

TkinterでエクスプローラからDnDでファイルを受け取るアプリを作成してみようと思いました。

参考サイトの内容を元にコードを作成してみましたが、残念なことに 64bit Windows 10 ではうまく動作しません。

Python+TkでD&D -MasaHeroの日記より
http://d.hatena.ne.jp/MasaHero/20111201/p1

ctypesを使って、WindowsのDDLを直接操作しています。
32bit Windows7 にて、python 2.7 と 3.4で動作を確認しています。

これを64bit Windowsでも動作させるにはどのように修正すればよいでしょうか?

python

1# -*- coding: UTF-8 -*- 2# このスクリプトは OSのDDLを利用しているため、Windowsでのみ動作します。 3 4try: import Tkinter as tk 5except ImportError: import tkinter as tk 6import ctypes, sys 7from ctypes import c_long, WINFUNCTYPE 8from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM 9 10WM_DROPFILES = 0x0233 11GWL_WNDPROC = -4 12FS_ENCODING = sys.getfilesystemencoding() 13 14DragAcceptFiles = ctypes.windll.shell32.DragAcceptFiles 15DragQueryFile = ctypes.windll.shell32.DragQueryFile 16DragFinish = ctypes.windll.shell32.DragFinish 17CallWindowProc = ctypes.windll.user32.CallWindowProcW 18GetWindowLong = ctypes.windll.user32.GetWindowLongW 19SetWindowLong = ctypes.windll.user32.SetWindowLongW 20 21 22class TkApp(tk.Frame): 23 dnd_interval = 600 24 25 def __init__(self, *args, **kargs): 26 tk.Frame.__init__(self, *args, **kargs) 27 self.createwidget() 28 self.dropnames = [] 29 win = args[0] if args else self._root() 30 win = self.tw 31 32 def drop_check(): 33 if self.dropnames: 34 fns = self.dropnames 35 self.dropnames = [] 36 self.dnd_notify(fns) 37 win.after(self.dnd_interval, drop_check) 38 39 40 @WINFUNCTYPE(c_long, HWND, UINT, WPARAM, LPARAM) 41 def replace_win_proc(hwnd, msg, wp, lp): 42 u"""D&D用のコールバック 43 ファイルのドラッグアンドドロップイベント(WM_DROPFILES)を検出して、 44 ドロップされたファイル名を保持する。 45 ここでウィンドウ(tk)を使用するとハングアップするのでデータ保存だけ行う。 46 """ 47 if msg == WM_DROPFILES: 48 nf = DragQueryFile(wp, -1, None, None) 49 buf = ctypes.c_buffer(260) 50 fns = [ buf.value.decode(FS_ENCODING) for nn in range(nf) \ 51 if DragQueryFile(wp, nn , buf, ctypes.sizeof(buf)) ] 52 DragFinish(wp) 53 self.dropnames.extend(fns) 54 print("%s dnd_notify: %s" % (self, hwnd)) 55 56 return CallWindowProc(self.org_proc, hwnd, msg, wp, lp) 57 58 59 def dnd_setup(): 60 "Windowsのイベント処理のフックを定義する" 61 hwnd = win.winfo_id() 62 DragAcceptFiles(hwnd, True) 63 self.org_proc = GetWindowLong(hwnd, GWL_WNDPROC) 64 self.win_proc = replace_win_proc 65 SetWindowLong(hwnd, GWL_WNDPROC, self.win_proc) 66 win.after_idle(drop_check) 67 print("%s dnd_setup: %s,%s" % (self, hwnd, self.org_proc)) 68 69 self.pack(fill='both', expand=1) 70 win.after_idle(dnd_setup) 71 72 73 def createwidget(self): 74 tw = tk.Text(self, width=30,height=16) 75 tw.pack(fill='both', expand=1) 76 self.tw = tw 77 78 79 def dnd_notify(self, filenames): 80 for nn in filenames: 81 self.tw.insert('end',"%s\n" % nn) 82 83 84if __name__ == "__main__": 85 a = TkApp() 86 if 0: TkApp(tk.Toplevel()) # 複数ウィンドウに対応 87 a.mainloop() 88

実行するとウィンドウが表示されます。そちらにエクスプローラからファイルやディレクトリをドロップすると、そのファイルのパスを表示(追記)します。

UNCでもうまく動作しているようです。

64bit Windows 10 では GetWindowLong の返り値が0になっているので、その辺りを調整すればよいのでは?と思うのですが..

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

その後の調査で、64bit版のpythonでもエクスプローラからDnDが動作するようになりました。
まずDLLからAPIのエンドポイントを取り出すところの記述を次のように修正しました。

python

1DragQueryFile = ctypes.windll.shell32.DragQueryFile 2DragQueryFile.argtypes = [ ctypes.c_void_p, UINT, ctypes.c_void_p, UINT ] 3DragFinish = ctypes.windll.shell32.DragFinish 4DragFinish.argtypes = [ ctypes.c_void_p ] 5CallWindowProc = ctypes.windll.user32.CallWindowProcW 6CallWindowProc.argtypes = [ ctypes.c_void_p, HWND , UINT, WPARAM, LPARAM ] 7try: SetWindowLong = ctypes.windll.user32.SetWindowLongPtrW 8except AttributeError: SetWindowLong = ctypes.windll.user32.SetWindowLongW

64bitの場合は、SetWindowLong の APIのエンドポイントが異なるのと
ポインタを操作する部分を 明示的に ctypes.c_void_p で示してやる必要がありました。

またこの利用ケースの場合、SetWindowLong はその返り値が GetWindowLong と同じであることが分かったので
GetWindowLong 関連の記述を除去して

python

1self.org_proc = SetWindowLong(hwnd, GWL_WNDPROC, self.win_proc)

と置き換えました。

また、python 3 ではプロトタイプで UINTとしているところに Noneを渡すとエラーとなりました。
そのため DragQueryFile の呼び出しを次のように None から 0 に変えました。

python

1nf = DragQueryFile(wp, -1, None, 0)

以上の修正で動作が確認できました。

投稿2016/11/05 02:43

IwaoWatanabe

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問