mirror of
https://github.com/ClassicDIY/ModbusTool.git
synced 2026-10-18 22:00:29 +08:00
754 lines
24 KiB
C#
754 lines
24 KiB
C#
using System;
|
|
using System.Configuration;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.IO.Ports;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
namespace Modbus.Common
|
|
{
|
|
public partial class BaseForm : Form
|
|
{
|
|
private DisplayFormat _displayFormat = DisplayFormat.Integer;
|
|
private CommunicationMode _communicationMode = CommunicationMode.TCP;
|
|
protected Socket _socket;
|
|
protected readonly ushort[] _registerData;
|
|
private bool _logPaused = false;
|
|
|
|
#region Form
|
|
|
|
public BaseForm()
|
|
{
|
|
InitializeComponent();
|
|
_registerData = new ushort[65600];
|
|
}
|
|
|
|
private void BaseFormLoading(object sender, EventArgs e)
|
|
{
|
|
comboBoxBaudRate.SelectedIndex = 4;
|
|
FillRTUDropDownLists();
|
|
CurrentTab.RegisterData = _registerData;
|
|
if (_registerData == null)
|
|
{
|
|
throw new ApplicationException("Failed to allocate 128k block");
|
|
}
|
|
LoadUserData();
|
|
CurrentTab.DisplayFormat = DisplayFormat;
|
|
RefreshData();
|
|
}
|
|
|
|
private void BaseFormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
SaveUserData();
|
|
}
|
|
|
|
private void FillRTUDropDownLists()
|
|
{
|
|
comboBoxSerialPorts.Items.Clear();
|
|
foreach (var port in SerialPort.GetPortNames())
|
|
{
|
|
comboBoxSerialPorts.Items.Add(port);
|
|
}
|
|
if (comboBoxSerialPorts.Items.Count > 0)
|
|
comboBoxSerialPorts.SelectedIndex = 0;
|
|
comboBoxParity.Items.Clear();
|
|
comboBoxParity.Items.Add(Parity.None.ToString());
|
|
comboBoxParity.Items.Add(Parity.Odd.ToString());
|
|
comboBoxParity.Items.Add(Parity.Even.ToString());
|
|
comboBoxParity.Items.Add(Parity.Mark.ToString());
|
|
comboBoxParity.Items.Add(Parity.Space.ToString());
|
|
}
|
|
|
|
private void LoadUserData()
|
|
{
|
|
CommunicationMode mode;
|
|
if (Enum.TryParse(Properties.Settings.Default.CommunicationMode, out mode))
|
|
CommunicationMode = mode;
|
|
DisplayFormat format;
|
|
if (Enum.TryParse(Properties.Settings.Default.DisplayFormat, out format))
|
|
DisplayFormat = format;
|
|
IPAddress ipAddress;
|
|
if (IPAddress.TryParse(Properties.Settings.Default.IPAddress, out ipAddress))
|
|
IPAddress = ipAddress;
|
|
TCPPort = Properties.Settings.Default.TCPPort;
|
|
PortName = Properties.Settings.Default.PortName;
|
|
Baud = Properties.Settings.Default.Baud;
|
|
Parity = Properties.Settings.Default.Parity;
|
|
StartAddress = Properties.Settings.Default.StartAddress;
|
|
DataLength = Properties.Settings.Default.DataLength;
|
|
SlaveId = Properties.Settings.Default.SlaveId;
|
|
SlaveDelay = Properties.Settings.Default.SlaveDelay;
|
|
DataBits = Properties.Settings.Default.DataBits;
|
|
StopBits = Properties.Settings.Default.StopBits;
|
|
}
|
|
|
|
private void SaveUserData()
|
|
{
|
|
Properties.Settings.Default.CommunicationMode = CommunicationMode.ToString();
|
|
Properties.Settings.Default.IPAddress = IPAddress.ToString();
|
|
Properties.Settings.Default.DisplayFormat = DisplayFormat.ToString();
|
|
Properties.Settings.Default.TCPPort = TCPPort;
|
|
Properties.Settings.Default.PortName = PortName;
|
|
Properties.Settings.Default.Baud = Baud;
|
|
Properties.Settings.Default.Parity = Parity;
|
|
Properties.Settings.Default.StartAddress = StartAddress;
|
|
Properties.Settings.Default.DataLength = DataLength;
|
|
Properties.Settings.Default.SlaveId = SlaveId;
|
|
Properties.Settings.Default.SlaveDelay = SlaveDelay;
|
|
Properties.Settings.Default.DataBits = DataBits;
|
|
Properties.Settings.Default.StopBits = StopBits;
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Import - Export
|
|
|
|
private void ButtonImportClick(object sender, EventArgs e)
|
|
{
|
|
openFileDialog.AddExtension = true;
|
|
openFileDialog.DefaultExt = ".csv";
|
|
openFileDialog.Multiselect = false;
|
|
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
using (var s = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read))
|
|
{
|
|
using (var r = new StreamReader(s))
|
|
{
|
|
var rec = r.ReadToEnd();
|
|
var sets = rec.Split(',');
|
|
var first = true;
|
|
foreach (var s1 in sets)
|
|
{
|
|
DisplayFormat fmt;
|
|
var v = s1.Split(':');
|
|
var address = int.Parse(v[0]);
|
|
ushort data;
|
|
if (v[1].StartsWith("0x", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
fmt = DisplayFormat.Hex;
|
|
var sub = v[1].Substring(2);
|
|
ushort.TryParse(sub, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out data);
|
|
}
|
|
else if (v[1].Length > 6) //must be binary
|
|
{
|
|
fmt = DisplayFormat.Binary;
|
|
data = Convert.ToUInt16(v[1], 2);
|
|
}
|
|
else
|
|
{
|
|
fmt = DisplayFormat.Integer;
|
|
data = Convert.ToUInt16(v[1], 10);
|
|
}
|
|
if (address < _registerData.Length)
|
|
{
|
|
_registerData[address] = data;
|
|
}
|
|
if (first)
|
|
{
|
|
SetFunction(fmt);
|
|
first = false;
|
|
StartAddress = UInt16.Parse(v[0]);
|
|
}
|
|
}
|
|
r.Close();
|
|
DataLength = Convert.ToUInt16(sets.Length);
|
|
// display data
|
|
}
|
|
s.Close();
|
|
}
|
|
RefreshData();
|
|
}
|
|
}
|
|
|
|
public delegate void SetFunctionDelegate(DisplayFormat log);
|
|
|
|
protected void SetFunction(DisplayFormat fmt)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
BeginInvoke(new SetFunctionDelegate(SetFunction), new object[] { fmt });
|
|
return;
|
|
}
|
|
DisplayFormat = fmt;
|
|
switch (fmt)
|
|
{
|
|
case DisplayFormat.Integer:
|
|
radioButtonInteger.Checked = true;
|
|
break;
|
|
case DisplayFormat.Binary:
|
|
radioButtonBinary.Checked = true;
|
|
break;
|
|
case DisplayFormat.Hex:
|
|
radioButtonHex.Checked = true;
|
|
break;
|
|
case DisplayFormat.LED:
|
|
radioButtonLED.Checked = true;
|
|
break;
|
|
case DisplayFormat.FloatReverse:
|
|
radioButtonReverseFloat.Checked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ButtonExportClick(object sender, EventArgs e)
|
|
{
|
|
var startAddress = StartAddress;
|
|
var length = DataLength;
|
|
string suffix = "-";
|
|
switch (DisplayFormat)
|
|
{
|
|
case DisplayFormat.Integer:
|
|
suffix = "_Decimal_";
|
|
break;
|
|
case DisplayFormat.Hex:
|
|
suffix = "_HEX_";
|
|
break;
|
|
case DisplayFormat.Binary:
|
|
suffix = "_Binary_";
|
|
break;
|
|
case DisplayFormat.LED:
|
|
suffix = "_LED_";
|
|
break;
|
|
}
|
|
var filename = "ModbusExport_" + startAddress + suffix + DateTime.Now.ToString("yyyyMMddHHmm") + ".csv";
|
|
saveFileDialog.AddExtension = true;
|
|
saveFileDialog.DefaultExt = ".csv";
|
|
saveFileDialog.FileName = filename;
|
|
saveFileDialog.OverwritePrompt = true;
|
|
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
using (var s = saveFileDialog.OpenFile())
|
|
{
|
|
using (var w = new StreamWriter(s))
|
|
{
|
|
for (var x = 0; x < length; x++)
|
|
{
|
|
w.Write(startAddress++);
|
|
w.Write(':');
|
|
var data = _registerData[StartAddress + x];
|
|
switch (DisplayFormat)
|
|
{
|
|
case DisplayFormat.Integer:
|
|
w.Write(string.Format("{0}", data));
|
|
break;
|
|
case DisplayFormat.Hex:
|
|
w.Write(string.Format("0x{0:x4}", data));
|
|
break;
|
|
case DisplayFormat.Binary:
|
|
case DisplayFormat.LED:
|
|
w.Write(Convert.ToString(data, 2).PadLeft(16, '0'));
|
|
break;
|
|
}
|
|
if (x < length - 1)
|
|
w.Write(',');
|
|
}
|
|
w.Flush();
|
|
w.Close();
|
|
}
|
|
s.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Radion button check handlers
|
|
|
|
private void RadioButtonModeChanged(object sender, EventArgs e)
|
|
{
|
|
SetMode();
|
|
}
|
|
|
|
protected void SetMode()
|
|
{
|
|
if (radioButtonTCP.Checked)
|
|
{
|
|
_communicationMode = CommunicationMode.TCP;
|
|
groupBoxTCP.Enabled = true;
|
|
groupBoxRTU.Enabled = false;
|
|
}
|
|
if (radioButtonRTU.Checked)
|
|
{
|
|
_communicationMode = CommunicationMode.RTU;
|
|
groupBoxTCP.Enabled = false;
|
|
groupBoxRTU.Enabled = true;
|
|
}
|
|
if (radioButtonUDP.Checked)
|
|
{
|
|
_communicationMode = CommunicationMode.UDP;
|
|
groupBoxTCP.Enabled = true;
|
|
groupBoxRTU.Enabled = false;
|
|
}
|
|
}
|
|
|
|
private void RadioButtonDisplayFormatCheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (sender is RadioButton)
|
|
{
|
|
var rb = (RadioButton)sender;
|
|
if (rb.Checked)
|
|
{
|
|
DisplayFormat.TryParse(rb.Tag.ToString(), true, out _displayFormat);
|
|
CurrentTab.DisplayFormat = DisplayFormat;
|
|
RefreshData();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
private ushort _startAddress;
|
|
protected ushort StartAddress
|
|
{
|
|
get
|
|
{
|
|
return _startAddress;
|
|
}
|
|
set
|
|
{
|
|
CurrentTab.StartAddress = value;
|
|
var tab = tabControl1.SelectedTab;
|
|
tab.Text = value.ToString();
|
|
_startAddress = value;
|
|
}
|
|
}
|
|
|
|
private ushort _dataLength;
|
|
private bool showDataLength;
|
|
|
|
protected ushort DataLength
|
|
{
|
|
get
|
|
{
|
|
return _dataLength;
|
|
}
|
|
set
|
|
{
|
|
_dataLength = value;
|
|
CurrentTab.DataLength = value;
|
|
}
|
|
}
|
|
|
|
public bool ShowDataLength
|
|
{
|
|
get => showDataLength;
|
|
set
|
|
{
|
|
showDataLength = value;
|
|
foreach (DataTab tab in tabPage1.Controls)
|
|
{
|
|
tab.ShowDataLength = value;
|
|
}
|
|
foreach (DataTab tab in tabPage2.Controls)
|
|
{
|
|
tab.ShowDataLength = value;
|
|
}
|
|
}
|
|
}
|
|
protected IPAddress IPAddress
|
|
{
|
|
get
|
|
{
|
|
return IPAddress.Parse(txtIP.Text);
|
|
}
|
|
set
|
|
{
|
|
txtIP.Text = value.ToString();
|
|
}
|
|
}
|
|
|
|
protected int TCPPort
|
|
{
|
|
get
|
|
{
|
|
return Int32.Parse(textBoxPort.Text);
|
|
}
|
|
set
|
|
{
|
|
textBoxPort.Text = Convert.ToString(value);
|
|
|
|
}
|
|
}
|
|
|
|
protected byte SlaveId
|
|
{
|
|
get
|
|
{
|
|
return Byte.Parse(textBoxSlaveID.Text);
|
|
}
|
|
set
|
|
{
|
|
textBoxSlaveID.Text = Convert.ToString(value);
|
|
}
|
|
}
|
|
|
|
protected int SlaveDelay
|
|
{
|
|
get
|
|
{
|
|
return int.Parse(textBoxSlaveDelay.Text);
|
|
}
|
|
set
|
|
{
|
|
textBoxSlaveDelay.Text = Convert.ToString(value);
|
|
}
|
|
}
|
|
|
|
protected string PortName
|
|
{
|
|
get
|
|
{
|
|
return comboBoxSerialPorts.Text;
|
|
}
|
|
set
|
|
{
|
|
comboBoxSerialPorts.Text = value;
|
|
}
|
|
}
|
|
|
|
protected int Baud
|
|
{
|
|
get
|
|
{
|
|
return Int32.Parse(comboBoxBaudRate.Text);
|
|
}
|
|
set
|
|
{
|
|
comboBoxBaudRate.SelectedItem = Convert.ToString(value);
|
|
}
|
|
}
|
|
|
|
protected Parity Parity
|
|
{
|
|
get
|
|
{
|
|
var parity = Parity.None;
|
|
if (comboBoxParity.SelectedItem.Equals(Parity.None.ToString()))
|
|
{
|
|
parity = Parity.None;
|
|
}
|
|
else if (comboBoxParity.SelectedItem.Equals(Parity.Odd.ToString()))
|
|
{
|
|
parity = Parity.Odd;
|
|
}
|
|
else if (comboBoxParity.SelectedItem.Equals(Parity.Even.ToString()))
|
|
{
|
|
parity = Parity.Even;
|
|
}
|
|
else if (comboBoxParity.SelectedItem.Equals(Parity.Mark.ToString()))
|
|
{
|
|
parity = Parity.Mark;
|
|
}
|
|
else if (comboBoxParity.SelectedItem.Equals(Parity.Space.ToString()))
|
|
{
|
|
parity = Parity.Space;
|
|
}
|
|
return parity;
|
|
}
|
|
set
|
|
{
|
|
comboBoxParity.SelectedItem = Convert.ToString(value);
|
|
}
|
|
}
|
|
|
|
protected int DataBits
|
|
{
|
|
get
|
|
{
|
|
int bits = 0;
|
|
switch (comboBoxDataBits.SelectedIndex)
|
|
{
|
|
case 0:
|
|
bits = 7;
|
|
break;
|
|
case 1:
|
|
bits = 8;
|
|
break;
|
|
}
|
|
return bits;
|
|
}
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case 7:
|
|
comboBoxDataBits.SelectedIndex = 0;
|
|
break;
|
|
case 8:
|
|
comboBoxDataBits.SelectedIndex = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected StopBits StopBits
|
|
{
|
|
get
|
|
{
|
|
StopBits bits = StopBits.None;
|
|
switch (comboBoxStopBits.SelectedIndex)
|
|
{
|
|
case 0:
|
|
bits = StopBits.None;
|
|
break;
|
|
case 1:
|
|
bits = StopBits.One;
|
|
break;
|
|
case 2:
|
|
bits = StopBits.OnePointFive;
|
|
break;
|
|
case 3:
|
|
bits = StopBits.Two;
|
|
break;
|
|
}
|
|
return bits;
|
|
}
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case StopBits.None:
|
|
comboBoxStopBits.SelectedIndex = 0;
|
|
break;
|
|
case StopBits.One:
|
|
comboBoxStopBits.SelectedIndex = 1;
|
|
break;
|
|
case StopBits.OnePointFive:
|
|
comboBoxStopBits.SelectedIndex = 2;
|
|
break;
|
|
case StopBits.Two:
|
|
comboBoxStopBits.SelectedIndex = 3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected DisplayFormat DisplayFormat
|
|
{
|
|
get { return _displayFormat; }
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case DisplayFormat.LED:
|
|
radioButtonLED.Checked = true;
|
|
break;
|
|
case DisplayFormat.Binary:
|
|
radioButtonBinary.Checked = true;
|
|
break;
|
|
case DisplayFormat.Hex:
|
|
radioButtonHex.Checked = true;
|
|
break;
|
|
case DisplayFormat.Integer:
|
|
radioButtonInteger.Checked = true;
|
|
break;
|
|
case DisplayFormat.FloatReverse:
|
|
radioButtonReverseFloat.Checked = true;
|
|
break;
|
|
}
|
|
_displayFormat = value;
|
|
CurrentTab.DisplayFormat = DisplayFormat;
|
|
RefreshData();
|
|
}
|
|
}
|
|
|
|
protected CommunicationMode CommunicationMode
|
|
{
|
|
get { return _communicationMode; }
|
|
set
|
|
{
|
|
switch (value)
|
|
{
|
|
case CommunicationMode.TCP:
|
|
radioButtonTCP.Checked = true;
|
|
break;
|
|
case CommunicationMode.UDP:
|
|
radioButtonUDP.Checked = true;
|
|
break;
|
|
case CommunicationMode.RTU:
|
|
radioButtonRTU.Checked = true;
|
|
break;
|
|
}
|
|
_communicationMode = value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Logging
|
|
|
|
public delegate void AppendLogDelegate(String log);
|
|
|
|
protected void ButtonClearLogClick(object sender, EventArgs e)
|
|
{
|
|
listBoxCommLog.Items.Clear();
|
|
}
|
|
|
|
private void buttonPauseLog_Click(object sender, EventArgs e)
|
|
{
|
|
_logPaused = !_logPaused;
|
|
buttonPauseLog.Text = _logPaused ? "Resume" : "Pause";
|
|
}
|
|
|
|
protected void DriverIncommingData(byte[] data, int len)
|
|
{
|
|
if (_logPaused)
|
|
return;
|
|
var hex = new StringBuilder(len);
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
hex.AppendFormat("{0:x2} ", data[i]);
|
|
}
|
|
AppendLog(String.Format("RX: {0}", hex));
|
|
}
|
|
|
|
protected void DriverOutgoingData(byte[] data)
|
|
{
|
|
if (_logPaused)
|
|
return;
|
|
var hex = new StringBuilder(data.Length * 2);
|
|
foreach (byte b in data)
|
|
hex.AppendFormat("{0:x2} ", b);
|
|
AppendLog(String.Format("TX: {0}", hex));
|
|
}
|
|
|
|
protected void AppendLog(String log)
|
|
{
|
|
if (_logPaused)
|
|
return;
|
|
if (InvokeRequired)
|
|
{
|
|
BeginInvoke(new AppendLogDelegate(AppendLog), new object[] { log });
|
|
return;
|
|
}
|
|
var now = DateTime.Now;
|
|
var tmpStr = ">" + now.ToLongTimeString() + ": " + log;
|
|
listBoxCommLog.Items.Add(tmpStr);
|
|
listBoxCommLog.SelectedIndex = listBoxCommLog.Items.Count - 1;
|
|
listBoxCommLog.SelectedIndex = -1;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Data Table
|
|
|
|
protected void ButtonDataClearClick(object sender, EventArgs e)
|
|
{
|
|
ClearRegisterData();
|
|
}
|
|
|
|
protected void ClearRegisterData()
|
|
{
|
|
for (int i = 0; i < _registerData.Length; i++)
|
|
{
|
|
_registerData[i] = 0;
|
|
}
|
|
RefreshData();
|
|
}
|
|
|
|
protected DataTab CurrentTab
|
|
{
|
|
get
|
|
{
|
|
var tab = tabControl1.SelectedTab;
|
|
return (DataTab)tab.Controls[0];
|
|
}
|
|
}
|
|
|
|
public void RefreshData()
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
BeginInvoke(new Action(RefreshData));
|
|
return;
|
|
}
|
|
CurrentTab.RefreshData();
|
|
|
|
// Reset event handler
|
|
CurrentTab.OnApply -= dataTab_OnApply;
|
|
CurrentTab.OnApply += dataTab_OnApply;
|
|
}
|
|
|
|
public void UpdateDataTable()
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
BeginInvoke(new Action(UpdateDataTable));
|
|
return;
|
|
}
|
|
CurrentTab.UpdateDataTable();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void tabControl1_Selected(object sender, TabControlEventArgs e)
|
|
{
|
|
CurrentTab.RegisterData = _registerData;
|
|
CurrentTab.DisplayFormat = DisplayFormat;
|
|
var tab = tabControl1.SelectedTab;
|
|
if (tab.Text.Equals("...") && tabControl1.TabPages.Count < 20)
|
|
{
|
|
DataTab dataTab = new DataTab();
|
|
dataTab.DataLength = 256;
|
|
dataTab.DisplayFormat = DisplayFormat.Integer;
|
|
dataTab.Location = new Point(3, 3);
|
|
dataTab.Name = "dataTab" + (tabControl1.TabPages.Count + 1);
|
|
dataTab.RegisterData = _registerData;
|
|
dataTab.ShowDataLength = ShowDataLength;
|
|
dataTab.Size = new Size(839, 406);
|
|
dataTab.StartAddress = 0;
|
|
dataTab.TabIndex = 0;
|
|
dataTab.OnApply += dataTab_OnApply;
|
|
TabPage tabPage = new TabPage();
|
|
tabPage.Controls.Add(dataTab);
|
|
tabPage.Location = new Point(4, 22);
|
|
tabPage.Name = "tabPage" + (tabControl1.TabPages.Count + 1);
|
|
tabPage.Padding = new Padding(3);
|
|
tabPage.Size = new Size(851, 411);
|
|
tabPage.TabIndex = tabControl1.TabPages.Count;
|
|
tabPage.Text = "...";
|
|
tabPage.UseVisualStyleBackColor = true;
|
|
tabControl1.Controls.Add(tabPage);
|
|
}
|
|
var address = CurrentTab.StartAddress;
|
|
tab.Text = address.ToString();
|
|
_startAddress = address;
|
|
_dataLength = CurrentTab.DataLength;
|
|
}
|
|
|
|
void dataTab_OnApply(object sender, EventArgs e)
|
|
{
|
|
var tab = tabControl1.SelectedTab;
|
|
var address = CurrentTab.StartAddress;
|
|
tab.Text = address.ToString();
|
|
_startAddress = address;
|
|
_dataLength = CurrentTab.DataLength;
|
|
}
|
|
|
|
private void donate_Click(object sender, EventArgs e)
|
|
{
|
|
string url = "";
|
|
|
|
string business = "graham.a.ross@gmail.com";
|
|
string description = "Support%20ModbusTool%20development";
|
|
string currency = "USD";
|
|
|
|
url += "https://www.paypal.com/cgi-bin/webscr" +
|
|
"?cmd=" + "_donations" +
|
|
"&business=" + business +
|
|
"&item_name=" + description +
|
|
"¤cy_code=" + currency +
|
|
"&source=url";
|
|
|
|
System.Diagnostics.Process.Start(url);
|
|
}
|
|
}
|
|
}
|