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

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

ただいまの
回答率

90.11%

CythonでC++のkeybd_eventが使えない(LinkError等が発生)

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 560

 前提・実現したいこと

Python3にて、C++のkeybd_event関数を高速に使おうとしています。
そこでCythonを用いて、keybd_eventを使おうといろいろ試しているのですがうまくいきません。

(keybd_eventを使うだけならpyautoguiで事足りますが、1度のキー出力に0.1秒ほどかかってしまいます。
コードをさらに高速化させたいため、Cythonを使おうとしています)

 発生している問題・エラーメッセージ

(key.pyxおよびsetup.pyは下に載っています)
key.pyxを実行すると、以下のようなエラーが出ます。

Traceback (most recent call last):

  File "<ipython-input-1-414ff3ef1721>", line 1, in <module>
    runfile('D:/(ユーザ名)/proglam/Pythpn/cython/key.pyx', wdir='D:/(ユーザ名)/proglam/Pythpn/cython')

  File "C:\Users\(ユーザ名)\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 666, in runfile
    ipython_shell.run_cell_magic('cython', '', f.read())

  File "C:\Users\(ユーザ名)\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2167, in run_cell_magic
    result = fn(magic_arg_s, cell)

  File "<decorator-gen-130>", line 2, in cython

  File "C:\Users\(ユーザ名)\Anaconda3\lib\site-packages\IPython\core\magic.py", line 187, in <lambda>
    call = lambda f, *a, **k: f(*a, **k)

  File "C:\Users\(ユーザ名)\Anaconda3\lib\site-packages\Cython\Build\IpythonMagic.py", line 329, in cython
    quiet=args.quiet)

  File "C:\Users\(ユーザ名)\Anaconda3\lib\site-packages\Cython\Build\IpythonMagic.py", line 439, in _build_extension
    build_extension.run()

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\command\build_ext.py", line 339, in run
    self.build_extensions()

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\command\build_ext.py", line 448, in build_extensions
    self._build_extensions_serial()

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\command\build_ext.py", line 473, in _build_extensions_serial
    self.build_extension(ext)

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\command\build_ext.py", line 558, in build_extension
    target_lang=language)

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\ccompiler.py", line 717, in link_shared_object
    extra_preargs, extra_postargs, build_temp, target_lang)

  File "C:\Users\(ユーザ名)\Anaconda3\lib\distutils\_msvccompiler.py", line 481, in link
    raise LinkError(msg)

LinkError: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\x86_amd64\\link.exe' failed with exit status 1120

また、コマンドプロンプト上で python setup.py build_ext --inplace と入力して、
setup.pyをビルドしようとすると、以下のようなエラーが出ます。

running build_ext
cythoning key.pyx to key.c
building 'key' extension
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c /
nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\Users\(ユーザ名)\Anaconda3\include -IC:\Users\mas
a\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\IN
CLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt" "-IC
:\Program Files (x86)\Windows Kits\8.1\include\shared" "-IC:\Program Files (x86)
\Windows Kits\8.1\include\um" "-IC:\Program Files (x86)\Windows Kits\8.1\include
\winrt" /Tckey.c /Fobuild\temp.win-amd64-3.5\Release\key.obj
key.c
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\link.exe /n
ologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C
:\Users\(ユーザ名)\Anaconda3\libs /LIBPATH:C:\Users\(ユーザ名)\Anaconda3\PCbuild\amd64 "/LIB
PATH:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\amd64" "/LIBPATH
:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\ucrt\x64" "/LIBPATH:C:\
Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x64" /EXPORT:PyInit_key buil
d\temp.win-amd64-3.5\Release\key.obj /OUT:D:\(ユーザ名)\proglam\Pythpn\cython\key.
cp35-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.l
ib
key.obj : warning LNK4197: export 'PyInit_key' specified multiple times; using f
irst specification
   Creating library build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.lib and
object build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.exp
key.obj : error LNK2001: unresolved external symbol __imp_keybd_event
D:\(ユーザ名)\proglam\Pythpn\cython\key.cp35-win_amd64.pyd : fatal error LNK1120:
1 unresolved externals
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\
x86_amd64\\link.exe' failed with exit status 1120

 該当のソースコード

key.pyxとsetup.pyが同一のフォルダに入っています。
key.pyx

cdef extern from "windows.h":
    void keybd_event(int a,int b,int c,int d)

def f(a,b,c,d):
    keybd_event(a,b,c,d)
    return a

setup.py(Cythonドキュメントを参考にしました)

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("key", ["key.pyx"])]

setup(
  name = 'key world app',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

 試したこと

① key.pyxの内容を以下のように変える。

cdef extern from "windows.h":
    void keybd_event(int a,int b,int c,int d)

cdef int f(int a,int b,int c,int d):
    keybd_event(a,b,c,d)
    return a

これにより、上記のエラーはどちらも起こらなくなり、後者のコマンドでは正常に
key.cp35-win_amd64.pyd
が出力されました。

そうして、import keyでkeyをインポートすることはできたのですが、
関数fを使おうとすると、以下のように「関数fが定義されていない」とエラーが出て使えませんでした。
AttributeError: module 'key' has no attribute 'f'


② key.pyxの内容を以下のように変える。
(①の2つ目のcdefcpdefに変えただけです)

cdef extern from "windows.h":
    void keybd_event(int a,int b,int c,int d)

cpdef int f(int a,int b,int c,int d):
    keybd_event(a,b,c,d)
    return a

しかし、これを行っても、最初と同じようなエラーが出てしまい解決できませんでした。
1つ目のエラーは同じですが、2つ目のエラーは少し変化がありました。

cythoning key.pyx to key.c
building 'key' extension
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe /c
nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\Users\(ユーザ名)\Anaconda3\include -IC:\Users\ma
a\Anaconda3\include "-IC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\I
CLUDE" "-IC:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt" "-I
:\Program Files (x86)\Windows Kits\8.1\include\shared" "-IC:\Program Files (x86
\Windows Kits\8.1\include\um" "-IC:\Program Files (x86)\Windows Kits\8.1\includ
\winrt" /Tckey.c /Fobuild\temp.win-amd64-3.5\Release\key.obj
key.c
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\link.exe /
ologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:
:\Users\(ユーザ名)\Anaconda3\libs /LIBPATH:C:\Users\(ユーザ名)\Anaconda3\PCbuild\amd64 "/LI
PATH:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\amd64" "/LIBPAT
:C:\Program Files (x86)\Windows Kits\10\lib\10.0.10240.0\ucrt\x64" "/LIBPATH:C:
Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x64" /EXPORT:PyInit_key bui
d\temp.win-amd64-3.5\Release\key.obj /OUT:D:\(ユーザ名)\proglam\Pythpn\cython\key
cp35-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.
ib
key.obj : warning LNK4197: export 'PyInit_key' specified multiple times; using
irst specification
   Creating library build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.lib and
object build\temp.win-amd64-3.5\Release\key.cp35-win_amd64.exp
key.obj : error LNK2001: unresolved external symbol __imp_keybd_event
D:\(ユーザ名)\proglam\Pythpn\cython\key.cp35-win_amd64.pyd : fatal error LNK1120:
1 unresolved externals
error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\
x86_amd64\\link.exe' failed with exit status 1120

③ そもそもCythonやCモジュールの導入に失敗しているのではないかと思い、
別のコードを入力してみて、正常に動くか確かめてみました。

sin.pyx

cdef extern from "math.h":
    double sin(double)

def f(x):
    return sin(x)

setup2.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("sin", ["sin.pyx"])]

setup(
  name = 'sin world app',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

結果、sin.pyxは正常に実行できました。
また、python setup.py build_ext --inplaceを入力してこれをビルドすることもできました。
そして、正常に関数fを使うこともできました。

よって、Cythonやmath.hに問題はないとみられます。


④ C:\\Program Files (x86)\\Microsoft Visual Studio 14.0 内には、windows.hが入っていません。
windows.hは、C:\Program Files (x86)\Windows Kits\8.1\Include\umの中に入っていました。

そこで、Microsoft Visual Studio 14.0のフォルダ内の
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include中にWindows.hを入れて、
問題のコードと②のコードをそれぞれ実行してみましたが、変化がなく、これまでと同じように
エラーが発生しました。

(ちなみに、Windows.hは単体では動きませんが、エラーメッセージに変化がなかったことから、
結局この方法には効果がなかったと考えています)


私が試したのはここまでですが、どれも解決に至りませんでした。
C++コンパイラの再インストールなどをする、という手もありますが、
環境の再整備が大変そうなのでやっていません。

 補足情報(FW/ツールのバージョンなど)

使用したものは以下の通りです。

  • OS
    Windows8.1 (64-bit)

  • プログラム等
    Anaconda3 5.2.0 (64-bit)
    Python 3.5.6
    Cython 0.28.5
    Spyder 3.3.1 (使用エディタ)

  • C++コンパイラ
    Microsoft Visual Studio 14.0 (x86)
    Microsoft Visual C++ 2017 Redistributable (x64) - 14.15.26706
    (Cython内で使われているのはVisual Studioの方です)

状況の説明は以上です。
長文になってしまい、すみません。
何か分かることがあれば、ぜひご教授ください。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

自己解決しました

この質問をしたあとから今まで、2か月余り回答がありませんでした。
また、自分でこれをさらに調べてみたところ、以下のことがわかりました。

  • Cythonでkeybd_event(Windows.h)を使う方法はよくわからなかった
    少なくとも私にはできません。というか、Cython等は計算を高速化するもので、こういうモジュール?を使うには向いていないのかな……?

  • そもそも、C++のkeybd_event自体が遅い
    C++でも、1回のキーイベントに0.1秒くらいかかってしまうので、Cythonを使ってもあまり効果が出なさそうです。

ということで、解決になっていないかもしれませんが、
軽量化をしない(素直にPython内のpyautoguiを使う)」ということにしました。

2019/3/18 追記

ctypes.windll.user32.keybd_event

を使ったところ1000倍速くなりました

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る