繁体   English   中英

在 c# 中递归获取 SAP Gui Session 的所有子级

[英]Recursively get all children of SAP Gui Session in c#

我希望在 c# 中遍历所有 SAP GuiComponents,但我正在努力获取 gui 会话的所有子级。

这是我到目前为止所拥有的(最初将 session.ActiveWindow.Children 传递给节点):

 private void IterateFindIDByNAme(GuiComponentCollection nodes, string searchstring)
    {
        if (foundID == "")
        {
            foreach (GuiComponent node in (GuiComponentCollection)nodes)
            {
                var comp = node;
                if (comp.Name.Contains(searchstring))
                    foundID = comp.Id;
                else
                {
                    try
                    {
                        FindIDByNAme((GuiComponentCollection)node, searchstring);
                    }
                    catch { }
                }
            }
        }
    }

它能够获取 session.ActiveWindow 的所有子元素,但是当尝试将所有子元素转换为 GuiComponentCollections 时,它会摔倒。

如果我可以使用 FindByName 函数,这将无关紧要,但由于某种原因,它无法在我当前正在处理的 SAP 屏幕上工作(它确实适用于其他人,不知道为什么)。

字段ID为:

wnd[0]/usr/subBCA_SUB_HEADER:SAPLBANK_BDT_CTRL:0100/subSUBS_DETAIL:SAPLBUSS:0028/ssubGENSUB:SAPLBUSS:4038/subA01P02:SAPLBCA_DYN_CN_CNMANO:0002/ctxtBCA_DYN_CONTRACT_ORGENTRY-ORGUNIT

我正在尝试的功能是:

((GuiTextField)session.FindByName("BCA_DYN_CONTRACT_ORGENTRY-ORGUNIT", "GuiTextField")).Text = "Test";

findbyid 工作正常,但不是 findbyname?

我知道这是两个问题,但有点相关。

ctxt BCA_DYN_CONTRACT_ORGENTRY-ORGUNIT

控件的类型是GuiCTextField而不是GuiTextField

session.FindByName("BCA_DYN_CONTRACT_ORGENTRY-ORGUNIT", "GuiCTextField")

示例代码:

        public DateTime? GetModificationDate(int employeeID)
        {
            var session = SapHelper.GetActiveSession();

            Console.WriteLine("Recherche de la measure [A5 ou A6] avec un motif 90...");
            var window = session.BeginTransaction("PA20", "Afficher données de base personnel");
            window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
            window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = "Mesures  (0000)";
            window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = null;
            window.FindByName<GuiButton>("btn[20]").Press(); // list view

            if (window.Text == "Afficher données de base personnel")
            {
                Console.WriteLine(">> " + window.FindByName<GuiStatusbar>("sbar").Text);
                return null;
            }

            /*  Index Type          Title             Tooltip             
                0     GuiTextField  Début             Date de début       
                1     GuiTextField  Fin               Date de fin         
                2     GuiCTextField Mes.              Catégorie de mesure 
                3     GuiTextField  Dés. cat. mesure  Dés. cat. mesure    
                4     GuiCTextField MotMe             Motif mesure        
                5     GuiTextField  Dés. motif mesure Dés. motif mesure   
                6     GuiCTextField Client            Statut propre client
                7     GuiCTextField Activité          Statut d'activité   
                8     GuiCTextField Paiement          Statut paiement part */
            var result = window.FindByName<GuiTableControl>("MP000000TC3000").AsEnumerable()
                .Select(x => new
                {
                    Start = x.GetText(0),
                    Category = x.GetCText(2),
                    CategoryText = x.GetText(3),
                    Reason = x.GetCText(4),
                    ReasonText = x.GetText(5),
                })
                .Where(x => (x.Category == "A5" || x.Category == "AG") && x.Reason == "90")
                .FirstOrDefault();
            if (result == null)
            {
                Console.WriteLine(">> aucune measure [A5 ou A6] avec un motif 90 trouvée");
                return null;
            }
            else
            {
                Console.WriteLine(">> {0}:[{1}]{2} [{3}]{4}",
                    result.Start, result.Category, result.Category, result.Reason, result.ReasonText);
                return DateTime.ParseExact(result.Start, "yyyy/MM/dd", CultureInfo.InvariantCulture);
            }
        }

XY.Sap :

#region namespace Xy.Sap
namespace Xy.Sap
{
    using System.Reflection;
    using sapfewse;
    using saprotwr.net;
    using COMException = System.Runtime.InteropServices.COMException;

    public static class SapHelper
    {
        public static GuiSession GetActiveSession()
        {
            var rot = new CSapROTWrapper().GetROTEntry("SAPGUI");
            if (rot == null)
                throw SapException.NotOpened();

            var app = (GuiApplication)rot.GetType().InvokeMember("GetScriptingEngine", BindingFlags.InvokeMethod, null, rot, null);
            var connectedSession = app.Connections.Cast<GuiConnection>()
                .SelectMany(x => x.Children.Cast<GuiSession>())
                .Where(x => !string.IsNullOrEmpty(x.Info.User))
                .FirstOrDefault();

            if (connectedSession == null)
                throw SapException.NotOpened();

            return connectedSession;
        }
    }

    public class SapException : Exception
    {
        public SapException(string message) : base(message) { }

        public static SapException NotOpened()
        {
            return new SapException("Veuillez lancer le SAP et de connecter avec votre identité");
        }
    }

    public static class SapExtensions
    {
        #region GuiSession

        /// <summary>
        /// Shortcut for PA20 query
        /// </summary>
        /// <param name="session"></param>
        /// <param name="employeeID"></param>
        /// <param name="it">Infotype ID</param>
        /// <param name="sty">Subtype ID</param>
        /// <param name="asListView"></param>
        /// <returns></returns>
        public static GuiFrameWindow QueryPA20(this GuiSession session, int employeeID, string it, string sty = null, bool asListView = false)
        {
            var window = session.BeginTransaction("PA20", "Afficher données de base personnel");
            window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
            window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = it;
            window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = sty;
            window.FindByName<GuiButton>(asListView ? "btn[20]" : "btn[7]").Press();

            if (window.Text == "Afficher données de base personnel")
            {
                var exception = new InvalidOperationException(string.Format("Failed to access to personal information of {0}", employeeID));
                exception.Data["Employee ID"] = employeeID;
                exception.Data["Infotype"] = it;
                exception.Data["Subtype"] = sty;
                exception.Data["View"] = asListView ? "ListView[Mont]" : "RecordView[Glasses]";
                exception.Data["Status Message"] = window.FindByName<GuiStatusbar>("sbar").Text;

                throw exception;
            }

            return window;
        }

        /// <summary>
        /// Shortcut for PA30 query
        /// </summary>
        /// <param name="session"></param>
        /// <param name="employeeID"></param>
        /// <param name="it">Infotype ID</param>
        /// <param name="sty">Subtype ID</param>
        /// <param name="asListView"></param>
        /// <returns></returns>
        public static GuiFrameWindow QueryPA30(this GuiSession session, int employeeID, string it, string sty = null)
        {
            var window = session.BeginTransaction("PA30", "Gérer données de base HR");
            window.FindByName<GuiCTextField>("RP50G-PERNR").Text = employeeID.ToString();
            window.FindByName<GuiCTextField>("RP50G-CHOIC").Text = it;
            window.FindByName<GuiCTextField>("RP50G-SUBTY").Text = sty;
            window.FindByName<GuiButton>("btn[6]").Press();

            if (window.Text == "Gérer données de base HR")
            {
                var exception = new InvalidOperationException(string.Format("Failed to access to personal information of {0}", employeeID));
                exception.Data["Employee ID"] = employeeID;
                exception.Data["Infotype"] = it;
                exception.Data["Subtype"] = sty;
                exception.Data["Status Message"] = window.FindByName<GuiStatusbar>("sbar").Text;

                throw exception;
            }

            return window;
        }


        /// <summary>
        /// Start a new transaction and return the active window
        /// </summary>
        public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, string expectedTitle)
        {
            return session.BeginTransaction(transactionID,
                x => x.Text == expectedTitle,
                x =>
                {
                    var exception = new InvalidOperationException(string.Format("Failed to open transaction : {0}", transactionID));
                    exception.Data["Transaction ID"] = transactionID;
                    exception.Data["Expected Title"] = expectedTitle;
                    exception.Data["Current Title"] = x.Text;
                    exception.Data["Status Message"] = x.FindByName<GuiStatusbar>("sbar").Text;

                    return exception;
                });
        }
        public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, string> errorFormatter)
        {
            return session.BeginTransactionImpl(transactionID, validation, x => new Exception(errorFormatter(x)));
        }
        public static GuiFrameWindow BeginTransaction(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, Exception> errorBuilder)
        {
            return session.BeginTransactionImpl(transactionID, validation, errorBuilder);
        }
        private static GuiFrameWindow BeginTransactionImpl(this GuiSession session, string transactionID, Predicate<GuiFrameWindow> validation, Func<GuiFrameWindow, Exception> errorBuilder)
        {
            // force current transaction to end, preventing any blocking(eg: model dialog)
            session.EndTransaction();

            session.StartTransaction(transactionID);
            var window = session.ActiveWindow;
            if (!validation(window))
                throw errorBuilder(window);

            return window;
        }

        #endregion
        #region GuiFrameWindow

        public static TSapControl FindByName<TSapControl>(this GuiFrameWindow window, string name)
        {
            try
            {
                return (TSapControl)window.FindByName(name, typeof(TSapControl).Name);
            }
            catch (COMException e)
            {
                var writer = new StringWriter();
                writer.WriteLine("The control could not be found by name and type.");
                writer.WriteLine("Name : " + name);
                writer.WriteLine("Type : " + typeof(TSapControl).Name);

                throw new Exception(writer.ToString(), e);
            }
        }

        #endregion
        #region GuiTableControl

        /// <summary>Note: Do not iterate through this ienumerable more than once</summary>
        public static IEnumerable<GuiTableRow> AsEnumerable(this GuiTableControl table)
        {
            var container = table.Parent as dynamic;
            string name = table.Name, type = table.Type;
            int rowCount = table.VerticalScrollbar.Maximum;

            Func<GuiTableControl> getTable = () => container.FindByName(name, type) as GuiTableControl;

            for (int i = 0; i <= rowCount; i++)
            {
                getTable().VerticalScrollbar.Position = i;
                yield return getTable().Rows.Item(0) as GuiTableRow;
            }
        }

        public static TSapControl GetCell<TSapControl>(this GuiTableRow row, int column)
        {
            return (TSapControl)row.Item(column);
        }
        public static string GetCText(this GuiTableRow row, int column)
        {
            return row.GetCell<GuiCTextField>(column).Text;
        }
        public static string GetText(this GuiTableRow row, int column)
        {
            return row.GetCell<GuiTextField>(column).Text;
        }
        #endregion
    }
}
#endregion

编辑:下面评论中提到的 LINQPad 脚本的链接不再有效。 在这里重新上传了它。

此脚本可帮助您浏览 SAP GUI:

  • 列出控件类型、名称、id、内容
  • 列出属性和方法
  • 高亮控制
  • 生成选择器: .FindByName<GuiTableControl>("MP000000TC3000")

如果有人想知道如何获取任何 gui 组件的子元素(这只会将所有 id 写入控制台):

private void LoopAllElements(GuiComponentCollection nodes)
    {
        foreach (GuiComponent node in (GuiComponentCollection)nodes)
        {
            Console.WriteLine(node.Id);
                if (node.ContainerType)
                {
                    var children = ((node as dynamic).Children as GuiComponentCollection);
                    LoopAllElements(children);
                }
        }
    }

这将遍历您传递给它的任何 GUIComponentCollection 的所有子元素,例如

LoopAllElements(session.Children)

可能有更好的 linq 方法来执行此操作,但这应该可以帮助您入门。

另请查看 Xiaoy312 创建的这个要点https://gist.github.com/Xiaoy312/a1424bd34acae92554105b27b0c80971

这有很多有用的功能来调试您当前的 sap 会话,我发现这些功能非常有用。

您可以查看代码,因为它具有许多可以使用的功能,例如改进的 findbyid 和 findbyname,或者在 linqpad https://www.linqpad.net/Download.aspx 中运行它,这将允许您询问当前的 SAP 会话.

如果我理解正确,我们必须手动启动 SAP Logon 才能使下面的行正常工作:

var rot = new CSapROTWrapper().GetROTEntry("SAPGUI");

我说得对吗?

如果是这样,也许有人可以在线解释我为什么:

var app = (GuiApplication)rot.GetType().InvokeMember("GetScriptingEngine", BindingFlags.InvokeMethod, null, rot, null);

我收到异常:

System.Dynamic.dll 中出现“System.Runtime.InteropServices.COMException”类型的异常,但未在用户代码中处理附加信息:加载类型库/DLL 时出错。 (来自 HRESULT 的异常:0x80029C4A (TYPE_E_CANTLOADLIBRARY))

我已经尝试将目标平台从 Any CPU 更改为 x86 和 x64,但未能成功解决此问题。

但是当我使用以下脚本运行 SAPGUI 时:

var SapGuiApp = new GuiApplication();
string development = "Development ERP System";
var connection = SapGuiApp.OpenConnection(development);

而不是手动运行 SAP Logon 上面描述的问题不会重现。

但在这种情况下,我有其他问题:GUI 不同,第二个选项 GUI 看起来已损坏 - 一些 GUI 控件未显示(请参见屏幕截图)。 请给我建议可能有什么问题? 提前谢谢你在此处输入图片说明

更新:我找到了解决下面描述的问题的解决方案。 在尝试获取会话连接时,您必须使用 object 而不是 var:

CSapROTWrapper sapROTWrapper = new CSapROTWrapper();
        object rot = sapROTWrapper.GetROTEntry("SAPGUI");
        object engine = rot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, rot, null);
        GuiConnection connection = (engine as GuiApplication).OpenConnection(connectionName);
        GuiSession session = connection.Children.ElementAt(0) as GuiSession;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM