Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
ƯỚ
Ẫ
Ế Ể
Ầ
Ề
2011
ĐỀ
Ớ
- Chào các b ạn, hôm nay ngày 05/08/2011, đang nghỉ phép về nhà buồn quá không có việc gì để làm. Nên làm bài TUT hướ ng ng dẫn anh em newbie sài C# 2008 để giao tiếp với vi điều khiển. - Như chúng ta đã biết đấy chơi vi điề u khiển mà ko giao ti ếp vớ i PC thì chả có gì phải nói cả. Trên các diễn đàn cũng đã bàn luậ n về vấn đề này khá nhiều nhất là với VB6, VC++ … Nhưng vớ i C# thì có l ẽ là hơi ít. Ít ra thì tôi cũng đau đầ u khi mớ i làm vì ko tìm th ấy tài liệu ưng ý. - Có lẻ điểm mạnh của C# chúng ta ko c ần phải nói đến nữa. Bài TUT s ẽ đi sâu vào phần giao tiếp còn phần C# các b ạn nên đọc qua về nó. Nói chung là nó kh ả dễ để tiếp thu so vớ i thằng VB cũ kĩ và VC++ phức tạp. Bài TUT viết cho newbie nên tôi đã rất cố gắng làm cho nó chi ti ết để các bạn dể hiểu hơn. - Trướ c khi vào chúng ta s ẽ điểm qua 1 số nội dung trong TUT này: SerialPort vớ i C# 1 số hiệu ứng (phản ứng) của C#
-
1. Đầu tiên là mở VC# lên, và t ạo 1 project mớ i. i. File / New / Project…
1
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
- Chọn Windows Forms Application và đặt tên cho chúng. Đưa đế n cho chúng ta 1 giao diện của lập trình Form. Gi ống như các bạn làm bằng VB vậy
2. Tiếp đến là chúng ta s ẽ xây dựng giao diện như hình bên dướ i
- Như hình trên cấ u trúc rất đơn giả n chỉ chứ ComboBox, Button và TextBox - COM, BaudRate, Data Bits, Parity, Stop Bit là các ComboBox
2
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
- Kết nối, Ngắt, Xóa, Thoát và SEND là các Button - Còn lại 2 ống trống có viền xung quanh chính là các TextBox - Chúng ta sẽ tiến hành tạo và đặt tên cho chúng. ToolBox chứ các Control.
Ở đây sử dụng Common Control
Button để làm nút nh ấ n Combox để hộp điều khi ể ển chứa các Text
TextBox ch ứ văn bản,
dung để hiện thị dữ liệu nhận và truy ền đi
- Và nhớ phải đặt tên cho chúng sau mỗi lần kéo ra
3
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
Properties : Nơi để thay đổi các thu ộc tính của đối tượng. Tên sẽ được đặt ở đây. Đây chính là tên của đối tượng.
Kéo xu ống bên dướ i,
đặt tên ở Text. Tên này sẽ hiện thị lên Button
Tương tự như vậy vớ i các ph ần còn lại, chúng ta s ẽ xây dựng đượ c giao di ện
4
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
Thêm 1 chút mắm, muối chúng ta s ẽ có giao diện như hình bên dướ i
- Giao diện như vậy là đã xong, phầ n quan trọng chính là code
5
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
- Trước khi đến code ta s ẽ giải thích n ội dung của giao diện cũng như tên củ a các control cho ti ện trong việc tham khảo code. Cái này là lịch
cbCom
txtkq
cho đẹp thôi
Của Menu
cbRate cbBits cbParity cbBit
btKetNoi
txtSend
btNgat
btXoa
btThoat
btSend
Của StatusStrip1
3. Phần CODE using using using using using using using using
System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms; // Thêm 3 em này vào là OK, để sài SerialPort using System.IO; using System.IO.Ports; using System.Xml;
- Chuyển sang phần code bằng cách nhấp phải chọn viewCode, chúng ta s ẽ thêm 2 thư viện IO và xml vào. namespace HIENCLUBVN_RS232 { public partial class Form1 : Form { SerialPort P = new SerialPort SerialPort(); (); // Khai báo 1 Object SerialPort m ới. string InputData = String String.Empty; .Empty; // Khai báo string buff dùng cho hiển thị dữ liệu sau này.
6
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
delegate void SetTextCallback (string text); // Khai bao delegate SetTextCallBack voi tham so string
- Trướ c mắt bạn chỉ quan tâm đến câu lệnh
SerialPort P = new SerialPort SerialPort(); ();
// Khai báo 1 Object SerialPort m ới.
- Bướ c tiếp là chúng ta t ạo dữ liệu cho các comboBox, bướ c này bạn nên copy và dán vào project c ủa mình nhớ thay ớ thay tên các combo cho đú ng vớ i tên mà bạn đặt. public Form1() { InitializeComponent(); // Cài đặt các thông s ố cho COM // Mảng string port để chứ tất cả các cổng COM đang có trên máy tính string[] string [] ports = SerialPort SerialPort.GetPortNames(); .GetPortNames(); // Thêm toàn b ộ các COM đã tìm đượ c vào combox cbCom cbCom.Items.AddRange(ports); // Sử dụng AddRange thay vì dùng foreach P.ReadTimeout = 1000; // Khai báo hàm delegate b ằng phương thứ c DataReceived c ủa Object SerialPort; // Cái này khi có s ự kiện nhận dữ liệu sẽ nhảy đến phương thứ c DataReceive // Nếu ko hiểu đoạn này bạn có th ể tìm hiểu về Delegate, còn ko cứ COPY . Ko c ần quan tâm P.DataReceived += new SerialDataReceivedEventHandler (DataReceive);
// Cài đặt cho BaudRate string[] string [] BaudRate = { "1200" "1200", , "2400" "2400", , "4800" "4800", , "9600" "9600", , "19200" "19200", , "38400", "38400" , "57600", "57600" , "115200" }; cbRate.Items.AddRange(BaudRate);
// Cài đặt cho DataBits string[] Databits = { "6" string[] "6", , "7" "7", , "8" }; cbBits.Items.AddRange(Databits); //Cho Parity string[] string [] Parity = { "None" "None", , "Odd" "Odd", , "Even" }; cbParity.Items.AddRange(Parity); //Cho Stop bit string[] string [] stopbit = { "1" "1", , "1.5" "1.5", , "2" }; cbBit.Items.AddRange(stopbit); // Mấy cái này khá đơn giả n, bạn đừng hỏi vì sao. cho nhanh. :D }
cứ COPY paste
Bạn nên lưu ý là : public Form1() {
7
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
InitializeComponent();
Đã đượ c tạo ra trước đó bở i C#, chúng ta Copy, Paste thì ph ải sau hàm kh ở i tạo InitializeComponent();
Nói chung, cơ bản đến đây là xong phầ n khở i tạo các combox cho SerialPort r ồi. Đến đây bạn có thể cài đặt các thông số cho rs232 bằng tay, nếu làm cái này thì rs232 có thể gọi là Full, và ta có th ể hoàn toàn làm ch ủ nó về những cái nhỏ nhất. R ồi vào đây. Chính là các sự kiện mà 1 Nh ấ p chuột
combo có
vào đây
th ể có.
Đúp chuột vào đây. Lúc đầu nó ko có chữ, đúp vào sẽ có.
Lúc đúp vào ta đã tạ o ra 1 s ự kiện là khi thay đổi các giá tr ị trên combo thì chúng s ẽ đượ c gọi đến hàm phục vụ, và công việc của chúng ta s ẽ là ở đây. ở đây. private void cbCom_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); // Nếu đang mở Port thì ph ải đóng lại } P.PortName = cbCom.SelectedItem.ToString(); // Gán PortName b ằng COM đã chọn }
- Như ví dụ trên là s ẽ gọi đến hàm gán giá tr ị cổng đang chọn cho PortName - Tương tự như vậy chúng ta s ẽ làm cho toàn b ộ comboBox, các b ạn có thể tham khảo code dưới để hiểu rõ hơn. private void cbCom_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) {
8
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
P.Close(); // Nếu đang mở Port thì ph ải đóng lại } P.PortName = cbCom.SelectedItem.ToString(); // Gán PortName b ằng
COM đã chọn } private void cbRate_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } P.BaudRate = Convert Convert.ToInt32(cbRate.Text); .ToInt32(cbRate.Text); } private void cbBits_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } P.DataBits = Convert Convert.ToInt32(cbBits.Text); .ToInt32(cbBits.Text); } private void cbParity_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } // Với thằng Parity hơn lằ ng nhằng. Nhưng cũng OK thôi. ^_^ switch (cbParity.SelectedItem.ToString()) { case "Odd" "Odd": : P.Parity = Parity Parity.Odd; .Odd; break; break ; case "None" "None": : P.Parity = Parity Parity.None; .None; break; break ; case "Even" "Even": : P.Parity = Parity Parity.Even; .Even; break; break ; } } private void cbBit_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } switch (cbBit.SelectedItem.ToString()) { case "1" "1": : P.StopBits = StopBits StopBits.One; .One; break; break ;
9
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
case "1.5": "1.5": P.StopBits = StopBits StopBits.OnePointFive; .OnePointFive; break; break ; case "2" "2": : P.StopBits = StopBits.Two; StopBits .Two; break; break ; } }
Vậy là đã xong. Công việ c khở i tạo đã hoàn tất, còn bây gi ờ là công việc quan tr ọng nhất. Đó chính là xây dự ng các hàm, th ủ tục cho việc nhận và truyền dữ liệu qua COM // Hàm này đượ c sự kiện nhận dử liệu gọi đến. Mục đích để hiển thị thôi private void DataReceive(object DataReceive( object obj, SerialDataReceivedEventArgs e) { InputData = P.ReadExisting(); if (InputData != String String.Empty) .Empty) { // txtIn.Text = InputData; // Ko dùng đc như thế này vì khác threads . SetText(InputData); // Chính vì v ậy phải sử dụng ủy quyền tại
đây. Gọi delegate đã khai báo trước đó. } } // Hàm của em nó là ở đây. Đừng hỏi vì sao l ại thế. private void SetText(string SetText( string text) { if (this this.txtkq.InvokeRequired) .txtkq.InvokeRequired) { SetTextCallback d = new SetTextCallback SetTextCallback(SetText); (SetText); // khởi tạo 1 delegate m ới gọi đến SetText this.Invoke(d, this .Invoke(d, new object object[] [] { text }); } else this this.txtkq.Text .txtkq.Text += text; } // Toàn bộ cái này b ạn nên COPY, nó cũng làm tôi đau đầ u. :D
Đến đây là phầ n nhận đã xong, tiếp đến phần gửi dữ liệu đi. Bạn nhấp đúp vào button SEND, để lấy sự kiện rồi viết hàm theo bên dướ i // Đến hàm gửi data xuống COM private void btSend_Click( object sender, EventArgs e) { if (P.IsOpen) { if (txtSend.Text == "" "") ) MessageBox MessageBox.Show( .Show( "Chưa có dữ liệu!" u!", ,"Thông Báo"); Báo" ); else P.Write(txtSend.Text); } else MessageBox MessageBox.Show( .Show( "COM chưa mở ." .", , "Thông báo", báo" , MessageBoxButtons .OK, MessageBoxIcon .Error); txtSend.Clear(); } // Đến đây coi như mọ i việc đã ngon lành cành đào rồ i.
10
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
// Để chọn tiện trong vi ệc Test thì chúng ta s ẽ làm thêm bướ c nữa. cho mấy cái thông s ố hay dùng đượ c chọn. Ko c ần thiết nếu bạn cảm thấy ko cần.
Đúp chuột vào thanh tiêu đề để l ấ ấ y sự kiện Load. Hàm này s ẽ được gọi ngay khi khởi động. private void Form1_Load(object Form1_Load( object sender, EventArgs e) // sẽ được gọi khi m ở
chương trình. { cbCom.SelectedIndex = 0; // chọn COM được tìm thấy đầu tiên cbRate.SelectedIndex = 3; // 9600 cbBits.SelectedIndex = 2; // 8 cbParity.SelectedIndex = 0; // None cbBit.SelectedIndex = 0; // None // Hiện thị Status cho Pro tí status.Text = "Hãy chọn 1 cổng COM để kết nối." i."; ; }
Bướ c cuối cùng là gi ải quyết 4 button còn l ại để kết nối, ngắt kết nối, thoát kh ỏi chương trình và xóa d ữ liệu củ. Bước này khá đơn giả n bạn chỉ việc nhấp đúp vào từng button để lấy sự kiện, có thể tham khảo code dướ i.i. private void btKetNoi_Click( object sender, EventArgs e) { try { P.Open(); btNgat.Enabled = true true; ; btKetNoi.Enabled = false false; ; // Hiện thị Status status.Text = "Đang kết nối với cổng " + cbCom.SelectedItem.ToString(); } catch (Exception ex) { MessageBox.Show( MessageBox .Show("Không "Không k ết nối được." c.", ,"Thử lại" i", ,MessageBoxButtons .OK,MessageBoxIcon .OK,MessageBoxIcon .Error); } } private void btNgat_Click( object sender, EventArgs e) { P.Close(); btKetNoi.Enabled = true true; ; btNgat.Enabled = false false; ; // Hiện thị Status
11
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
status.Text = "Đã Ngắt Kết Nối" i"; ; } private void btThoat_Click( object sender, EventArgs e) { DialogResult kq = MessageBox MessageBox.Show( .Show("B "Bạn thực sự muốn thoát", thoát" , "HIENCLUBVN", "HIENCLUBVN" , MessageBoxButtons .YesNo, MessageBoxIcon .Question); if (kq == DialogResult DialogResult.Yes) .Yes) { MessageBox.Show( MessageBox .Show("C "Cảm ơn bạn đã sử dụng chương trình", trình" ,"HIENCLUBVN" "HIENCLUBVN"); ); this.Close(); this .Close(); } } private void btXoa_Click(object btXoa_Click( object sender, EventArgs e) { txtkq.Text = "" ""; ; txtSend.Text = "" ""; ; }
Đến đây là mọi việc đã ngon lành cành đào rồ i. Để cá nhân hóa chương trình củ a mình bạn có thể thêm các label như ví dụ là : HỌC VIỆN HẢI QUÂN hay là menu như Tác Giả và 1 số hiệu ứng khác mang tính chuyên nghi ệp để Profectional hơn :D - Bằng cách là thêm 1 project m ớ i: i: Project / Add Windows Form … Và tạo nó theo mẫu là mình ưu ý. Cuố i cùng là thêm hàm s ự kiện khi nhấn vào tác giả như code dướ i.i. private void aboutToolStripMenuItem_Click( object sender, EventArgs e) { Form2 frm = new Form2 Form2(); (); frm.ShowDialog(); }
// Lưu ý : Tác Giả là 1 control c ủa ToolStripMenu b ạn có thể lôi nó ra t ừ Toolbox
12
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
Lưu ý : Nhấn F6 để biên dịch lỗi trong quá trình code và F5 để chạy chương trình. 4. Toàn bộ code để tiện tham khảo using using using using using using using using
System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms; // Thêm 3 em này vào là OK, để sài SerialPort using System.IO; using System.IO.Ports; using System.Xml; // Bắt đầu code namespace HIENCLUBVN_RS232 { public partial class Form1 : Form { SerialPort P = new SerialPort SerialPort(); (); // Khai báo 1 Object SerialPort m ới. string InputData = String String.Empty; .Empty; // Khai báo string buff dùng cho hiển thị dữ liệu sau này. delegate void SetTextCallback (string text); // Khai bao delegate SetTextCallBack voi tham so string public Form1() { InitializeComponent(); // Cài đặt các thông s ố cho COM // Mảng string port để chứ tất cả các cổng COM đang có trên máy tính string[] string [] ports = SerialPort SerialPort.GetPortNames(); .GetPortNames(); // Thêm toàn b ộ các COM đã tìm đượ c vào combox cbCom cbCom.Items.AddRange(ports); // Sử dụng AddRange thay vì dùng foreach P.ReadTimeout = 1000; // Khai báo hàm delegate b ằng phương thứ c DataReceived c ủa Object SerialPort; // Cái này khi có s ự kiện nhận dữ liệu sẽ nhảy đến phương thức DataReceive // Nếu ko hiểu đoạn này bạn có th ể tìm hiểu về Delegate, còn ko cứ COPY . Ko c ần quan tâm
13
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
P.DataReceived += new SerialDataReceivedEventHandler (DataReceive);
// Cài đặt cho BaudRate string[] BaudRate = { "1200" string[] "1200", , "2400" "2400", , "4800" "4800", , "9600" "9600", , "19200" "19200", , "38400", "38400" , "57600" "57600", , "115200" }; cbRate.Items.AddRange(BaudRate);
// Cài đặt cho DataBits string[] Databits = { "6" string[] "6", , "7" "7", , "8" }; cbBits.Items.AddRange(Databits); //Cho Parity string[] string [] Parity = { "None" "None", , "Odd" "Odd", , "Even" }; cbParity.Items.AddRange(Parity); //Cho Stop bit string[] string [] stopbit = { "1" "1", , "1.5" "1.5", , "2" }; cbBit.Items.AddRange(stopbit); // Mấy cái này khá đơn giả n, bạn đừng hỏi vì sao. cứ COPY paste cho nhanh. :D } private void cbCom_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); // Nếu đang mở Port thì ph ải đóng lại } P.PortName = cbCom.SelectedItem.ToString(); // Gán PortName b ằng COM đã chọn } private void cbRate_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } P.BaudRate = Convert Convert.ToInt32(cbRate.Text); .ToInt32(cbRate.Text); } private void cbBits_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } P.DataBits = Convert Convert.ToInt32(cbBits.Text); .ToInt32(cbBits.Text); } private void cbParity_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close();
14
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
} // Với thằng Parity hơn lằ ng nhằng. Nhưng cũng OK thôi. ^_^ switch (cbParity.SelectedItem.ToString()) { case "Odd" "Odd": : P.Parity = Parity Parity.Odd; .Odd; break; break ; case "None" "None": : P.Parity = Parity Parity.None; .None; break; break ; case "Even" "Even": : P.Parity = Parity Parity.Even; .Even; break; break ; } } private void cbBit_SelectedIndexChanged( object sender, EventArgs e) { if (P.IsOpen) { P.Close(); } switch (cbBit.SelectedItem.ToString()) { case "1" "1": : P.StopBits = StopBits StopBits.One; .One; break; break ; case "1.5" "1.5": : P.StopBits = StopBits StopBits.OnePointFive; .OnePointFive; break; break ; case "2" "2": : P.StopBits = StopBits StopBits.Two; .Two; break; break ; } } // Hàm này đượ c sự kiện nhận dử liệu gọi đến. Mục đích để hiển thị thôi private void DataReceive(object DataReceive( object obj, SerialDataReceivedEventArgs e) { InputData = P.ReadExisting(); if (InputData != String String.Empty) .Empty) { // txtIn.Text = InputData; // Ko dùng đc như thế này vì khác threads . SetText(InputData); // Chính vì v ậy phải sử dụng ủy quyền tại
đây. Gọi delegate đã khai báo trước đó. } } // Hàm của em nó là ở đây. Đừng hỏi vì sao l ại thế. private void SetText(string SetText( string text) { if (this this.txtkq.InvokeRequired) .txtkq.InvokeRequired) { SetTextCallback d = new SetTextCallback SetTextCallback(SetText); (SetText); // khởi tạo 1 delegate m ới gọi đến SetText this.Invoke(d, this .Invoke(d, new object object[] [] { text });
15
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
} else this this.txtkq.Text .txtkq.Text += text; } // Toàn bộ cái này b ạn nên COPY, nó cũng làm tôi đau đầ u. :D // Đến hàm gửi data xuống COM private void btSend_Click( object sender, EventArgs e) { if (P.IsOpen) { if (txtSend.Text == "" "") ) MessageBox MessageBox.Show( .Show( "Chưa có dữ liệu!" u!", ,"Thông Báo"); Báo" ); else P.Write(txtSend.Text); } else MessageBox MessageBox.Show( .Show( "COM chưa mở ." .", , "Thông báo", báo" , MessageBoxButtons .OK, MessageBoxIcon .Error); txtSend.Clear(); } // Đến đây coi như mọ i việc đã ngon lành cành đào rồ i. // Để chọn tiện trong vi ệc Test thì chúng ta s ẽ làm thêm bướ c nữa. cho mấy cái thông s ố // hay dùng đượ c chọn. Ko cần thiết nếu bạn cảm thấy ko cần. private void Form1_Load(object Form1_Load( object sender, EventArgs e) // sẽ được gọi khi mở chương trình. { cbCom.SelectedIndex = 0; cbRate.SelectedIndex = 3; // 9600 cbBits.SelectedIndex = 2; // 8 cbParity.SelectedIndex = 0; // None cbBit.SelectedIndex = 0; // None // Hiện thị Status cho Pro tí status.Text = "Hãy chọn 1 cổng COM để kết nối." i."; ; } private void btKetNoi_Click( object sender, EventArgs e) { try { P.Open(); btNgat.Enabled = true true; ; btKetNoi.Enabled = false false; ; // Hiện thị Status status.Text = "Đang kết nối với cổng " + cbCom.SelectedItem.ToString(); } catch (Exception ex) { MessageBox.Show( MessageBox .Show("Không "Không k ết nối được." c.", ,"Thử lại" i", ,MessageBoxButtons .OK,MessageBoxIcon .OK,MessageBoxIcon .Error); } } private void btNgat_Click( object sender, EventArgs e) { P.Close(); btKetNoi.Enabled = true true; ; btNgat.Enabled = false false; ;
16
Ngô Đă ng Hiề n – H ọ c Việ n H ải Quân
2011
// Hiện thị Status status.Text = "Đã Ngắt Kết Nối" i"; ; } private void btThoat_Click( object sender, EventArgs e) { DialogResult kq = MessageBox MessageBox.Show( .Show("B "Bạn thực sự muốn thoát", thoát" , "HIENCLUBVN", "HIENCLUBVN" , MessageBoxButtons .YesNo, MessageBoxIcon .Question); if (kq == DialogResult DialogResult.Yes) .Yes) { MessageBox.Show( MessageBox .Show("C "Cảm ơn bạn đã sử dụng chương trình", trình" ,"HIENCLUBVN" "HIENCLUBVN"); ); this.Close(); this .Close(); } } private void btXoa_Click(object btXoa_Click( object sender, EventArgs e) { txtkq.Text = "" ""; ; txtSend.Text = "" ""; ; } private void aboutToolStripMenuItem_Click( object sender, EventArgs e) { Form2 frm = new Form2 Form2(); (); frm.ShowDialog(); } } }
17