フォームのプロジェクトを作ったら、ソリューションエクスプローラーからプロジェクトのプロパティを開いて、"アプリケーション"タブにある、対象のフレームワークを.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 件のコメント:
コメントを投稿