小编工作的单位性质偏向于服务制造。大家都知道,中国的高端装备基本都依赖进口,而其自带CAM软件一般都是全封闭的,不开放API接口。但是在工业互联网时代,设备联网,生产数据上云已经是大势所趋。那么,如何在国外技术封锁的情况下实现第三方软件的数据获取呢?
在Windows系统下,有一个东西叫句柄,它是Windows编程的基础,只要是运行在Windows上的软件都绕不开它。本文就带大家学习如何利用底层句柄来实现第三方软件的交互。
句柄及其作用关于句柄的官方解释就不在此处过多提及,一般刚接触这个概念的人也看不懂。掌握句柄只需要理解这几点:
1、句柄不是什么玄乎的东西,它只是一个4字节(64系统下为8字节,后续文章都以32位来展开文章)的数值。但是它是唯一的,系统在启动时会建立一个句柄表,程序在Windows系统上运行时由系统自动分配给每一个对象,例如:窗体句柄、窗体控件句柄(输入框、按钮)、文件句柄等。
2、句柄单独存在于一块固定的区域,由于对象在内存中的地址可能会实时变化,但是系统会将这个变化的地址通过唯一不变的句柄绑定好,以不变应万变。
3、你可以将其理解成类似指针,通过它你能找到程序中的每一个对象。
4、虽然句柄类似指针,可以通过句柄找到对应的对象,但是你却不能像使用指针那样使用句柄,必须通过系统封装的API去使用。
如下图,是通过spy 查看GifCam(一个第三方gif制作小软件)各控件的句柄。图中Handle:000A07F0就是Rec按钮的句柄,获得了它的句柄,就可以通过系统API控制按钮的各项功能,比如单击开始录制。
spy 查看GifCam
类比理解如果上述表述大家还不能理解的话,请看类比理解:
大家都看过古装电视剧吧?在古代,每个奴隶身上都会被烙上一个奴隶印记,并且每个印记的编号不同。奴隶主在管理奴隶时,都是通过印记来管理的。但是每个奴隶本身也都是有名字的。类比到句柄就可以这样理解:奴隶这个实物就是对象,他的名字就是他在计算机中的地址(名字可以随意变,奴隶想更名改姓重新开始生活,但是奴隶主会愿意吗?),奴隶印记就是句柄(不管你怎么更名改姓,只要这个奴隶印记在,你就跑不掉)。
图示理解:
句柄示意图
句柄实际应用前文讲了很多关于句柄理解的内容,此处开始回到实际项目中,一起探究句柄在实际项目中如何发挥作用。
需求:如下图,需要实时获取图中振幅和相位的值。
需获取数据的窗体
程序设计思路:
程序设计思路
利用CSharp实现上述功能需要引入using System.Runtime.InteropServices;和using System.Diagnostics;
System.Diagnostics:负责进程相关
System.Runtime.InteropServices:dllImport特性,负责调用系统API。
代码实现:
class Program
{
//系统API
//通过窗体标题寻找窗体句柄
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName
);
//遍历主窗体下的所有句柄(各控件对象)
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(
IntPtr hWnd1,
IntPtr hWnd2,
string lpsz1,
string lpsz2
);
//通过对象句柄获取控件文字内容
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
//通过对象句柄获取控件类名
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
//通过对象句柄给对象发系统消息(系统定义的消息标识:如单击、输入文字等)
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessages(IntPtr hWnd, // 信息发往的窗口的句柄
int msg, // 消息ID
int wParam, // 参数1
int lParam //参数2
);
//---------------------------
static void Main(string[] args)
{
bool find = false;
IntPtr hwnd = IntPtr.Zero;
while (!find)
{
string title = null;
Process[] process = Process.GetProcesses();
foreach (Process p in process)
{
title = p.MainWindowTitle.ToString();
if (title.Contains("iBalance"))
{
hwnd = p.Handle;
find = true;
break;
}
}
// Console.WriteLine(title);
if (find) hwnd = FindWindow(null, title);
else
{
ProcessStartInfo info = new ProcessStartInfo(@"C:\Program Files\ANCA\RN31.1-1\TG7\BIN\Vibe_Monitor.exe");
Process.Start(info);
Thread.Sleep(1000);
Console.WriteLine("open it");
}
}
find = false;
if (hwnd != IntPtr.Zero)
{
StringBuilder sb_text = new StringBuilder(1024);
StringBuilder sb_class= new StringBuilder(1024);
IntPtr child = IntPtr.Zero ;
do
{
child = FindWindowEx(hwnd, child, null, null);
GetWindowText(child,sb_text,sb_text.Capacity);
// Console.Write(sb_text.ToString() "::CLASS::");
GetClassName(child, sb_class, sb_class.Capacity);
//Console.WriteLine(sb_class.ToString());
if (sb_text.ToString().Contains(".") && sb_class.ToString() == "Static")
{
find = true;
break;
}
sb_text.Remove(0,sb_text.Length);
sb_class.Remove(0, sb_class.Length);
} while (child != IntPtr.Zero);
StringBuilder msg = new StringBuilder();
if (find)
{
int i = 0;
string msg_combine = string.Empty;
try
{
while (true)
{
Thread.Sleep(10);
sb_text.Remove(0, sb_text.Length);
GetWindowText(child, sb_text, sb_text.Capacity);
int index = sb_text.ToString().IndexOf("\n");
msg_combine = DateTime.Now.ToString() ":" "震幅:" sb_text.ToString().Substring(0, index) "相位:" sb_text.ToString().Substring(index 1, sb_text.ToString().Length - index - 1);
i ;
Console.WriteLine(msg_combine "::" i);
msg.AppendLine(msg_combine);
if (i == 1024)
{
i = 0;
StreamWriter sw = new StreamWriter("msg.txt", true, Encoding.UTF8);
sw.WriteLine(msg.ToString());
msg = new StringBuilder();
sw.Flush();
sw.Close();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("程序被关闭");
}
}
}
Console.Read();
}
}