はじめに
Unityプロジェクトを.NET Standardで設定した場合、Windows環境でシリアルポートを使用する際にSystem.IO.Portsクラスが問題を引き起こします。この問題の根本的な原因は、Unityが.NET Frameworkに設定されている必要があるためです。具体的には以下のエラーが発生します:
-
NotSupportedException: System.IO.Ports is currently only supported on Windows.
ただし、プロジェクト全体を.NET Frameworkに変更すると、マルチプラットフォーム対応に影響が出るため、Windows専用の解決策が必要になります。
実装した解決方法
私たちは、この問題を解決するためにWindows向けのC++ネイティブプラグインを開発しました。これにより、Unityを.NET Standardのまま維持しつつ、Windowsでシリアルポートを安全に制御できるようになります。

Visual Studioでの設定
Unityで使用可能なDLLを作成するために、Visual Studioでは以下の設定が必要です:
- プロジェクトテンプレートとして「Dynamic Link Library (DLL)」(動的リンクライブラリ)を使用
- プラットフォームターゲットをプロジェクトに合わせてx64またはx86に設定
- Unityから呼び出す関数を__declspec(dllexport)を使って明確に定義
主なメソッドの実装について
C++で実装したプラグインは、Windows APIを使用してシリアルポートを直接制御しています。主な関数は以下の通りです。
- OpenSerialPort:シリアルポートを開くためにCreateFileAを使用します。
-
__declspec(dllexport) bool OpenSerialPort(const char* portName)
-
{
-
hSerial = CreateFileA(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
-
if (hSerial == INVALID_HANDLE_VALUE)
-
{
-
return false;
-
}
-
serialPorts[portName] = hSerial;
-
return true;
-
}
- WriteSerialPort:開いたシリアルポートにデータを書き込みます。WriteFileを使用します。
-
__declspec(dllexport) bool WriteSerialPort(const char* data, DWORD dataLength)
-
{
-
DWORD bytesWritten;
-
if (!WriteFile(hSerial, data, dataLength, &bytesWritten, NULL))
-
{
-
return false;
-
}
-
return true;
-
}
- GetAvailableSerialPorts:利用可能なシリアルポートを取得するために、QueryDosDeviceWを使用します。
-
__declspec(dllexport) const wchar_t* GetAvailableSerialPorts()
-
{
-
static std::wstring portsList;
-
portsList.clear();
-
wchar_t devices[65536];
-
DWORD charCount = QueryDosDeviceW(nullptr, devices, 65536);
-
if (charCount == 0) {
-
portsList = L"NONE";
-
return portsList.c_str();
-
}
-
wchar_t* currentDevice = devices;
-
while (*currentDevice != L'\0') {
-
std::wstring deviceName(currentDevice);
-
if (deviceName.find(L"COM") == 0) {
-
if (!portsList.empty()) portsList += L";";
-
portsList += deviceName;
-
}
-
currentDevice += wcslen(currentDevice) + 1;
-
}
-
if (portsList.empty()) {
-
portsList = L"NONE";
-
}
-
return portsList.c_str();
-
}
UnityへのDLLの組み込み
生成したDLLをUnityプロジェクトに組み込む方法は以下の通りです:
1. VisualStudioでDLLのビルド:
Visual StudioでプロジェクトをReleaseモードでビルドします。
作成されたDLLは、プロジェクトフォルダ内のReleaseフォルダに保存されます(例:SerialPortPlugin.dll)。
2. UnityへのDLLの追加:
DLLファイルをコピーします。
Unityのプロジェクト内にAssets/Pluginsというフォルダを作成します。
Assets/Pluginsフォルダ内にDLLをペーストします。
3. DLLの設定確認:
Unity内でDLLファイルを選択し、ターゲットプラットフォームとアーキテクチャが正しく設定されていることを確認します。

4. UnityからのDLL利用方法:
Unityプロジェクト内でDLLを呼び出して利用するためには、[DllImport("SerialPortPlugin")]を用いて関数を宣言します。
Windows環境のみで動作するため、#if UNITY_STANDALONE_WINの使用が必須です。
以下は簡単なコード例です。
-
using System.Runtime.InteropServices;
-
using UnityEngine;
-
public class SerialPortManager : MonoBehaviour
-
{
-
#if UNITY_STANDALONE_WIN
-
[DllImport("SerialPortPlugin")]
-
private static extern bool OpenSerialPort(string portName);
-
[DllImport("SerialPortPlugin")]
-
private static extern bool WriteSerialPort(string data, uint dataLength);
-
[DllImport("SerialPortPlugin")]
-
private static extern System.IntPtr GetAvailableSerialPorts();
-
void Start()
-
{
-
string availablePorts = Marshal.PtrToStringUni(GetAvailableSerialPorts());
-
Debug.Log($"利用可能なポート: {availablePorts}");
-
string[] ports = availablePorts.Split(';');
-
if (ports.Length > 0 && ports[0] != "NONE")
-
{
-
if (OpenSerialPort(ports[0]))
-
{
-
Debug.Log($"ポート {ports[0]} を開きました。");
-
string message = "Hello, Serial!";
-
WriteSerialPort(message, (uint)message.Length);
-
}
-
else
-
{
-
Debug.LogError($"ポート {ports[0]} のオープンに失敗しました。");
-
}
-
}
-
else
-
{
-
Debug.LogError("利用可能なポートがありません。");
-
}
-
}
-
#endif
-
}
まとめ
この方法により、Unity(.Net Standard)プロジェクトのマルチプラットフォーム対応を維持したまま、Windows環境でのシリアルポート利用を実現しています。


上記ブログの内容に少しでも興味がありましたら、お気軽にご連絡ください。
弊社のエンジニアがフレンドリーに対応させていただきます。