3

I have a tricky situation and was wondering if anyone could shed any light on the matter:

I have a blocking collection action worker that is called like this

 ultraHash hash = new ultraHash(lblFolder.Text, (Action) (() => {
       textBox1.Text = self.getMD5();
 });
 runQueue.addAction(hash);

now the "self" thing is there is pseudo code, and this is why, this is the worker (where runQueue is the BlockingCollection container class)

class ultraHash
{
    string _filePath;
    Action _onComplete;
    string _md5;
    public ultraHash(string filePath)
    {
        _filePath = filePath;
    }

    public ultraHash(string filePath, Action onComplete) : this(filePath) //constructor chaining
    {
        _onComplete = onComplete;
    }

    public override void action()
    {
        using (var md5 = MD5.Create())
        {
            try
            {
                using (var stream = File.OpenRead(_filePath))
                {
                    _md5 = MakeHashString(md5.ComputeHash(stream));
                    if (_onComplete != null) _onComplete();
                }
            }
            catch (IOException)
            {
                /***
                 * file is Busy
                 */
            }
        }
    }

    public string getMD5()
    {
        return _md5;
    }

    private string MakeHashString(byte[] hashBytes)
    {
        StringBuilder hash = new StringBuilder(32);
        foreach (byte b in hashBytes)
        {
            hash.Append(b.ToString("X2").ToLower());
        }
        return hashBytes.ToString();
    }
}

now what I want to happen is for the anonymous method Action (() => {} in the first snippet to execute after the queue has completed and be able to contain the MD5 sum. I know there is thread safety issues here, so I understand I have to invoke this back to the parent thread where textBox1 resides, but at the moment I am just lost as to how to do this (is it even possible?)

EDIT

I don't normally "edit" answers but in case anyone else hits this issue this is the code I eventually used, thanks to the answer from usr. Notice the BeginInvoke

  ultraHash hash = null;
  hash = new ultraHash(lblFolder.Text, (Action) (() => {
       this.BeginInvoke((Action) (() => {
            txtMain.Text = hash.getMD5();
        }));
  }));
  runQueue.addAction(hash);

also, if you are wondering why I did this, it is merely so that I can "watch" a folder for any file changes and then store meta data changes, since entire folders can be dragged in/out there needs to be a queueing mechanism in a FIFO queue, also the parent folders are hashed too, so any file alterations need to bubble up, lastly it may be that a file is locked when it is trying to be hashed in which case a FIFO queue can wait

Mr Heelis
  • 2,370
  • 4
  • 24
  • 34
  • Assume there are many `ultraHash` objects in the queue, why not wait all of them completed and take the desired md5 hash from someone? Not sure I understand why to create those actions for each if only one action at the end required. – Artyom May 18 '15 at 16:22
  • the onComplete will actually update a data abstraction layer and a database record, and the ultraHasher will do three things: hash files, rehash the parent folders of that file and manage to wait for files that are locked, placing pending flags all over the place – Mr Heelis May 19 '15 at 09:44

1 Answers1

1

Probably, you should pass the hash as an argument to your onComplete function. getMD5 does not need to exist.

If you insists on doing it just this way you need a little initialization dance:

ultraHash hash = null;
hash = new ultraHash(lblFolder.Text, (Action) (() => {
           textBox1.Text = hash.getMD5();
     });

The whole design of the ultraHash seems strange. Is it really necessary for that class to exist? A single static method that computes the hash should be enough.

usr
  • 168,620
  • 35
  • 240
  • 369
  • thanks @usr however just to be clear: `ultraHasher` is a `worker` in a FIFO queue. It is running in a different thread, the whole point of this new code is to get away from what you describe – Mr Heelis May 19 '15 at 09:42
  • weird, actually I see what you are doing, it is so subtle I rather ignored the significance of it at first, how come `hash` doesn't work unless it is initialised? – Mr Heelis May 19 '15 at 09:57
  • Because you can't use an uninitialized variable. The C# language is defined that way for safety reasons. – usr May 19 '15 at 10:12
  • no, I mean why does the scope not notice it, never mind – Mr Heelis May 19 '15 at 11:07
  • 1
    The right hand side is evaluated before the assignment. That could cause the RHS to use an uninitialized variable in the compilers mind. – usr May 19 '15 at 11:09