Search code examples
c#multithreadinginterruptblocking

Return data while executing blocking method


I can't think of a way to do something like this: I have a C# application interfacing with another device using serial port, and this device can sometimes have interrupts which does block the execution on itself, but once it has been fixed the operation keeps continuing until it finishes.

For example think of a printer, a 100 paged job starts to print on the printer, but there's 70 paper inside the printer, so the printer prints 70 pages and then warns the user via the PC to put more papers in the tray. after user puts paper inside it, the printing continues and after the printout finishes, the printer sends the result to the PC.

Thats the situation but happening faster than a printer. How can I code something like this? For example:

private void StartPrinting()
{    
    boolean bIsPrinted = SendPrinterDocument("p100.pdf");
}

private void ShowPaperOutError()
{
    MessageBox.Show("Paper is out. Please load some papers");
}

If you have only this SendPrinterDocument() method, and it blocks the execution of the application, like it's on a DLL library, how would you trigger the ShowPaperOutError() message?

FYI, I have access to all codes, the PC software (C#), the DLL (C#) and the device (C) and can compile them.


Solution

  • You could do this by having SendPrinterDocument return you an object which raises appropriate events, and perhaps expose appropriate methods to do what you needs.

    I'll use (semi-)pseudo code, as I cant see all the code you say you have access to and can change - I assume you will be able to implement this idea.

    If SendPrinterDocument returned immediatly with an object which implemented this example interface:

    public interface ISendPrinterDocumentResponse
    {
        event EventHandler PrintingFinished;
        event EventHandler<PrintingInterruptedEventArgs> PrintingInterrupted;
        void Retry();
    }
    
    public class PrintingInterruptedEventArgs: EventArgs
    {
         public string Reason { get;set; }
    }
    

    You would be able to use it such as:

    var response = SendPrinterDocument("p100.pdf");
    response.PrintingFinished = (s,ea) => MessageBox.Show("Finished");
    response.PrintingInterrupted = (s,ea) => {
        MessageBox.Show("Printing stopped, hit ok to retry (reason:" + ea.Reason + ")");
        response.Retry();
    };
    

    Edit, based on comments:

    The idea above breaks down quite quickly if your actual functionality is quick enough that it finishes before you have actually assigned the event handlers, in this case, you probably want a second parameter for your function, which provides some "callbacks" which get called when relevant events occur. You can use the same sort of setup:

    public void SendPrinterDocument(string documentName, ISendPrinterEvents events);
    {
         // when interrupted
         if(events.PrintingInterrupted != null)
         {
               events.PrintingInterrupted(this, new PrintingInterruptedEventArgs{Reason = "Out of paper"});
         }
    }