需求:通過Unity啓動外部的一個winform程序,傳參數進行初始化。 程序啓動後,untiy通過winApi給啓動的程序發消息。
unity調用HoneyWellSDK顯示視頻,關閉的時候藏至後臺。(後續發現,honeywell能通過Rtsp的方式,直接調用視頻)
Untiy端:
Process myprocess;//攝像頭程序進程
public const int WM_COPYDATA = 0x004A; //進程發送信息標識,之前用別的不行,用這個winform才接收到消息
public string HoneyWellWindowName = "HoneyWellVideoMonitor";
/// <summary>
/// 創建新進程
/// </summary>
/// <param name="fileName"></param>
/// <param name="args"></param>
private void CreateNewProcess(string args,string fileName="")
{
if(string.IsNullOrEmpty(fileName))
{
if (IsSDKInit) return;
fileName = Application.dataPath + "\\..\\release\\HUSSDKDemo.exe";
if (!File.Exists(fileName))
{
UnityEngine.Debug.LogErrorFormat("path:{0} not exist!", fileName);
return;
}
IsSDKInit = true;
}
myprocess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo(fileName, args);
myprocess.StartInfo = startInfo;
myprocess.StartInfo.UseShellExecute = false;
myprocess.Start();
}
/// <summary>
/// 開啓視屏程序
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private void StartProcess(string args)
{
try
{
string fileName = Application.dataPath + "\\..\\release\\HUSSDKDemo.exe";
if (!File.Exists(fileName))
{
UnityEngine.Debug.LogErrorFormat("path:{0} not exist!", fileName);
}
if (myprocess == null)
{
UnityEngine.Debug.LogError("create myprocess.StartInfo");
CreateNewProcess(args,fileName);
}
else if (myprocess.HasExited)
{
UnityEngine.Debug.LogError("Camera:myprocess.HasExited");
CreateNewProcess(args,fileName);
}
else
{
//這一部分,好像沒有作用。根據名稱找窗口,還是爲0
IntPtr hWnd = myprocess.MainWindowHandle; //獲取Form1.exe主窗口句柄
int pId = -1;
if (hWnd.ToInt32() == 0)
{
//HoneyWellWindowName = "HoneyWellVideoMonitor"; 窗體名爲中文時,找不到。改成英文可以
pId = FindWindow(null, HoneyWellWindowName);
}
else
{
pId = hWnd.ToInt32();
}
string sendString = args;
byte[] sarr = System.Text.Encoding.Default.GetBytes(sendString);
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)0;
cds.cbData = len + 1;
cds.lpData = sendString;
ThreadManager.Run(()=>
{
//下方pid爲0的情況,所有程序的 DefWndProc方法,都能接收到Untiy發的信息,這也是需要優化的地方
SendMessage(pId, WM_COPYDATA, 0, ref cds); //SendMessage是同步的,考慮放在線程裏,防止卡住主線程
//想用PostMessage的方式,異步發送消息。但是測試沒成功,後續得再調整
},()=>
{
UnityEngine.Debug.LogError("Show camera:" + args);
},"");
}
}
catch (Exception ex)
{
UnityEngine.Debug.LogError("Error : CameraVideoManage.StartProcess:" + ex.Message);
}
}
private void OnDestroy()
{
//winform關閉時,攔截了關閉消息。默認讓它隱藏在後臺,所以關閉untiy時,通過進程把程序關了。
if (myprocess != null) myprocess.CloseMainWindow(); //程序關閉識,把進程關閉。不然再打開程序,會有多個視頻進程
}
#region WinAPI
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
/// <summary>
/// 根據句柄查找進程ID
/// </summary>
/// <param name="hwnd"></param>
/// <param name="ID"></param>
/// <returns></returns>
[System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
/// <summary>
/// 使用COPYDATASTRUCT來傳遞字符串
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
//消息發送API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
IntPtr hWnd, // 信息發往的窗口的句柄
int Msg, // 消息ID
int wParam, // 參數1
int lParam //參數2
);
//消息發送API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
int hWnd, // 信息發往的窗口的句柄
int Msg, // 消息ID
int wParam, // 參數1
ref COPYDATASTRUCT lParam //參數2
);
//消息發送API
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(
IntPtr hWnd, // 信息發往的窗口的句柄
int Msg, // 消息ID
int wParam, // 參數1
int lParam // 參數2
);
//異步消息發送API
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(
int hWnd, // 信息發往的窗口的句柄
int Msg, // 消息ID
int wParam, // 參數1
ref COPYDATASTRUCT lParam // 參數2
);
#endregion
WinFrom端:
程序啓動入口,通過unity打開程序,能夠收到傳遞的參數:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ShowLog(args);
}
static void ShowLog(string[] args)
{
args = new string[] {"192.168.1.1|admin|admin|dddddd" };
if (args.Length <= 0)
{
Application.Run(new Form_SDKDemo());
}
if (args.Length == 1)
{
//MessageBox.Show("成功啓動,參數爲" + args.ToString());
SingleLiveForm form = new SingleLiveForm(args);
Application.Run(form);
}
}
}
功能界面:SingleLiveForm 只放部分用到的代碼
/// <summary>
/// 使用COPYDATASTRUCT來傳遞字符串
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
private static int WMA_InterPro = 0x004A;
protected override void DefWndProc(ref Message m)
{
if (m.Msg == WMA_InterPro)
{
COPYDATASTRUCT cds = new COPYDATASTRUCT();
Type t = cds.GetType();
cds = (COPYDATASTRUCT)m.GetLParam(t);
string receiveInfo = cds.lpData;
this.Show();
this.ShowInTaskbar = true;
string[] values = receiveInfo.Split('|');
if (values.Length < 4)
{
return;
}
string devGuid = values[3];
ShowVideo(devGuid);
}
else
{
base.DefWndProc(ref m);
}
}
private void SingleLiveForm_FormClosing(object sender, FormClosingEventArgs e)
{
//取消關閉窗口 ,隱藏至後臺
e.Cancel = true;
this.Hide();
this.ShowInTaskbar = false;
}