期间也遇到很多问题:
使用了豆包和千问回答问题基本相似,使用activeX,使用在左侧的 工具箱 (Toolbox) 中找到你之前添加的 National Instruments CW UI Control。(这个方法一直没有找到控件的问题,处理了很久)后来依据豆包的答案使用 ativeX 的方法实际是通过 LabVIEW 的 ActiveX 服务器接口,C# 可以直接打开 VI 并操控前面板控件,甚至把前面板窗口嵌入到 WinForms 窗体里实际是运行起来强制使用win32API放到对应的Form窗体里。
第二种方式:
首先明确:LabVIEW 2024 已经没有「工具 → 转换 → 生成 .NET 控件」这个功能了。NI 在较新版本中移除了直接把 VI 前面板打包成 .NET UserControl 的能力。(根据追问情况 说是 2015以后得版本都没有这个功能跟个,但是我下载了 8.6版以及2012版安装完了以后一样没有这个菜单功能)
第三种方法:
LabVIEW 菜单:工具 → 选项 → 服务器:Web 服务器 / VI 服务器
确认 VI 服务器已启用,TCP/IP 勾选(操作了一遍实际上就是将web页面转换成image来展示)
所以我选择第一中方式:
其中意义就是用ActiveX应用打开VI然后将VI强制转入到Form窗体内:
需要解决的问题:
1.去掉VI页面上的菜单栏(这个试过很多方法依然无法解决,没有办法只能把在移动的时候网上提了个48DPI).
2.要确定打开应用的句柄查找的方式:
用 Spy++ 工具查看真实类名
Visual Studio 自带「Spy++」工具(工具栏 → 工具 → Spy++)打开后按查找窗口,拖到 LabVIEW 前面板上,就能看到准确的类名和标题(据说还可以
csharp
IntPtr lvHwnd = (IntPtr)vi.FrontPanel.NativeWindow;
那句柄但是我好像没有找到。)
先要引入COM
添加 LabVIEW COM 引用Visual Studio 右键项目 → 添加 → COM 引用
勾选「National Instruments LabVIEW 2024 Type Library」(版本号对应你的 LabVIEW)
点确定,项目里会多出一个 LabVIEW 的引用
完整代码如下:
csharp
public partial class Form2 : Form
{
// 查找窗口
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
// Win10/11 去掉 DWM 阴影边框(可选)
[DllImport("dwmapi.dll")]
static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr,
ref int attrValue, int attrSize);
[DllImport("user32.dll")]
static extern IntPtr GetMenu(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SetMenu(IntPtr hWnd, IntPtr hMenu);
[DllImport("user32.dll")]
static extern bool DrawMenuBar(IntPtr hWnd);
// 常量
const int GWL_STYLE = -16;
const int GWL_EXSTYLE = -20;
// 窗口样式位
const uint WS_CAPTION = 0x00C00000; // 标题栏
const uint WS_THICKFRAME = 0x00040000; // 可调边框
const uint WS_BORDER = 0x00800000; // 细边框
const uint WS_DLGFRAME = 0x00400000; // 对话框边框
const uint WS_SYSMENU = 0x00080000; // 系统菜单(右上角按钮)
const uint WS_MINIMIZEBOX = 0x00020000; // 最小化按钮
const uint WS_MAXIMIZEBOX = 0x00010000; // 最大化按钮
const uint WS_EX_DLGMODALFRAME = 0x00000001; // 扩展边框
const int DWMWA_NCRENDERING_POLICY = 2;
const int DWMNCRP_DISABLED = 1;
const uint SWP_NOMOVE = 0x0002;
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
const uint SWP_FRAMECHANGED = 0x0020; // 关键:强制重新计算非客户区
int menuBarHeight = -48; // 根据实际调整,一般 20~28 像素菜单剪切 需要双倍的剪切
public Form2()
{
InitializeComponent();
}
private IntPtr _lvHwnd;
private dynamic _lvApp;
private dynamic _vi;
private void button1_Click(object sender, EventArgs e)
{
//// 1. 创建 LabVIEW ActiveX 对象
//Type lvType = Type.GetTypeFromProgID("LabVIEW.Application");
//_lvApp = Activator.CreateInstance(lvType);
//// 2. 打开 VI
//string viPath = @"C:\Projects\MyTestVI.vi";
//_vi = _lvApp.VIs.Open(viPath);
//// 3. 显示前面板
//_vi.FrontPanel.Visible = true;
LabVIEW.Application lv = new LabVIEW.ApplicationClass();
LabVIEW.VirtualInstrument vi = lv.GetVIReference(System.Windows.Forms.Application.StartupPath + "\\LabViewFiles\\26072301.vi", "", false);
vi.FPWinOpen = true;//打开前面板
vi.SetControlValue("风扇", true); //设定控件值
//var myValue = vi.GetControlValue("指示器名称");
vi.Run(false);//打开当前VI
vi.OpenFrontPanel(true,LabVIEW.FPStateEnum.eMaximized);
// 4. 稍等窗口创建完成
System.Threading.Thread.Sleep(500);
// 5. 查找前面板窗口句柄
// 方法A:按窗口标题找(标题就是 VI 文件名)
_lvHwnd = FindWindow(null, "26072301.vi 前面板");
// 方法B:如果知道类名,更准确
// _lvHwnd = FindWindow("LVFrontPanelClass", "MyTestVI.vi");
if (_lvHwnd == IntPtr.Zero)
{
MessageBox.Show("找不到前面板窗口");
return;
}
//// 6. 去掉窗口标题栏和边框
//RemoveTitleBarAndBorder(_lvHwnd);
//// 7. 嵌入到 Panel 容器里
//SetParent(_lvHwnd, PanelMain.Handle);
//// 8. 调整大小填满容器
//MoveWindow(_lvHwnd, 0, 0, PanelMain.Width, PanelMain.Height, true);
///一次性使用放入面板
///
EmbedLabVIEW(_lvHwnd,PanelMain);
}
public void RemoveTitleBarAndBorder(IntPtr hWnd)
{
// 1. 读取当前样式
int style = GetWindowLong(hWnd, GWL_STYLE);
style &= ~(int)(WS_CAPTION | WS_THICKFRAME | WS_BORDER | WS_DLGFRAME | WS_SYSMENU);
SetWindowLong(hWnd, GWL_STYLE, style);
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
// 5. 【关键】强制窗口重新计算边框(否则改了不生效)
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
//去掉菜单栏
IntPtr hMenu = GetMenu(hWnd);
if (hMenu != IntPtr.Zero)
{
SetMenu(hWnd, IntPtr.Zero); // 设为空菜单
DrawMenuBar(hWnd); // 重绘,让改动生效
}
// 6. Win10/11 可选:去掉 DWM 阴影
int val = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, ref val, 4);
}
public void EmbedLabVIEW(IntPtr lvHwnd, Control container)
{
// 1. 去掉标题栏和边框
int style = GetWindowLong(lvHwnd, GWL_STYLE);
style &= ~(int)(WS_CAPTION | WS_THICKFRAME | WS_BORDER | WS_DLGFRAME | WS_SYSMENU);
SetWindowLong(lvHwnd, GWL_STYLE, style);
SetWindowPos(lvHwnd, IntPtr.Zero, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
// 2. 去掉菜单栏
IntPtr hMenu = GetMenu(lvHwnd);
if (hMenu != IntPtr.Zero)
{
SetMenu(lvHwnd, IntPtr.Zero);
DrawMenuBar(lvHwnd);
}
// 3. 嵌入容器
SetParent(lvHwnd, container.Handle);
MoveWindow(lvHwnd, 0, menuBarHeight, container.Width, container.Height- menuBarHeight, true);
}
private void Form2_SizeChanged(object sender, EventArgs e)
{
}
private void PanelMain_SizeChanged(object sender, EventArgs e)
{
if (_lvHwnd != IntPtr.Zero)
{
MoveWindow(_lvHwnd, 0, menuBarHeight, PanelMain.Width, PanelMain.Height - menuBarHeight, true);
}
}
}