技術要點
1.根據SAPGUI應用程序名動態讀取登錄配置文件的絕對路徑,如果應用程序未安裝,則提示相應消息。
2. 根據獲取的路徑得到配置文件的XML文本字符串,其中包含所有的登錄信息,調用上一篇日誌解析XML的類方法,得到登錄界面結構的文檔實例
3.通過文檔實例利用控件類動態生成控件的實例TreeView和ListView,作爲窗體面板控件的子節點
4.TreeView和ListView聯動控制,點擊TreeView葉子節點動態加載ListView,點ListView中某一行項目,則根據該項的信息關聯配置文件獲的的數據信息,動態生成一個*.sap圖標文件,並將相關信息構造成結構化字符串寫入到*.sap登錄文件中,
5.調用SapGui.exe可執行程序打開剛創建好的 *.sap文件,從而實現GUI動態登錄;當然這個功能強大的地方不在於此,而是找到了一種不用修改底層數據文件和代碼,直接通過前端UI操作來修改底層數據,實現想要的功能的方法
讀寫註冊表的類
class Registry
{
public enum SoftName : short
{
SAPgui
}
public enum ConfigKeyName : short
{
ConnectionConfigFile, //D:\ProgramFiles\Java\Common\saplogon.ini
MessageServerConfigFile, //C:\WINDOWS\SAPMSG.INI
RouterConfigFile, //C:\WINDOWS\SAPROUTE.INI
ShortcutConfigFile, //D:\ProgramFiles\Java\Common\sapshortcut.ini
TreeConfigFile //D:\ProgramFiles\Java\Common\SapLogonTree.xml
}
private static int index;
public static string findApplicaiton(SoftName softName)
{
string strKeyName = string.Empty;
string softPath;
try
{
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.LocalMachine;
switch(softName)
{
case SoftName.SAPgui:
softPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\AppPaths\";
Microsoft.Win32.RegistryKey regSubKey = regKey.OpenSubKey(softPath +softName + ".exe", false);
object objResult = regSubKey.GetValue(strKeyName);
Microsoft.Win32.RegistryValueKind regValueKind =regSubKey.GetValueKind(strKeyName);
if (regValueKind == Microsoft.Win32.RegistryValueKind.String)
{
return objResult.ToString();
}
break;
default:
break;
}
}
catch
{
return "";
}
return "";
}
public static string findApplicationFile(SoftName softName, ConfigKeyName fileName)
{
index = 0;
if (findApplicaiton(softName) == "")
{
return "";
}
else
{
switch(softName)
{
case SoftName.SAPgui:
return getApplicationKeyInfo(softName, fileName);
//break;
default:
break;
}
}
return "";
}
private static string getApplicationKeyInfo(SoftName softName, ConfigKeyName fileName)
{
Microsoft.Win32.RegistryKey KeyNode;
string path;
string[] keyName;
switch (softName)
{
case SoftName.SAPgui:
KeyNode = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\SAP\SAPLogon");
keyName = KeyNode.GetSubKeyNames();
for (int i = index; i < keyName.Length; i++)
{
index = i;
if (keyName[i].Contains("ConfigFiles"))
{
path = (string)KeyNode.OpenSubKey(keyName[i]).GetValue(fileName.ToString());
if (path == null || path == "")
{
continue;
}
else
{
return path;
}
}
}
break;
default:
break;
}
return "";
}
}
讀取配置文件的類,配置文件是以格式化文本的形式保存,根據其規則動態加載到鍵值表實例
class AnalyseKeyValueString
{
static Dictionary<string, string>[]source;
static int index = 0;
static Regex regex = newRegex(@"[a-zA-Z0-9-\s]+?=(""(\s|\S)*?""|[^""]*?$)");
static MatchCollection matchCollection;
public static Dictionary<string, string>[]getStruct(string str)
{
string[] keyvalue = new string[2];
string lable = "";
int num = getNum(str);
source = new Dictionary<string, string>[num];
for (int i = 0;i < num; i++)
{
source[i] = new Dictionary<string, string>();
}
string[] lines = str.Split(new char[] { '\r', '\n' },StringSplitOptions.RemoveEmptyEntries);
for (int i = 0;i < lines.Length; i++)
{
if(lines[i][0]=='[')
{
index = 0;
lable = lines[i].Substring(1, lines[i].Length - 2);
continue;
}
matchCollection = regex.Matches(lines[i]);
if(matchCollection.Count == 1)
{
keyvalue =matchCollection[0].Value.Split(newchar[] { '=' }, 2);
source[index].Add(lable, keyvalue[1]);
}
else if(matchCollection.Count > 1)
{
for (int j = 0; j < matchCollection.Count; j++)
{
keyvalue =matchCollection[j].Value.Split(newchar[] { '=' }, 2);
source[index].Add(keyvalue[0].Trim(), keyvalue[1].Replace("\"",""));
}
}
index++;
}
return source;
}
private static int getNum(string str)
{
return new Regex(@"([a-zA-Z0-9-\s]+?=(""(\s|\S)*?""|[^""^\[]*?\r\n)){2,}").Match(str).Value.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
解析XML的類AnalyseXML內容有點多,在上一篇日誌已經提過,有幾個主要的方法
1通過遞歸的方式將偏平的鏈表結構轉爲樹狀的深度鏈表結構
private List<tagNest> getNest(List<tagList> ltag)
{
List<tagNest>ntag = new List<tagNest>();
tagNest subtag;
for (int i = 0;i < ltag.Count; i++)
{
subtag = new tagNest();
subtag.tagName = ltag[i].tagName;
subtag.Attribute = ltag[i].Attribute;
subtag.subtag = getNest(Ltag.FindAll(s =>s.parentKey.Equals(ltag[i].Key)));
ntag.Add(subtag);
}
return ntag;
}
public void plain2nest()
{
Ntag = getNest(new List<tagList>() { Ltag[0] });
}
2.根據獲取的深度鏈表動態創建TreeView節點
/// <summary>
/// 初始化時用
/// </summary>
/// <paramname="aplicationView"></param>
public void addToAplicationViewTree()
{
getStruct();
plain2nest();
TreeNode node;
TreeNode subNode;
List<tagNest>Nodes = Ntag[0].subtag[1].subtag[1].subtag;
for (int i = 0;i < Nodes.Count; i++)
{
node = aplicationView.treeView1.Nodes.Add(Nodes[i].tagName);
node.SelectedImageIndex = node.ImageIndex = 2; //設置選中後的圖片和選中前的一致
for (int j = 0; j < Nodes[i].subtag.Count; j++)
{
Nodes[i].subtag[j].tagName= Nodes[i].subtag[j].Attribute["name"].ToString().Replace("\"","");
subNode = node.Nodes.Add(Nodes[i].subtag[j].tagName);
subNode.SelectedImageIndex = subNode.ImageIndex = 3;
}
}
}
3.根據點擊TreeView不同層級的節點響應不同的方法,葉子動態加載ListView
/// <summary>
/// 點擊Tree Node時調用
/// </summary>
/// <paramname="aplicationView.listView1">要展示的地方,提前已經創建好了Header</param>
/// <paramname="p">Node節點名稱</param>
internal void addToAplicationViewList(TreeNode treeNode)
{
Dictionary<string, string> detail;
if (treeNode.Level == 0)
{
if(treeNode.IsExpanded)
{
treeNode.Collapse();
treeNode.ImageIndex = 2;
treeNode.SelectedImageIndex = 2;
}
else
{
treeNode.Expand();
treeNode.ImageIndex = 1;
treeNode.SelectedImageIndex = 1;
}
}else if (treeNode.Level == 1)
{
aplicationView.listView1.Items.Clear();
tagNestnodes = Ntag[0].subtag[1].subtag[1].subtag.Find(s =>s.tagName.Equals(treeNode.Parent.Text)).subtag.Find(j =>j.tagName.Equals(treeNode.Text));
aplicationView.listView1.BeginUpdate(); //數據更新,UI暫時掛起,直到EndUpdate繪製控件,可以有效避免閃爍並大大提高加載速度
switch(treeNode.Parent.Text)
{
case "Favorites":
aplicationView.nodeStyle = AplicationView.NodeStyle.Favorites;
break;
case "Shortcuts":
aplicationView.nodeStyle = AplicationView.NodeStyle.Shortcuts;
//tagNest nodes =Ntag[0].subtag[1].subtag[1].subtag.Find(s=>s.Attribute.First(q=>q.Value== ""))
aplicationView.listView1.Columns.Clear();
aplicationView.listView1.Columns.Add("名稱", 120, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("描述", 150, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("系統", 60, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("集團", 60, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("賬號", 100, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("事務代碼", 60, HorizontalAlignment.Left);
for (int i = 0; i < nodes.subtag.Count; i++)
{
nodes.subtag[i].tagName = nodes.subtag[i].Attribute["name"].ToString().Replace("\"", "");
ListViewItem lvi = new ListViewItem();
lvi.ImageIndex = 4; //通過與imageList綁定,顯示imageList中第i項圖標
lvi.Text =nodes.subtag[i].tagName;
detail =aplicationView.listViewDetailShortcuts.First((s) => s["Label"] == lvi.Text);
lvi.SubItems.Add(detail["-desc"]);
lvi.SubItems.Add(detail["-sid"]);
lvi.SubItems.Add(detail["-clt"]);
lvi.SubItems.Add(detail["-u"]);
lvi.SubItems.Add(detail["-cmd"]);
aplicationView.listView1.Items.Add(lvi);
}
break;
case "Connections":
aplicationView.nodeStyle = AplicationView.NodeStyle.Connections;
aplicationView.listView1.Columns.Clear();
aplicationView.listView1.Columns.Add("名稱", 180, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("服務器地址", 180, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("系統編號", 60, HorizontalAlignment.Left);
aplicationView.listView1.Columns.Add("系統標識", 60, HorizontalAlignment.Left);
for (int i = 0; i < nodes.subtag.Count; i++)
{
nodes.subtag[i].tagName = nodes.subtag[i].Attribute["name"].ToString().Replace("\"", "");
ListViewItem lvi = new ListViewItem();
lvi.ImageIndex = 4; //通過與imageList綁定,顯示imageList中第i項圖標
lvi.Text =nodes.subtag[i].tagName;
detail =aplicationView.listViewDetailConnections.First((s) => s["Description"] == lvi.Text);
lvi.SubItems.Add(detail["Server"]);
lvi.SubItems.Add(detail["Database"]);
lvi.SubItems.Add(detail["MSSysName"]);
aplicationView.listView1.Items.Add(lvi);
}
break;
default:
break;
}
aplicationView.listView1.EndUpdate(); //結束數據處理,UI界面一次性繪製。
}
}
登錄窗口的類
public partial class AplicationView : Form
{
AnalyseXML analyseXML;
Form1 form1;
string configfilename="";
int edition = 0;
Dictionary<string, string> items;
public Dictionary<string, string>[]listViewDetailShortcuts;
public Dictionary<string, string>[]listViewDetailConnections;
public enum NodeStyle
{
None,
Favorites,
Shortcuts,
Connections,
}
public NodeStyle nodeStyle;
public AplicationView(Form1 form1)
{
this.form1 = form1;
InitializeComponent();
}
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
analyseXML.addToAplicationViewList(e.Node);
}
private void AplicationView_Load(object sender, EventArgs e)
{
this.listView1.View = View.Details;
this.listView1.MultiSelect = false;
this.listView1.HeaderStyle = ColumnHeaderStyle.Clickable;
this.listView1.LabelEdit = false;
this.listView1.FullRowSelect = true;
string txtpath = Registry.findApplicationFile(Registry.SoftName.SAPgui, Registry.ConfigKeyName.TreeConfigFile);
//MessageBox.Show(FileProcess.readContent(a));
analyseXML = new AnalyseXML(FileProcess.readContent(txtpath,Encoding.UTF8),this);
analyseXML.addToAplicationViewTree();
listViewDetailShortcuts = AnalyseKeyValueString.getStruct(FileProcess.readContent(Registry.findApplicationFile(Registry.SoftName.SAPgui, Registry.ConfigKeyName.ShortcutConfigFile),Encoding.GetEncoding("GB2312")));
listViewDetailConnections = AnalyseKeyValueString.getStruct(FileProcess.readContent(Registry.findApplicationFile(Registry.SoftName.SAPgui, Registry.ConfigKeyName.ConnectionConfigFile),Encoding.GetEncoding("GB2312")));
}
//ConnectSAPconnectSAP;
string fileContent;
private void listView1_Click(object sender, EventArgs e)
{
string[] paths =null;
int selectCount = this.listView1.SelectedItems.Count;
if (selectCount > 0)
{
switch(nodeStyle)
{
case NodeStyle.None:
break;
case NodeStyle.Favorites:
break;
case NodeStyle.Shortcuts:
items =listViewDetailShortcuts.First(s => s["-desc"].Equals(this.listView1.SelectedItems[0].SubItems[1].Text));
FileProcess.SubFolderCheckOrCreate(Application.StartupPath, "LoginConfig");
try
{
configfilename = Regex.Replace(items["-desc"], "[/:*?\"<>|]", match => { return "_";}) + this.listView1.SelectedItems[0].SubItems[3].Text;
paths = Directory.GetFiles(Application.StartupPath + "\\LoginConfig", configfilename + "*.sap", SearchOption.TopDirectoryOnly);
}
catch (Exception)
{
}
if (null != paths && paths.Length > 0)
{
edition = Convert.ToInt32(Path.GetFileNameWithoutExtension(paths[0]).Substring(configfilename.Length+ 1)) + 1;
File.Delete(paths[0]);
}
else
{
edition = 1;
}
fileContent = "[System]\r\nName=" + this.listView1.SelectedItems[0].SubItems[2].Text+ "\r\nDescription=" + this.listView1.SelectedItems[0].SubItems[1].Text+ "\r\nClient=" + this.listView1.SelectedItems[0].SubItems[3].Text+ "\r\n[User]\r\nName=" + this.listView1.SelectedItems[0].SubItems[4].Text+ "\r\nLanguage=" + items["-l"] + "\r\nPassword=" + items["-pwenc"] + "\r\n[Function]\r\nTitle=" + configfilename + "_" + edition + "\r\nCommand=" + items["-cmd"] + "\r\n[Configuration]\r\nWorkDir=" + items["-wd"] + "\r\n[Options]\r\nReuse=1\r\n";
FileProcess.CreateFormatplainFile("LoginConfig\\" + configfilename + "_" + edition + ".sap", fileContent);
break;
case NodeStyle.Connections:
break;
default:
break;
}
Process.Start(Application.StartupPath + "\\LoginConfig\\" + configfilename + "_"+ edition + ".sap");
//connectSAP = new ConnectSAP(this.form1);
//connectSAP.ShiLianSystem();
}
}
連接RFC的類
using SAP.Middleware.Connector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WritePlane
{
class ConnectSAP
{
RfcDestination rfcDest;
RfcConfigParameters Parms;
RfcRepository rfcrep;
IRfcFunction func;
string FFMName;
string InputName;
string OutputName;
public class ConnectStruct
{
public ConnectStruct(bool p_isconnect, bool p_isconnectsuccessful, string p_connectinfo)
{
isconnect = p_isconnect;
isconnectsuccessful = p_isconnectsuccessful;
connectinfo = p_connectinfo;
}
public bool isconnect = false;//連接狀態:true正在連接,false連接結束
public bool isconnectsuccessful = false;//是否成功連接,ture成功,false失敗
public string connectinfo = "WithoutConnection";//連接信息
};
ConnectStruct connstru;
delegate void SetTextCallback(ConnectStruct info);//具有一個傳入參數的委託
Func<ConnectStruct>func_sys;
Func<ConnectStruct>func_rfm;
IAsyncResult cookie;
Form1 form1;
public ConnectSAP(Form1form1)
{
this.form1 = form1;
}
public void ShiLianSystem()
{
form1.toolStripStatusLabel1.Text = "Wait...";
connstru = new ConnectStruct(false, false, "WithoutConnection");
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.Name, "ED1");
parms.Add(RfcConfigParameters.AppServerHost, "XX.XX.XX.XXX");
parms.Add(RfcConfigParameters.SystemNumber, "XX");
parms.Add(RfcConfigParameters.Client, "XXX");
parms.Add(RfcConfigParameters.User, "XXXXXXX");
parms.Add(RfcConfigParameters.Password, "XXXXXXXXXX");
parms.Add(RfcConfigParameters.Language, "ZH");
parms.Add(RfcConfigParameters.PoolSize, "1");
//parms.Add(RfcConfigParameters.,"10");
parms.Add(RfcConfigParameters.IdleTimeout, "1");
parms.Add(RfcConfigParameters.MaxPoolWaitTime, "1");
parms.Add(RfcConfigParameters.SAPRouter, "XXXXXXXXXXXXX");
FFMName = "ZZTEST_WEBSERVICE_02";
InputName = "ZZIMSEG";
OutputName = "ZZEMSEG";
this.SetConnectandRFM(parms);
//this.SetRFM("ZZTEST_WEBSERVICE_02");
//RfcDestinationrfcDest;
//rfcDest =RfcDestinationManager.GetDestination(parms);
//RfcRepository rfcrep= rfcDest.Repository;
//IRfcFunction func =rfcrep.CreateFunction("ZPPRFM005");//RFM名稱
//func.SetValue("I_USAGE","1");//SAP裏面的傳入參數
//func.SetValue("I_STATUS","4");
//func.SetValue("I_UNIT","EA");
//func.SetValue("I_ACTION","1");
//func.Invoke(rfcDest);
//IRfcTable IrfTable =func.GetTable("IT_ROUTING"); //提前實例化一個空的表結構出來
//IRfcStructure IrfStru =func.GetStructure("E_MESSAGE"); //提前實例化一個空的表結構出來
//string a=IrfStru.GetString("ID");
//DataTable dt = newDataTable();
//dt.Columns.Add("ID");
//dt.Columns.Add("ZBX");
//dt.Columns.Add("MESSAGE");
////循環把IRfcTable裏面的數據放入Table裏面,因爲類型不同,不可直接使用。
//for (int i = 0; i< IrfStru.Count; i++)
//{
// IrfTable.CurrentIndex = i;
// DataRow dr = dt.NewRow();
// dr["ID"] =IrfStru.GetString("ID");
// dr["ZBX"] =IrfStru.GetString("ZBX");
// dr["MESSAGE"] =IrfStru.GetString("MESSAGE");
// dt.Rows.Add(dr);
//}
////將重新生成的Table賦值給數據控件DataGridView。
//dataGridView1.DataSource= dt;
}
public string ExecuteRFM( string i_param)
{
//先檢查服務器是否連接成功(需要檢查服務器是否真正連接),不成功不允許調用參數
if (connstru.isconnect == false)
{
return "正在連接RFM中,請勿重複點擊,當然我也不會處理!";//正在連接中,直接返回,不讓調用RFC函數
}
else
{
if(connstru.isconnectsuccessful == false)
{
return "連接RFM失敗,請勿重複點擊,當然我也不會處理!";//連接失敗,直接返回,不讓調用RFC函數
}
}
func.SetValue(InputName, i_param);//SAP裏面的傳入參數
func.Invoke(rfcDest);
//returnfunc.GetValue(1).ToString();
return func.GetString(OutputName);
}
//public DataTableGetSAPData(DataTable dt)
//{
// DataTable dtSAPData = new DataTable();
// String str1, str2, str3;
// try
// {
// //向數據庫中添加字段
// dtSAPData.Columns.Add("DATA1", typeof(string));
// dtSAPData.Columns.Add("DATA2", typeof(string));
// dtSAPData.Columns.Add("DATA3", typeof(string));
// string strOrder = null;
// IRfcTable tSAP =func.GetTable("IT_ROUTING");
// for (int i = 0; i < dt.Rows.Count;i++)
// {
// str1 = dt.Rows[i][0].ToString();
// str2 = dt.Rows[i][1].ToString();
// str3 = dt.Rows[i][2].ToString();
// IRfcStructure struSAP =tSAP.Metadata.LineType.CreateStructure();
// struSAP.SetValue("str1",str1);
// struSAP.SetValue("str2",str2);
// struSAP.SetValue("str3",str3);
// tSAP.Append(struSAP);
// }
// func.SetValue("INPUT_TABLE",tSAP); //table 參數
// func.SetValue("WERKS","A"); //單個參數
// func.SetValue("STATUS","B"); //單個參數
// func.Invoke(_rfcDest);
// IRfcTable SAPDataTable =func.GetTable("RETURN_TABLE");
// for (int i = 0; i <SAPDataTable.RowCount; i++)
// {
// IRfcStructure stru =SAPDataTable[i];
// DataRow dr = dtSAPData.NewRow();
// dr["DATA1"] =stru.GetValue("X").ToString();
// dr["DATA2"] =stru.GetValue("Y").ToString();
// dr["DATA3"] =stru.GetValue("Z").ToString();
// dtSAPData.Rows.Add(dr);
// }
// }
// catch (Exception ex)
// {
// string strcatch = ex.Message;
// }
// return dtSAPData;
//}
public void SetRFM(stringname,string inputname,stringoutputname)
{
if (rfcrep == null)//如果沒有創建數據對象倉庫
{
return;
}
if (rfcrep.CachedFunctionMetadata.Count == 0)//如果數據倉庫中沒有添加函數對象
{
return;
}
if (rfcrep.CachedFunctionMetadata.Find(s =>s.Name.Equals(name)) != null)//如果數據倉庫中找到函數名爲name的對象,則不設置函數
{
return;
}
FFMName = name;
InputName = inputname;
OutputName = outputname;
func_rfm = SetRFM;//泛型func<out ConnectStruct>設置了一個返回參數,不能用Acion
cookie = func_rfm.BeginInvoke((IAsyncResult res) => { this.SetLableText(func_rfm.EndInvoke(cookie)); }, null);//回調函數,當異步調用完成之後觸發,第二個參數是傳入參數
}
private ConnectStruct SetRFM()
{
try
{
func = rfcrep.CreateFunction(FFMName);//RFM名稱
}
catch (Exceptionex)
{
return new ConnectStruct(true, false, ex.ToString());
}
return new ConnectStruct(true, true, "ConnectRFM successful!");
}
public void SetConnectandRFM(RfcConfigParameters param)
{
Parms = param;
func_sys = SetConnectandRFM;//泛型func<outstring>設置了一個返回參數,不能用Acion
cookie = func_sys.BeginInvoke((IAsyncResult res) => { this.SetLableText(func_sys.EndInvoke(cookie)); }, null);//回調函數,當異步調用完成之後觸發,第二個參數是傳入參數
}
private ConnectStruct SetConnectandRFM()
{
try
{
rfcDest = RfcDestinationManager.GetDestination(Parms);
rfcrep = rfcDest.Repository;
func = rfcrep.CreateFunction(FFMName);//RFM名稱
}
catch (Exceptionex)
{
return new ConnectStruct(true, false, ex.ToString());
}
return new ConnectStruct(true, true, "Connectsuccessful!");
}
private void SetLableText(ConnectStruct info)
{
// InvokeRequiredrequired compares the thread ID of the
// calling thread tothe thread ID of the creating thread.
// If these threadsare different, it returns true.
if (this.form1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetLableText);
this.form1.Invoke(d,new object[] { info });
}
else
{
this.form1.toolStripStatusLabel1.Text+= "\r\n" + info.connectinfo;
connstru = info;
}
}
}
}