VWorks 14 OpenLab API

Helloo! Has anyone tried to automate the OpenLab login step when opening the VWorks 14? Right now I can open OpenLab in C# by calling the exe along with –console –param 14, but the login through username and password is still done manually. It seems like there’s an option for password token but I cannot find this anywhere. Anyone has any idea?

If this really doesn’t work we will stick to manually logging in. It’s just something nice to have that I am currently exploring!

TYIA!!

1 Like

I think it has been fixed in 14.5. Haven’t tried yet.

And… you should be using the API. https://www.agilent.com/cs/library/usermanuals/public/D0008025_VWorks_API_Reference_Agilent.pdf

M

1 Like

I did something to control the vwork software to run protocol file through LIMS interface of vwork, for at that time I do not have the vwork API manual. I think do as the manual will be the best way. But it will be possible to control vwork, and I share the code.

using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;

namespace ScratchNet.Agilent.Controller
{
    class VWorkRunItem
    {
        protected static readonly ILog log = LogManager.GetLogger("vwork");
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
        [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
        [DllImport("user32.dll")]
        static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        public const int WM_SYSCOMMAND = 0x0112;

        public const int SC_CLOSE = 0xF060;

        [DllImport("user32.dll")]
        static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")]
        static extern int PostMessage(IntPtr hwnd, uint msg, IntPtr wparam, IntPtr lparam);
        [DllImport("USER32.DLL")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("User32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        public const int SW_SHOWMINNOACTIVE = 7;
        [DllImport("user32.dll")]
        static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int msg, int Param, string s);

        const int WM_SETTEXT = 0x000c;

        private void SetTextt(IntPtr hWnd, int item, string text)
        {
            IntPtr boxHwnd = GetDlgItem(hWnd, item);
            SendMessage(boxHwnd, WM_SETTEXT, 0, text);
        }
        /// <summary>
        /// 启动vwork软件,并登录
        /// </summary>
        /// <returns></returns>
        public Process StartVWork(string binPath, string user, string password)
        {
            //var ps = new ProcessStartInfo(binPath);
            //ps.CreateNoWindow = false;
            //ps.UseShellExecute = true;
            var px = Process.Start(binPath);
            Thread.Sleep(5000);
            //登录窗口句柄
            IntPtr huser = IntPtr.Zero;
            while (huser == IntPtr.Zero)
            {
                //主窗口句柄
                IntPtr handle = px.MainWindowHandle;
                //SetForegroundWindow(handle);
                ShowWindow(handle, SW_SHOWMINNOACTIVE);
                //菜单栏句柄
                //IntPtr h2 = FindWindowEx(handle, IntPtr.Zero, "ProfUIS-DockBar", "Menubar");
                //Console.WriteLine(string.Format("{0:X2}", h2.ToInt32()));
                //获得工具栏句柄
                //IntPtr h3 = FindWindowEx(h2, IntPtr.Zero, "ProfUIS-ControlBar", "Control Toolbar");
                //Console.WriteLine(string.Format("{0:X2}", h3.ToInt32()));
                //发送点击登录按钮消息
                PostMessage(handle, 0x0111, new IntPtr(0x03E9), IntPtr.Zero);
                //Console.WriteLine("Send Completed");
                Thread.Sleep(200);
                //查找登录窗口
                huser = FindWindowByCaption(IntPtr.Zero, "User Authentication");
            }
            log.Info("find the login dialog " + huser);
            //填写用户名和密码
            //Console.WriteLine(huser);
            Thread.Sleep(50);
            var btnHandler = FindWindowEx(huser, IntPtr.Zero, "Button", "OK");
            SetTextt(huser, 1000, user);
            SetTextt(huser, 1012, password);
            //发送点击OK按钮消息            
            PostMessage(huser, 0x0111, new IntPtr(0x01), btnHandler);
            DateTime timeout = DateTime.Now.AddSeconds(5);
            Thread.Sleep(50);
            huser = FindWindowByCaption(IntPtr.Zero, "User Authentication");
            while (huser != IntPtr.Zero && timeout > DateTime.Now)
            {
                Thread.Sleep(50);
                huser = FindWindowByCaption(IntPtr.Zero, "User Authentication");
            }
            Thread.Sleep(150);
            //查找登录失败窗口
            timeout = DateTime.Now.AddSeconds(2);
            var failedHandler = FindWindow("#32770", "Login Error");
            while (failedHandler == IntPtr.Zero && timeout > DateTime.Now)
            {
                Thread.Sleep(50);
                failedHandler = FindWindow("#32770", "Login Error");
            }
            log.Info("finding the login failure dialog " + failedHandler);
            //Console.WriteLine(huser + ", " + failedHandler);
            if (huser != IntPtr.Zero || failedHandler != IntPtr.Zero)
            {
                log.Error("login to vwork failed with " + user + "/" + password);
                throw new Exception("Failed to login to vwork");// Language.LoginFailedDesc);
            }
            return px;
        }

        public string ModifiedProtocol { get; private set; }//因为运行后文件被打开,任何修改已经都没用了,所以必须用一个新名字
        public string StartFile { get; private set; }
        public string CompleteFile { get; private set; }
        public string OriginalProtocol { get; private set; }
        public event EventHandler<EventArgs> Started;
        public event EventHandler<EventArgs> Completed;
        public VWorkRunItem(string protocol, string modifiedProtocolPath, string startFile, string completeFile)
        {
            OriginalProtocol = protocol;
            ModifiedProtocol = modifiedProtocolPath;
            StartFile = startFile;
            CompleteFile = completeFile;
            ModifyProtocol(protocol, modifiedProtocolPath, startFile, completeFile);
        }
        bool IsAborting = false;
        public void Abort()
        {
            IsAborting = true;
        }
        public void Run(string inputfile, int waitStartTimeout=5, int waitCompletTimeout=0)
        {
            IsAborting = false;
            if (File.Exists(StartFile))
                File.Delete(StartFile);
            if (File.Exists(CompleteFile))
                File.Delete(CompleteFile);
            log.Info("run modified protocol: " + ModifiedProtocol);
            PrepareInputFile(inputfile);
            bool waitStarted = false;
            //如果开始失败,重试几次
             waitStarted = WaitFile(StartFile, waitStartTimeout);
            //如果开始还是失败,则通知用户
            if (!waitStarted)
            {
                log.Error("failed to start the profocol, and ask for restarting the vwork software");
                throw new TimeoutException("Timeout when starting the protocol");
            }
            //删除开始文件
            Thread.Sleep(200);
            File.Delete(StartFile);

            Started?.Invoke(this, new EventArgs());

            if (!WaitFile(CompleteFile, waitCompletTimeout))
            {
                log.Error("no complete file found");
                MessageBox.Show("failed to complete");
            }

            //删除结束文件
            Thread.Sleep(100);
            File.Delete(CompleteFile);
            int retry = 5;
            if (retry>0 && !WaitAndCloseVWorksDlg())
            {
                log.Error("failed to close the complete dialog");
                retry--;
                Thread.Sleep(200);
            }

            Completed?.Invoke(this, new EventArgs());

        }
        void PrepareInputFile(string inputfile)
        {
            var temp = Path.GetTempFileName();
            StreamWriter writer = new StreamWriter(new FileStream(temp, FileMode.Create));
            writer.Write(ModifiedProtocol.Replace("\\", "/"));
            writer.Flush();
            writer.Close();
            File.Move(temp, inputfile);

        }
        bool WaitFile(string file, int timeout = 60)
        {
            log.Info("wait for " + file);
            if (timeout <= 0)
            {
                while (!IsAborting)
                {
                    if (File.Exists(file))
                    {
                        //Console.WriteLine("find file " + file);
                        return true;
                    }
                    Thread.Sleep(200);
                }
                return false;
            }
            else
            {
                DateTime t = DateTime.Now.AddSeconds(timeout);
                while (DateTime.Now < t)
                {
                    if (File.Exists(file))
                    {
                        //Console.WriteLine("find file " + file);
                        return true;
                    }
                    if (IsAborting)
                        return false;
                    Thread.Sleep(200);
                }
                return false;
            }
        }
        bool WaitAndCloseVWorksDlg(int timeout = 10)
        {
            //Console.WriteLine("finding the dlg");
            DateTime t = DateTime.Now.AddSeconds(timeout);
            IntPtr handler = IntPtr.Zero;//dlg handler
            while (DateTime.Now < t)
            {
                handler = FindWindow("#32770", "VWorks");
                if (handler != IntPtr.Zero)
                {
                    //Console.WriteLine("find dlg " + handler);
                    break;
                }
                Thread.Sleep(50);
            }
            if (handler == IntPtr.Zero)
                return false;
            Thread.Sleep(200);
            SendMessage(handler, WM_SYSCOMMAND, SC_CLOSE, 0);
            return true;
        }
        void ModifyProtocol(string protocol, string modifiedProtocol, string startFile, string completeFile)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(protocol);
            var node = doc.SelectSingleNode("Velocity11/File_Info");
            SetScript(doc, node, "StartScript", CreateScript(startFile, "started"));
            SetScript(doc, node, "FinishScript", CreateScript(completeFile, "completed"));
            doc.Save(modifiedProtocol);
        }
        string CreateScript(string file, string content)
        {
            string script = "var obj=new File();obj.Open(\"" + file.Replace("\\", "/") + "\");obj.Write(\"" + content + "\");obj.Close();";
            return script;
        }
        void SetScript(XmlDocument doc, XmlNode node, string name, string script)
        {
            var snode = node.Attributes[name];
            if (snode == null)
            {
                snode = doc.CreateAttribute(name);
                node.Attributes.Append(snode);
            }
            snode.Value = script;
        }
    }
}


for handling user name and passwork in login dialog, following code was used (run under administrator)

huser = FindWindowByCaption(IntPtr.Zero, "User Authentication");            
var btnHandler = FindWindowEx(huser, IntPtr.Zero, "Button", "OK");
SetTextt(huser, 1000, user);
SetTextt(huser, 1012, password);
3 Likes

Hi Mauro,

Yea we are aware of that! It’s just all our production ODTC bravos are on 14.4 and if we upgrade we need to do a lot of validation/verification :frowning: , but if there’s no other way around we will think about it.

It is still openlab 2.8 so you will only have to replace the vworks part, without touching openlab.

I understand that validation takes time, but it is probably worth using the native feature. Anyway. Up to you! :blush:

1 Like