使い方に苦戦したので供養として書きます🙏
初投稿なのでご容赦を!
記事を書いたきっかけ
Steamのゲームを遊んでいて、マウスの移動速度をゲーム内設定で変更出来ない事に気づきました。
「マウスの移動速度をゲームやる度に変えるのはめんどいな~」と思ったので、バッチファイルか何かでマウスを使わずに移動速度の変更&復元をしようと思い立ちました。
そうなったら、コマンドかあるいはWindowsを使っているのでWin32ApiだよねとなりSystemParametersInfoを見つけました。
そうして公式ドキュメントを見ながらコーディングしていたのですが…なぜかマウスの移動速度が変更出来ない。
結論から言えば使い方を間違えていたのですが、他の人も間違いそうだと思ったので記事にしようと思いました。
SystemParametersInfoとは?
WindowsOSで提供されているシステム全体のパラメータを取得/設定する関数です。
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
冒頭に書いたマウスの移動速度もパラメータに含まれています。
SystemParametersInfo関数の詳細
BOOL SystemParametersInfoA(
[in] UINT uiAction, // パラメータの種類
[in] UINT uiParam, // 「パラメータの依存するパラメータ」らしい。詳細は知りません
[in, out] PVOID pvParam, // 取得/設定する為のデータ指定
[in] UINT fWinIni // ユーザプロファイルの更新/通知をするかどうか
);
パラメータを取得したい時は、対応した変数のアドレスをpvParamに渡す訳ですね。
設定をしたい時も同様に…という訳にはいきませんでした。
マウスの移動速度の取得
int speed = 0;
BOOL ret = SystemParametersInfo(
SPI_GETMOUSESPEED,
0,
reinterpret_cast<PVOID>(&speed),
0
);
これでspeed変数にマウスの移動速度を取得出来ます。
Windowsのバージョンやハード構成によって変わるのかもしれませんが、筆者の環境のWindows10では以下の値が取得されていました。
設定>デバイス>マウス>カーソル速度
マウススピードの設定
次はいよいよマウススピードの変更です。
int speed = 20; // 最大スピードに変えたい!
BOOL ret = SystemParametersInfo(
SPI_SETMOUSESPEED,
0,
reinterpret_cast<PVOID>(&speed),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE // システム全体への設定項目の変更と通知を行う
);
おや、マウスの移動速度が変わりませんね?
なぜでしょう?
筆者はここから色々と「パラメータの種類が間違っていたのではないか?」とか「他の引数に何か指定する必要があるのではないか?」と試行錯誤をしていましたが、他の人に苦労して欲しくないので正解を記します。
マウススピードの設定(正解)
正解はこうです。
int speed = 20; // 最大スピードに変えたい!
BOOL ret = SystemParametersInfo(
SPI_SETMOUSESPEED,
0,
reinterpret_cast<PVOID>(speed),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE // システム全体への設定項目の変更と通知を行う
);
どこが変わったと思いますか?
第3引数のspeedの渡し方を変えました。
アドレスではなく変数の値そのものを渡しています。
ドキュメントのSETMOUSESPEEDにもしっかりと指定方式が書かれてあるのですが、私は間違いました…
現在のマウス速度を設定します。
pvParam パラメーターは、1 (最も遅い) ~ 20 (最速) の整数です。 既定値は 10 です。
この値は通常、マウス のコントロール パネル アプリケーションを使用して設定されます。
なぜ間違えたか?
pvParamの型であるPVOID1にはアドレスを渡すのだという先入観がありました。
しかし、実際はアドレスではなく値を渡すべきでした。
よく見ないままに「取得する時と同じように設定すれば良いんだ!」となったのが敗因です。
豆知識
あまり普通は使わない知識だと思うのですが、ポインタ型の変数はポインタのサイズに収まる範囲(64bitOSなら8バイト)ならスカラー型2の値を保持させる事が出来ます。
// 常識的な範囲でのポインタの使い方
int speed = 10;
void* ptr = (void*)&speed; // speedのアドレスを渡す
int value = *(int*)ptr; // speedのアドレスからintの値を取ってくる
// スカラー型を保持するポインタの使い方
int speed = 10;
void* ptr = (void*)speed; // speedの値(10)を渡す
int value = (int)ptr; // ptrに格納されたspeedの値(10)
アドレスとスカラー型両方をvoid*で受け取れてしまう訳ですね。
もはやポインタというよりか「何でも型」
こういった何でも取り扱えるポインタは、ライブラリで一つのインタフェースで色々な事をしたい時によく使われている印象です。
一言言い訳させてもらうなら、何でも取り扱えるポインタを使うならなるべく間違わないようにドキュメント書いて…!
結論
・設定と取得は同じ方法になるはずという先入観を捨てよう
・何でも取り扱えるポインタには注意しよう
南無🙏