フォームのプロジェクトを作ったら、ソリューションエクスプローラーからプロジェクトのプロパティを開いて、"アプリケーション"タブにある、対象のフレームワークを.Net Framework 2.0にする。"ビルド"タブにある、プラットフォームターゲットをAny CPUからx86に変更する。
いくつかの参照はリンクが切れているので、警告マークが出た参照は削除しておく。
Form1.csとProgram.csのusingも、リンク切れを削除しておく。
C:\Program Files (x86)\Microsoft Games\Microsoft Flight Simulator X SDK\SDK\Core Utilities Kit\SimConnect SDK\lib\managed\Microsoft.FlightSimulator.SimConnect.dllを参照に追加する。
Microsoft.FlightSimulator.SimConnectとSystem.Runtime.InteropServicesのusingを追加する。
ソースコードは長い(400行以上ある)ので、続きでどうぞ。
とりあえず、FSXの情報を取り出すサンプルが2種類と、FSXにコマンドを送るサンプルが1個と、オマケにもう1種類。
情報を取り出すのは、ギアハンドルが操作されたらFormに表示する、というサンプルと、常に位置情報を取り出して表示する、というサンプルの2種類。
コマンドを送るのは、フォームからの操作でギアハンドルの位置を変える操作を行う。
オマケは、SendInputでWin Alt PrintScreenを押す、という動作。
ちなみにプリントスクリーンだけで120行くらいあるので、SimConnectだけでは300行くらい。
キャプチャはGeForce ExperienceのAlt F1だと楽なんだけど、WinゲームバーだとWinキーが必要で、これはSendKeys.Sendでうまく送れないので、Win32APIを叩く必要がある。
SimConnectでできることを網羅した、というには程遠いが、ある程度のことはこのサンプルを改造するだけでできるはず。
値の名前とか単位とかはLockheed MartinのWebページが詳しいのでそちらを参照のこと。ただしFSXとP3Dでは使える要素に違いがあると思うので、そのあたりはMicrosoftのドキュメントも参照。
public partial class Form1 : Form
{
private const int WM_USER_SIMCONNECT = 0x0402;
private const int WS_EX_NOACTIVATE = 0x8000000;
private const string app_name = "SimConnect Tutorial";
readonly Enum SIMCONNECT_UNUSED = (dummy_enum)0xFFFF;
private SimConnect simconnect;
public Form1()
{
InitializeComponent();
Simconnect_OnRecvQuit(simconnect, null);
Load += delegate (Object sender, EventArgs e)
{
// デザイナでもTopMostをtrueにしておくこと
TopMost = true;
};
}
private void button1_Click(Object sender, EventArgs e)
{
if (simconnect == null)
{
try
{
simconnect = new SimConnect(app_name, Handle, WM_USER_SIMCONNECT, null, 0);
textBox1.Text = "Connecting...";
simconnect.OnRecvOpen += Simconnect_OnRecvOpen;
simconnect.OnRecvQuit += Simconnect_OnRecvQuit;
simconnect.OnRecvSimobjectData += Simconnect_OnRecvSimobjectData;
simconnect.OnRecvEvent += Simconnect_OnRecvEvent;
}
catch (COMException)
{
textBox1.Text = "Unable to Connect";
}
}
else
{
Simconnect_OnRecvQuit(simconnect, null);
}
}
private void Simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{
textBox1.Text = "Connected to FSX";
button1.Text = "Disconnect";
#region
// SIMCONNECT_DATA_REQUEST_FLAG.CHANGED 変化した時だけデータが来る
// SIMCONNECT_DATA_REQUEST_FLAG.DEFAULT 常にデータが来る
// SIMCONNECT_DATA_REQUEST_FLAG.TAGGED 不明
// SIMCONNECT_PERIOD.SIM_FRAME シミュレーション更新毎にデータが来る
// SIMCONNECT_PERIOD.VISUAL_FRAME 画面更新毎にデータが来る
// SIMCONNECT_PERIOD.SECOND 一秒ごとにデータが来る
// PERIODとDATA_REQUEST_FLAGの組み合わせでデータが来るタイミングが変わる
// VISUAL_FRAME, DEFAULTの場合は1フレームごとに常にデータが来る
// SIM_FRAME, CHANGEDの場合はデータが変化したときだけデータが来る
// SECOND, CHANGEDの場合は1秒毎に値をチェックし、変化していたらデータが来る(データチェックの1秒以内に複数回変化した場合、途中の値は得られない)
add_simobject(Request_id.gear_handle_position, new[] {
"gear handle position",
}, null, SIMCONNECT_PERIOD.SIM_FRAME, SIMCONNECT_DATA_REQUEST_FLAG.CHANGED);
add_simobject(Request_id.geo_position, new[] {
"GPS POSITION LAT",
"GPS POSITION LON",
"GPS POSITION ALT"
}, new[] {
"degrees",
"degrees",
"meters",
}, SIMCONNECT_PERIOD.SIM_FRAME, SIMCONNECT_DATA_REQUEST_FLAG.DEFAULT);
#endregion
#region
add_key_event(Group_id.user_input, Event_id.joystick_0_button_0);
add_key_event(Group_id.user_input, Event_id.joystick_0_button_4);
add_key_event(Group_id.user_input, Event_id.shift_and_tab);
#endregion
}
private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
{
button1.Text = "Connect";
textBox1.Text = "Disconnected";
simconnect?.Dispose();
simconnect = null;
}
private void Simconnect_OnRecvSimobjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data)
{
if (data.dwData.Length == 1)
{
Request_id request_id = (Request_id)data.dwRequestID;
if (false) { }
else if (data.dwData[0].GetType() == typeof(s_x1_Int32))
{
s_x1_Int32 value = (s_x1_Int32)data.dwData[0];
if (request_id == Request_id.gear_handle_position)
{
gear_handle_position_CheckBox.Checked = value.value1 != 0;
}
}
else if (data.dwData[0].GetType() == typeof(s_x3_double))
{
s_x3_double value = (s_x3_double)data.dwData[0];
if (request_id == Request_id.geo_position)
{
geo_position_TextBox.Text =
value.value1.ToString() + "\r\n" +
value.value2.ToString() + "\r\n" +
value.value3.ToString();
}
}
}
}
private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data)
{
// data.dwData:1 press
// data.dwData:0 release
// キーボード入力の場合は、キーを押している間は常に連続した0が送られる
Event_id event_id = (Event_id)data.uEventID;
Group_id group_id = (Group_id)data.uGroupID;
bool is_press = data.dwData != 0;
if (group_id == Group_id.user_input)
{
switch (event_id)
{
case Event_id.joystick_0_button_0:
if (is_press)
{
gear_handle_position_CheckBox_Click(gear_handle_position_CheckBox, null);
}
break;
case Event_id.joystick_0_button_4:
if (is_press)
{
send_win_alt_printscreen();
}
break;
case Event_id.shift_and_tab:
gear_handle_position_CheckBox_Click(gear_handle_position_CheckBox, null);
break;
}
}
}
private void add_simobject(Request_id request_id, string[] datum_name, string[] units_name, SIMCONNECT_PERIOD period, SIMCONNECT_DATA_REQUEST_FLAG request_flag)
{
if (units_name == null)
{
units_name = new string[datum_name.Length];
}
SIMCONNECT_DATATYPE[] types = null;
if (false) { }
else if (typeof(T) == typeof(s_x1_Int32))
{
types = new[]
{
SIMCONNECT_DATATYPE.INT32,
};
}
else if (typeof(T) == typeof(s_x3_double))
{
types = new[]
{
SIMCONNECT_DATATYPE.FLOAT64,
SIMCONNECT_DATATYPE.FLOAT64,
SIMCONNECT_DATATYPE.FLOAT64,
};
}
else
{
throw (new Exception());
}
if (types.Length != datum_name.Length || types.Length != units_name.Length)
{
throw (new Exception());
}
dummy_enum define_id = (dummy_enum)typeof(T).GetHashCode();
for (int i = 0; i < types.Length; i++)
{
simconnect.AddToDataDefinition(define_id, datum_name[i], units_name[i], types[i], 0, SimConnect.SIMCONNECT_UNUSED);
}
simconnect.RegisterDataDefineStruct(define_id);
simconnect.RequestDataOnSimObject(request_id, define_id, SimConnect.SIMCONNECT_OBJECT_ID_USER, period, request_flag, 0, 0, 0);
}
private void add_key_event(Group_id group_id, Event_id event_id)
{
simconnect.MapClientEventToSimEvent(event_id, null);
simconnect.AddClientEventToNotificationGroup(group_id, event_id, false);
simconnect.MapInputEventToClientEvent(group_id, event_id.ToString().Replace("_and_", "+").Replace('_', ':'), event_id, 0, SIMCONNECT_UNUSED, 0, false);
simconnect.SetInputGroupState(group_id, (uint)SIMCONNECT_STATE.ON);
}
protected override void DefWndProc(ref Message m)
{
if (m.Msg == WM_USER_SIMCONNECT)
{
simconnect?.ReceiveMessage();
}
else
{
base.DefWndProc(ref m);
}
}
// フォームをクリックしてもアクティブ化しないように
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
if (!DesignMode)
{
p.ExStyle |= (WS_EX_NOACTIVATE);
}
return (p);
}
}
private void gear_handle_position_CheckBox_Click(Object sender, EventArgs e)
{
if (simconnect != null)
{
s_x1_Int32 s = new s_x1_Int32() { value1 = !gear_handle_position_CheckBox.Checked ? 1 : 0 };
simconnect.SetDataOnSimObject((dummy_enum)s.GetType().GetHashCode(), SimConnect.SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG.DEFAULT, s);
}
}
public void send_win_alt_printscreen()
{
INPUT[] inp = new INPUT[6];
inp[0].type = INPUT_KEYBOARD;
inp[0].ki.wVk = (short)Keys.LWin;
inp[0].ki.wScan = (short)MapVirtualKey(inp[0].ki.wVk, 0);
inp[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYDOWN;
inp[0].ki.dwExtraInfo = 0;
inp[0].ki.time = 0;
inp[1].type = INPUT_KEYBOARD;
inp[1].ki.wVk = (short)0x12;
inp[1].ki.wScan = (short)MapVirtualKey(inp[1].ki.wVk, 0);
inp[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYDOWN;
inp[1].ki.dwExtraInfo = 0;
inp[1].ki.time = 0;
inp[2].type = INPUT_KEYBOARD;
inp[2].ki.wVk = (short)Keys.PrintScreen;
inp[2].ki.wScan = (short)MapVirtualKey(inp[2].ki.wVk, 0);
inp[2].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYDOWN;
inp[2].ki.dwExtraInfo = 0;
inp[2].ki.time = 0;
inp[3].type = INPUT_KEYBOARD;
inp[3].ki.wVk = (short)Keys.PrintScreen;
inp[3].ki.wScan = (short)MapVirtualKey(inp[3].ki.wVk, 0);
inp[3].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
inp[3].ki.dwExtraInfo = 0;
inp[3].ki.time = 0;
inp[4].type = INPUT_KEYBOARD;
inp[4].ki.wVk = (short)0x12;
inp[4].ki.wScan = (short)MapVirtualKey(inp[4].ki.wVk, 0);
inp[4].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
inp[4].ki.dwExtraInfo = 0;
inp[4].ki.time = 0;
inp[5].type = INPUT_KEYBOARD;
inp[5].ki.wVk = (short)Keys.LWin;
inp[5].ki.wScan = (short)MapVirtualKey(inp[5].ki.wVk, 0);
inp[5].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
inp[5].ki.dwExtraInfo = 0;
inp[5].ki.time = 0;
SendInput(inp.Length, ref inp[0], Marshal.SizeOf(inp[0]));
}
#region NonSoft - マウス操作やキーボード操作をシミュレーションするサンプル(C#.NET) (http://nonsoft.la.coocan.jp/SoftSample/CS.NET/SampleSendInput.html)
// マウスイベント(mouse_eventの引数と同様のデータ)
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
// キーボードイベント(keybd_eventの引数と同様のデータ)
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public short wVk;
public short wScan;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
// ハードウェアイベント
[StructLayout(LayoutKind.Sequential)]
private struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
};
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
};
[DllImport("user32.dll")]
private extern static void SendInput(int nInputs, ref INPUT pInputs, int cbsize);
// 仮想キーコードをスキャンコードに変換
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
private extern static int MapVirtualKey(int wCode, int wMapType);
private const int INPUT_MOUSE = 0; // マウスイベント
private const int INPUT_KEYBOARD = 1; // キーボードイベント
private const int INPUT_HARDWARE = 2; // ハードウェアイベント
private const int MOUSEEVENTF_MOVE = 0x1; // マウスを移動する
private const int MOUSEEVENTF_ABSOLUTE = 0x8000; // 絶対座標指定
private const int MOUSEEVENTF_LEFTDOWN = 0x2; // 左 ボタンを押す
private const int MOUSEEVENTF_LEFTUP = 0x4; // 左 ボタンを離す
private const int MOUSEEVENTF_RIGHTDOWN = 0x8; // 右 ボタンを押す
private const int MOUSEEVENTF_RIGHTUP = 0x10; // 右 ボタンを離す
private const int MOUSEEVENTF_MIDDLEDOWN = 0x20; // 中央ボタンを押す
private const int MOUSEEVENTF_MIDDLEUP = 0x40; // 中央ボタンを離す
private const int MOUSEEVENTF_WHEEL = 0x800; // ホイールを回転する
private const int WHEEL_DELTA = 120; // ホイール回転値
private const int KEYEVENTF_KEYDOWN = 0x0; // キーを押す
private const int KEYEVENTF_KEYUP = 0x2; // キーを離す
private const int KEYEVENTF_EXTENDEDKEY = 0x1; // 拡張コード
private const int VK_SHIFT = 0x10; // SHIFTキー
#endregion
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct s_x1_Int32
{
public Int32 value1;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct s_x3_double
{
public double value1;
public double value2;
public double value3;
}
enum Request_id
{
gear_handle_position,
geo_position,
}
enum Event_id
{
joystick_0_button_0,
joystick_0_button_4,
shift_and_tab,
}
enum Group_id
{
user_input,
}
enum dummy_enum { };
0 件のコメント:
コメントを投稿