2016-12-05 4 views
0

저는 C#을 처음 사용하고 Form1이라는 간단한 프로젝트를 가지고 있습니다.이 프로젝트는 Form2 (나중에 비밀번호로 보호되는 진단 페이지)을 여는 버튼이 있습니다. openport, sendline, isPortOpen 등과 같은 모든 직렬 포트 방법을 처리 할 수 ​​있다고 생각하면 SerialPortClass을 만들었습니다. 내가 원하는 것은 SerialPortClass의 직렬 포트에서 직렬 문자열을받은 다음이 문자열을 표시하는 것입니다. 텍스트 상자에 Form2. 나는이 사이트 및 다른 사이트의 많은 게시물을 읽은 후 여러 가지 방법으로이를 달성하려고 노력했습니다. 내가 읽은 것부터 BackGroundWorker을 사용하는 것이 가장 좋은 방법입니다. 그래서 예제 Microsoft Thread safe example을 복사하고 Form2에있는 버튼을 사용하여 BackGroundWorker을 사용하여 TextBox에 텍스트를 성공적으로 표시합니다. 내가 SerialPortClass에서 BackGroundWorker를 실행하려고하지만 때 내가 얻을 :C# 텍스트 상자, 다른 클래스, backgroundWorker

예외 발생 : SerialTest.exe에 'System.NullReferenceException' 추가 정보 : 개체 참조가 개체의 인스턴스로 설정되지 않았습니다.

누구나 올바른 방향으로 나를 가리킬 수 있습니까?

using System; 
using System.IO; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.IO.Ports; 
using System.Windows.Forms; 
using System.Collections; 
using System.Threading; 
using System.Reflection; 
using UsbLibrary; 

namespace SerialTest 
{ 
    public class SerialPortClass : Form 
    { 

     private static SerialPortClass instance; 

     private System.IO.Ports.SerialPort serialPort1 = new SerialPort();  // Initilises an instance of COM port 
     private System.IO.Ports.SerialPort serialPort2 = new SerialPort();  // and another 

     Form1 form1; 
     Form2 form2; 

     internal delegate void SerialDataReceivedEventHandlerDelegate(
       object sender, SerialDataReceivedEventArgs e); 

     delegate void SetTextCallback(string text); 
     string InputData = String.Empty; 
     private static SerialPort port; 

     public SerialPortClass() 
     { 
      serialPort1.DataReceived += 
        new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1); 
     } 

     public static readonly SerialPortClass _instance = new SerialPortClass(); 

     public ThreadStart ThreadProcSafe { get; private set; } 

     public bool serialOpen(int port) 
     { 
      if (port == 1) 
      { 
       if (serialPort1.IsOpen) return true; 
       else return false; 
      } 
      else if (port == 2) 
      { 
       if (serialPort2.IsOpen) return true; 
       else return false; 
      } 
      else return false; 
     } 

     public void serialSendString(int port, string command) 
     { 
      if (port == 1) 
      { 
       // If the port is closed, don't try to send a character. 
       if (!serialPort1.IsOpen) return; 

       serialPort1.WriteLine(command); 
      } 
      else if (port == 2) 
      { 
       if (!serialPort2.IsOpen) return; 

       serialPort2.WriteLine(command); 
      } 
      else 
      { 
       MessageBox.Show("Invalid port no"); 
      } 
     } 

     public void serialSendString1(string command) 
     { 
      // If the port is closed, don't try to send a character. 
      if (serialPort1.IsOpen) 
      { 
       serialPort1.WriteLine(command); 
      } 
      else 
      { 
       MessageBox.Show("port not opening at connect.."); 
       return; 
      } 
     } 

     public void serialSendString2(string command) 
     { 
      // If the port is closed, don't try to send a character. 
      if (serialPort2.IsOpen) 
      { 
       serialPort2.WriteLine(command); 
      } 
      else 
      { 
       MessageBox.Show("port not opening at connect.."); 
       return; 
      } 
     } 

     public void Connect() //SerialTest.Form1 form) //string comPortNo, int baud) 
     { 
      serialPort1.PortName = "COM38"; // comPortNo; 
      serialPort1.BaudRate = 9600; // baud; 

      if (serialOpen(1)) 
      { 
       MessageBox.Show("Serial port already open"); 
       return; 
      } 
      try 
      { 
       serialPort1.Open(); 
       serialPort1.NewLine = "\r"; 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.ToString()); 
      } 
      if (serialOpen(1)) 
      { 
       Console.WriteLine("port open"); 
      } 
      else 
      { 
       MessageBox.Show("port not opening at connect.."); 
      } 
     } 

     private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e) 
     { 
      Console.WriteLine("Data recieved"); 
      InputData = serialPort1.ReadExisting(); 
      if (InputData != String.Empty) 
      { 
       SetText(InputData); 
      } 
     } 

     public void SetText(string text) 
     { 
      Console.WriteLine("set text"); 
      form2.backgroundWorker1.RunWorkerAsync(); 
     } 

     public void disconnect() 
     { 
      //serialPort1.PortName = "COM38"; 
      //serialPort1.BaudRate = 9600; 

      if (serialOpen(1)) 
      { 
       serialPort1.Close(); 
       Console.WriteLine("Port closed"); 
      } 
      else 
      { 
       MessageBox.Show("Port not open to close"); 
      } 
     } 

     public class SerialErrorReceivedEventArgs : EventArgs 
     { 
      //Data to pass to the event 
      public string LineData { get; private set; } 

      public SerialErrorReceivedEventArgs(string lineData) 
      { 
       this.LineData = lineData; 
      } 
     } 
    } 
} 

및 형식 2 :

나는 실제로 아직 문자열을 전달하지만, 단지 테스트로 다른 클래스에

전체 SerialPortClass 작업 배경을 시작하려고 해요 알고

using System; 
using System.Threading; 
using UsbLibrary; 
using log4net; 
using SensorTestApp; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
using SerialTest; 
using System.IO.Ports; 


namespace SerialTest 
{ 
    public partial class Form2 : Form 
    { 
     string comPortNo; 

     // This delegate enables asynchronous calls for setting 
     // the text property on a TextBox control. 
     delegate void SetTextCallback(string text); 

     // This thread is used to demonstrate both thread-safe and 
     // unsafe ways to call a Windows Forms control. 
     public Thread demoThread = null; 

     // This BackgroundWorker is used to demonstrate the 
     // preferred way of performing asynchronous operations. 
     public BackgroundWorker backgroundWorker1; 

     //private TextBox tBQuery; 
     private Button setTextUnsafeBtn; 
     private Button setTextSafeBtn; 
     private Button setTextBackgroundWorkerBtn; 

     private System.ComponentModel.IContainer components1 = null; 

     public static Form2 _instance = new Form2(); 


     public Form2() 
     { 
      InitializeComponent(); 

      this.backgroundWorker1 = new BackgroundWorker(); 
      // here you have also to implement the necessary events 
      // this event will define what the worker is actually supposed to do 

      this.backgroundWorker1.DoWork += backgroundWorker1_DoWork; 

      //this.backgroundWorker1.RunWorkerAsync += backgroundWorker1_RunWorkerAsync; 

      // this event will define what the worker will do when finished 
      this.backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; 

      btnRelay1On.Enabled = false; 
      btnRelay2On.Enabled = false; 
      btnRelay3On.Enabled = false; 
      btnRelay4On.Enabled = false; 
      btnRelay5On.Enabled = false; 
      btnRelay1Off.Enabled = false; 
      btnRelay2Off.Enabled = false; 
      btnRelay3Off.Enabled = false; 
      btnRelay4Off.Enabled = false; 
      btnRelay5Off.Enabled = false; 
     } 

     // This event handler creates a thread that calls a 
     // Windows Forms control in a thread-safe way. 
     private void button2_Click(
      object sender, 
      EventArgs e) 
     { 
      this.demoThread = 
       new Thread(new ThreadStart(this.ThreadProcSafe)); 

      this.demoThread.Start(); 
     } 

     // This method is executed on the worker thread and makes 
     // a thread-safe call on the TextBox control. 
     public void ThreadProcSafe() 
     { 
      this.SetText("This text was set safely."); 
     } 

     // This method demonstrates a pattern for making thread-safe 
     // calls on a Windows Forms control. 
     // 
     // If the calling thread is different from the thread that 
     // created the TextBox control, this method creates a 
     // SetTextCallback and calls itself asynchronously using the 
     // Invoke method. 
     // 
     // If the calling thread is the same as the thread that created 
     // the TextBox control, the Text property is set directly. 

     public void AppendText(String text) 
     { 
      if (this.InvokeRequired) 
      { 
       this.Invoke(new Action<string>(AppendText), new object[] { text }); 
       return; 
      } 
      this.richTextBox1.Text += text; 
     } 

     public void SetText(string text) 
     { 
      // InvokeRequired required compares the thread ID of the 
      // calling thread to the thread ID of the creating thread. 
      // If these threads are different, it returns true. 
      if (this.tBQuery.InvokeRequired) 
      { 
       SetTextCallback d = new SetTextCallback(SetText); 
       this.Invoke(d, new object[] { text }); 
       Console.WriteLine("different thread, text callback"); 
      } 
      else 
      { 
       Console.WriteLine("same thread, string: %s", text); 
       this.tBQuery.Text = text; 
      } 
     } 

     // This event handler starts the form's 
     // BackgroundWorker by calling RunWorkerAsync. 
     // 
     // The Text property of the TextBox control is set 
     // when the BackgroundWorker raises the RunWorkerCompleted 
     // event. 
     public void button1_Click(
      object sender, 
      EventArgs e) 
     { 
      this.backgroundWorker1.RunWorkerAsync(); 
     } 

     public void backgroundWorker1_DoWork(object sender, 
      DoWorkEventArgs e) 
     { 
      Console.WriteLine("BackgroundWorker1_Do Work"); 
     } 

     // This event handler sets the Text property of the TextBox 
     // control. It is called on the thread that created the 
     // TextBox control, so the call is thread-safe. 
     // 
     // BackgroundWorker is the preferred way to perform asynchronous 
     // operations. 

     public void backgroundWorker1_RunWorkerCompleted(
      object sender, 
      RunWorkerCompletedEventArgs e) 
     { 
      this.tBQuery.Text = 
       "This text was set safely by BackgroundWorker."; 
     } 

     private void cbComPort_SelectedIndexChanged(object sender, EventArgs e) 
     { 
      comPortNo = cbComPort.Text.ToString(); 
      btnOpenCom.Enabled = true; 
     } 

     private void btnOpenCom_Click(object sender, EventArgs e) 
     { 
      //SerialPortClass.GetInstance().Connect(comPortNo, 9600); 

      try 
      { 
       SerialPortClass._instance.Connect(); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.ToString()); 
      } 

      if (SerialPortClass._instance.serialOpen(1)) 
      { 
       btnOpenCom.Enabled = false; 
       btnCloseCom.Enabled = true; 
       btnRelay1On.Enabled = true; 
       btnRelay2On.Enabled = true; 
       btnRelay3On.Enabled = true; 
       btnRelay4On.Enabled = true; 
       btnRelay5On.Enabled = true; 
       btnRelay1Off.Enabled = true; 
       btnRelay2Off.Enabled = true; 
       btnRelay3Off.Enabled = true; 
       btnRelay4Off.Enabled = true; 
       btnRelay5Off.Enabled = true; 
      } 
      else MessageBox.Show("port not open btnOpenCom"); 
     } 

     private void btnCloseCom_Click(object sender, EventArgs e) 
     { 
      try 
      { 
       SerialPortClass._instance.disconnect(); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.ToString()); 
      } 
      if (!SerialPortClass._instance.serialOpen(1)) 
      { 
       btnOpenCom.Enabled = true; 
       btnCloseCom.Enabled = false; 
       btnRelay1On.Enabled = false; 
       btnRelay2On.Enabled = false; 
       btnRelay3On.Enabled = false; 
       btnRelay4On.Enabled = false; 
       btnRelay5On.Enabled = false; 
       btnRelay1Off.Enabled = false; 
       btnRelay2Off.Enabled = false; 
       btnRelay3Off.Enabled = false; 
       btnRelay4Off.Enabled = false; 
       btnRelay5Off.Enabled = false; 
      } 
     } 

     private void btnRelay1On_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OH1"); 
     } 

     private void btnRelay1Off_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OL1"); 
     } 

     private void btnRelay2On_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OH2"); 
     } 

     private void btnRelay2Off_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OL2"); 
     } 

     private void btnRelay3On_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OH3"); 
     } 

     private void btnRelay3Off_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OL3"); 
     } 

     private void btnRelay4On_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OH4"); 
     } 

     private void btnRelay4Off_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OL4"); 
     } 

     private void btnRelay5On_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OH5"); 
     } 

     private void btnRelay5Off_Click(object sender, EventArgs e) 
     { 
      SerialPortClass._instance.serialSendString(1, "OL5"); 
     } 

     private void btnQuery_Click(object sender, EventArgs e) 
     { 
      //this.BeginInvoke(new SetTextCallback(SetText), new object[] { "hjdfdsfj" }); 
      SerialPortClass._instance.serialSendString(1, "?"); 
      Console.WriteLine("?"); 
     } 

    } 
} 

Designer 파일에는 BackgroundWorker에 대한 참조가 있으므로이 파일도 여기에 포함 시켰습니다.

this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); 

// 
// backgroundWorker1 
// 
this.backgroundWorker1.RunWorkerCompleted += new  System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); 

//private System.ComponentModel.BackgroundWorker backgroundWorker1; 
+0

어디에서 예외가 발생합니까? –

+0

'form2'는 어디에서 초기화합니까? 'backgroundWorker1'은 어디에서 초기화합니까? 어떤 객체가'null'입니까? – David

+0

'backgroundWorker1'에 대한 액션을 설정하지 않았습니다 – BugsFree

답변

1

누락 된 부분은 BackGroundWorker의 초기화입니다. 당신은 생성자에서이 작업을 수행해야합니다

public Form2() 
    { 
     InitializeComponent(); 

     this.backgroundWorker1 = new BackGroundWorker(); 
     // here you have also to implement the necessary events 
     // this event will define what the worker is actually supposed to do 
     this.backgroundWorker1 .DoWork += backgroundWorker1r_DoWork; 
     // this event will define what the worker will do when finished 
     this.backgroundWorker1 .RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted; 

    } 

편집 : 나는 좀 더 명확하게 글을 읽으면서

. 당신이 원하는 경우 :

나는 당신은 당신이 당신의 SerialPortClassForm2의 인스턴스를 가지고 있는지 확인해야합니다 SerialPortClass

에서 BackgroundWorker에 실행하려고. new 키워드 만 사용하는 경우 모니터에 이미 표시된 인스턴스와 다를 수 있습니다.

편집 2 :

좋아 신흥 패턴있을 것 같습니다. 내가 틀렸다면 나를 바로 잡으십시오. 필드가있는 Form1을여십시오.Form1에서 당신은 버튼을 누르면이 버튼은 메소드 호출 인스턴스화 된 적이 있기 때문에

form2.backgroundWorker1.RunWorkerAsync(); 

form2null입니다 : 당신이 선에 와서 분명히 할 때 때문에

sp_class.SetText(); 

지금 당신은 문제가 있다면을 ! 다음을 수행하십시오 : 인스턴스를 생성하고이 같은 SerialPortClass에서 양식을 열 : 이것은 당신이 실제로 실행하자 곳이기 때문에

form2 = new Form2(); 
form2.backgroundWorker1.RunWorkerAsync(); 
form2.Show(); 

는 이제 backgroundWorker1의 이벤트 등록은 여전히 ​​Form2 클래스에 속하는! 당신의 SerialPort 때문에

backgroundWorker1 이미 SeralPortClass의 생성자에를해야 트리거 : 인스턴스는 Form1, Form2SerialPortClass

EDIT 3의 삼각형에 혼동하지 마십시오 Form2 여전히 이 같은 Form2의 예 :

public SerialPortClass() 
{ 
    form2 = new Form2(); 

    serialPort1.DataReceived += 
      new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1); 
} 

하지만 당신은 정말 당신이 서로에 의존하는 것 당신의 3 개 클래스 사이에 매듭을 긴장을 풀고 무엇을해야하는지의 기능을 전달하는 것입니다 이 같은 SerialPortClass의 생성자에 Form2를의 tance :

public SerialPortClass(Form2 f2) 
{ 
    form2 = f2 

    serialPort1.DataReceived += 
      new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1); 
} 

나는 Form1에 당신이 전화 Form2의 인스턴스 및 SetText 전화를 사용하는 SerialPortClass을 것 같아요. 그리고 정확히 거기 당신은 생성자 호출로 인스턴스를 전달해야

SerialPortClass my_sp_class = new SerialPortClass(form2); 

이것은 당신이 문제는 여기에 원하는 형태

+0

Thanks Mong Zhu, 나는 생성자에서 누락 된 초기화를 추가하지 않았고 여전히 예외가 발생합니다. Form2의 인스턴스가 있습니다 : "Form2 form2;" 새 키워드를 어디에 사용합니까? – chasher

+0

@ chasher ** 당신이 ** 추가하지 않았거나 [..] 오류가 계속 발생합니까? 그래서 당신은 아무것도 바꾸지 않았고 오류가 지속됩니까? 'SerialPortClass'에'form2' 인스턴스를 어떻게 가져 옵니까? 전체 수업을 게시 할 수 있습니까? 'SerialPortClass.SetText()'의 호출 사이트를 게시 할 수 있습니까? 이것은 당신의 문제를 이해하는 데 결정적입니다. Form2는'form2.backgroundWorker1.RunWorkerAsync();에서'null'이라고 생각합니다. 맞습니까? 디버거가 당신에게 말할 때, 스텝을 밟을 때 무엇입니까? –

+0

"죄송합니다"가 추가되었습니다.form2 인스턴스 'Form2 form2;' 'SerialPortClass'에 1-2 분 안에 코드를 게시하십시오! – chasher

0

에 텍스트의 표시를 얻을 수 있는지 확인합니다.

form2.backgroundWorker1.RunWorkerAsync(); 

backgroundwoker을 작성했지만 메소드/위임자는 등록하지 않았습니다. 몽 - Zhu의 언급으로

this.backgroundWorker1.DoWork += backgroundWorker1_DoWork; //not found in snippet 
this.backgroundWorker1.RunWorkerCompleted +=backgroundWorker1_RunWorkerCompleted; 

또한, form2의 생성자에서이 문제를 추가하여 할당, 당신은 Form2를이 같이있는 BackgroundWorker를 초기화해야합니다.

this.backgroundWorker1 = new BackGroundWorker(); 

또한 개인적으로 다른 형태의 배경 작업자를 호출하는 것은 좋지 않다고 생각합니다.

+0

감사합니다. Prajwal, 시도했습니다. 너와 몽 주 (Mong Zhu)가 제안한 바 있지만, 배경 노동자라고 부를 때 여전히 예외가 생기면, 내가 원하는 것을 성취 할 수있는 더 좋은 방법은 무엇일까요? – chasher

+0

예외는 무엇입니까? 똑같다? – Prajwal

+0

우리는'SerialPortClass'에서'Form2'를'form2'로 참조했다고 가정했습니다. 가지고 있니? – Prajwal

0

주석을 읽은 후 동일한 form2를 참조하지 않고 새로운 참조를 초기화하지 않고있는 것을 볼 수 있습니다. 즉, 해당 null 참조가 유효하지 않음을 의미합니다.

당신이 코드를

Form2 form2; 

이 단순히 변수를 준비하는 것은 할당하고, 당신이하지 않은 이후로 당신은 객체의 액세스 할 수 없습니다. form2를 참조하려는 경우 액세스 할 수있는 곳에서 전역으로 초기화하고 거기에서 객체를 사용할 수 있습니다.

당신이 더 이상 지침이 필요하면 그냥 :)

0

회신이 BackgroundWorker에의 사용이 완료되지 않은 것 같다. 더 쉽게 사용할 수있는 Designer 또는 코드 숨김으로 사용할 수 있습니다.

디자이너와 함께 사용하려면 backgroundWorker 객체를 구성 요소 트레이로 끌어다 놓습니다. 그런 다음 새로 만든 BackgroundWorker1 개체를 클릭하십시오. 속성 트레이로 가서 이벤트에 대한 번개 모양을 선택하십시오. 3 가지 이벤트가 있습니다. 두 가지 이벤트는 DoWork와 RunWorkerCompleted입니다. 두 가지를 모두 두 번 클릭하여 메서드를 생성합니다.

코드 조각에 DoWork 부분이 누락되어 있고 실제로 BackGroundWorker 객체를 인스턴스화하지 않은 것으로 나타납니다. 흐름은 다음과 같습니다. BackgroundWorker가 선언되고 초기화되었습니다. 비동기 호출을해야 할 경우 .RunWorkerAsync 이벤트를 발생시킵니다. RunWorkerAsync 이벤트가 DoWork 이벤트 핸들러로 이동합니다. 여기서 비동기 적으로 수행하려는 작업의 코드를 배치합니다. 이 DoWork 이벤트가 만료되면 RunWorkercompleted 이벤트가 호출됩니다. 이 RunWorkerCompleted 이벤트가 만료되면 새 스레드도 종료됩니다.

작성한 스레드와 다른 스레드에서 디자이너 구성 요소를 변경하려고하면 (예 : BackgroundWorker의 DoWork 이벤트 처리기 내부에서 단추 텍스트를 설정), .InvokeRequired, 당신이 이미 시작한 것처럼 보입니다.

이 backgroundworker를 form2 외부에서 호출하는 경우 해당 클래스에 form2에 대한 핸들을 제공하고 있는지 확인하십시오. 스 니펫에서 새 인스턴스를 인스턴스화했는지 여부는 명확하지 않습니다. 쉬운 방법은 Serialization 클래스에서 Form2에 대한 개인 필드를 만들고 AttachForm (Form f) 메서드를 호출 한 다음 Form2에서 해당 메서드를 호출하여 this을 매개 변수로 전달하는 것입니다.

+0

고마워, 나는 지금 이해한다. – chasher