source: trunk/CrypPlugins/KeySearcher/KeySearcher.cs @ 1036

Last change on this file since 1036 was 1036, checked in by kopal, 12 years ago
  • Quick Watch Representation of the KeySearcher can now be viewed as Presentation for KeySearcher too
  • BooleanBinaryOperators now have a Configuration for its "Flags"
File size: 29.1 KB
Line 
1using System;
2using System.Linq;
3using System.Text;
4using Cryptool.PluginBase.Analysis;
5using Cryptool.PluginBase;
6using System.Windows.Controls;
7using System.ComponentModel;
8using Cryptool.PluginBase.Control;
9using System.Collections;
10using System.Collections.Generic;
11using System.Threading;
12using System.Windows.Threading;
13using Cryptool.PluginBase.Miscellaneous;
14using System.IO;
15
16namespace KeySearcher
17{   
18    [Author("Sven Rech, Nils Kopal, Raoul Falk, Dennis Nolte", "rech@cryptool.org", "Uni Duisburg-Essen", "http://www.uni-due.de")]
19    [PluginInfo(true, "KeySearcher", "Bruteforces a decryption algorithm.", "KeySearcher/DetailedDescription/Description.xaml", "KeySearcher/Images/icon.png")]
20    public class KeySearcher : IAnalysisMisc
21    {
22        /// <summary>
23        /// used for creating the TopList
24        /// </summary>
25        private Queue valuequeue;
26        private double value_threshold;
27        /// <summary>
28        /// the thread with the most keys left
29        /// </summary>
30        private int maxThread;
31        private Mutex maxThreadMutex = new Mutex();
32
33        private KeyPattern pattern = null;
34        public KeyPattern Pattern
35        {
36            get
37            {
38                return pattern;
39            }
40            set
41            {
42                pattern = value;
43                if ((settings.Key == null) || ((settings.Key != null) && !pattern.testWildcardKey(settings.Key)))
44                    settings.Key = pattern.giveInputPattern();
45            }
46        }
47
48        private bool stop;
49
50        #region IControlEncryption Members
51
52        private IControlEncryption controlMaster;
53        [PropertyInfo(Direction.ControlMaster, "Control Master", "Used for bruteforcing", "", DisplayLevel.Beginner)]
54        public IControlEncryption ControlMaster
55        {
56            get { return controlMaster; }
57            set
58            {
59                if (controlMaster != null)
60                {
61                    controlMaster.keyPatternChanged -= keyPatternChanged;
62                    controlMaster.OnStatusChanged -= onStatusChanged;
63                }
64                if (value != null)
65                {
66                    Pattern = new KeyPattern(value.getKeyPattern());
67                    value.keyPatternChanged += keyPatternChanged;
68                    value.OnStatusChanged += onStatusChanged;
69                    controlMaster = value;
70                    OnPropertyChanged("ControlMaster");
71
72                }
73                else
74                    controlMaster = null;
75            }
76        }
77
78        #endregion
79
80        #region IControlCost Members
81
82        private IControlCost costMaster;
83        [PropertyInfo(Direction.ControlMaster, "Cost Master", "Used for cost calculation", "", DisplayLevel.Beginner)]
84        public IControlCost CostMaster
85        {
86            get { return costMaster; }
87            set
88            {
89                costMaster = value;
90            }
91        }
92
93        #endregion
94
95        #region IPlugin Members
96
97        public event StatusChangedEventHandler OnPluginStatusChanged;
98
99        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
100
101        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
102
103        private KeySearcherSettings settings;
104
105        public KeySearcher()
106        {
107            settings = new KeySearcherSettings(this);
108            QuickWatchPresentation = new KeySearcherQuickWatchPresentation();
109        }
110
111        public ISettings Settings
112        {
113            get { return settings; }
114        }
115
116        public UserControl Presentation
117        {
118            get { return QuickWatchPresentation; }
119        }
120
121        public UserControl QuickWatchPresentation
122        {
123            get;
124            private set;
125        }
126
127        public void PreExecution()
128        {
129        }
130
131        public void Execute()
132        {
133        }
134
135        public void PostExecution()
136        {
137        }
138
139        public void Pause()
140        {
141        }
142
143        public void Stop()
144        {
145            stop = true;
146        }
147
148        public void Initialize()
149        {
150        }
151
152        public void Dispose()
153        {
154        }
155
156        #endregion
157
158        #region INotifyPropertyChanged Members
159
160        public event PropertyChangedEventHandler PropertyChanged;
161
162        public void OnPropertyChanged(string name)
163        {
164            if (PropertyChanged != null)
165            {
166                PropertyChanged(this, new PropertyChangedEventArgs(name));
167            }
168        }
169
170        #endregion
171
172        /* BEGIN functionality */
173
174        #region whole KeySearcher functionality
175
176        private class ThreadStackElement
177        {
178            public AutoResetEvent ev;
179            public int threadid;
180        }
181
182        #region code for the worker threads
183
184        private void KeySearcherJob(object param)
185        {
186            object[] parameters = (object[])param;
187            KeyPattern[] patterns = (KeyPattern[])parameters[0];
188            int threadid = (int)parameters[1];
189            BigInteger[] doneKeysArray = (BigInteger[])parameters[2];
190            BigInteger[] keycounterArray = (BigInteger[])parameters[3];
191            BigInteger[] keysLeft = (BigInteger[])parameters[4];
192            IControlEncryption sender = (IControlEncryption)parameters[5];
193            int bytesToUse = (int)parameters[6];
194            Stack threadStack = (Stack)parameters[7];
195
196            KeyPattern pattern = patterns[threadid];
197
198            bool useKeyblocks = true;
199
200            try
201            {
202                while (pattern != null)
203                {
204                    BigInteger size = pattern.size();
205                    keysLeft[threadid] = size;
206                    int nextWildcard;
207
208                    do
209                    {
210                        //if we are the thread with most keys left, we have to share them:
211                        if (maxThread == threadid && threadStack.Count != 0)
212                        {
213                            maxThreadMutex.WaitOne();
214                            if (maxThread == threadid && threadStack.Count != 0)
215                            {
216                                try
217                                {
218                                    KeyPattern[] split = pattern.split();
219                                    patterns[threadid] = split[0];
220                                    pattern = split[0];
221                                    ThreadStackElement elem = (ThreadStackElement)threadStack.Pop();
222                                    patterns[elem.threadid] = split[1];
223                                    elem.ev.Set();    //wake the other thread up                                   
224                                    size = pattern.size();
225                                    keysLeft[threadid] = size;
226                                }
227                                catch (Exception e)
228                                {
229                                    //pattern can't be split? who cares :)
230                                }
231                                maxThread = -1;
232                            }
233                            maxThreadMutex.ReleaseMutex();
234                        }
235
236
237                        ValueKey valueKey = new ValueKey();
238                        int blocksize = 0;
239                        nextWildcard = -3;
240                        try
241                        {
242                            string key = "";
243                            if (useKeyblocks)
244                                key = pattern.getKeyBlock(ref blocksize, ref nextWildcard);
245                            if (key == null)
246                                useKeyblocks = false;
247                            if (!useKeyblocks)
248                                key = pattern.getKey();
249                            valueKey.key = key;
250                        }
251                        catch (Exception ex)
252                        {
253                            GuiLogMessage("Could not get next key: " + ex.Message, NotificationLevel.Error);
254                            return;
255                        }
256
257                        int[] arrayPointers = null;
258                        int[] arraySuccessors = null;
259                        int[] arrayUppers = null;
260                        byte[] keya = ControlMaster.getKeyFromString(valueKey.key, ref arrayPointers, ref arraySuccessors, ref arrayUppers);
261                        if (keya == null)
262                        {
263                            useKeyblocks = false;
264                            nextWildcard = -2;
265                            continue;   //try again
266                        }
267
268                        if (arrayPointers == null)  //decrypt only one key
269                        {
270                            if (!decryptAndCalculate(sender, bytesToUse, ref valueKey, keya, 0, null))
271                                return;
272                            doneKeysArray[threadid]++;
273                            keycounterArray[threadid]++;
274                            keysLeft[threadid]--;
275                        }
276                        else  //decrypt several keys
277                        {
278                            int counter = 0;
279                            if (!bruteforceBlock(sender, bytesToUse, ref valueKey, keya, arrayPointers, arraySuccessors, arrayUppers, 0, ref counter, pattern))
280                                return;
281                            doneKeysArray[threadid] += blocksize;
282                            keycounterArray[threadid] += blocksize;
283                            keysLeft[threadid] -= blocksize;
284                        }
285                    } while (pattern.nextKey(nextWildcard) && !stop);
286
287                    if (stop)
288                        return;
289
290                    //Let's wait until another thread is willing to share with us:
291                    pattern = null;
292                    ThreadStackElement el = new ThreadStackElement();
293                    el.ev = new AutoResetEvent(false);
294                    el.threadid = threadid;
295                    patterns[threadid] = null;
296                    threadStack.Push(el);
297                    GuiLogMessage("Thread waiting for new keys.", NotificationLevel.Debug);
298                    el.ev.WaitOne();
299                    GuiLogMessage("Thread waking up with new keys.", NotificationLevel.Debug);
300                    pattern = patterns[threadid];
301                }
302            }
303            finally
304            {
305                sender.Dispose();
306            }
307        }
308
309        #region bruteforce methods
310
311        private bool bruteforceBlock(IControlEncryption sender, int bytesToUse, ref ValueKey valueKey, byte[] keya, int[] arrayPointers,
312            int[] arraySuccessors, int[] arrayUppers, int arrayPointer, ref int counter, KeyPattern pattern)
313        {
314            byte store = keya[arrayPointers[arrayPointer]];
315            while (!stop)
316            {
317                if (arrayPointer + 1 < arrayPointers.Length && arrayPointers[arrayPointer + 1] != -1)
318                {
319                    if (!bruteforceBlock(sender, bytesToUse, ref valueKey, keya, arrayPointers, arraySuccessors, arrayUppers, arrayPointer + 1, ref counter, pattern))
320                        return false;
321                }
322                else
323                {
324                    if (!decryptAndCalculate(sender, bytesToUse, ref valueKey, keya, counter, pattern))
325                        return false;
326                }
327
328                if (keya[arrayPointers[arrayPointer]] + arraySuccessors[arrayPointer] <= arrayUppers[arrayPointer])
329                {
330                    keya[arrayPointers[arrayPointer]] += (byte)arraySuccessors[arrayPointer];
331                    counter++;
332                }
333                else
334                    break;
335            }
336            keya[arrayPointers[arrayPointer]] = store;
337            if (stop)
338                return false;
339            return true;
340        }
341
342        private bool decryptAndCalculate(IControlEncryption sender, int bytesToUse, ref ValueKey valueKey, byte[] keya, int counter, KeyPattern pattern)
343        {
344            try
345            {
346                valueKey.decryption = sender.Decrypt(keya, bytesToUse);
347            }
348            catch (Exception ex)
349            {
350                GuiLogMessage("Decryption is not possible: " + ex.Message, NotificationLevel.Error);
351                GuiLogMessage("Stack Trace: " + ex.StackTrace, NotificationLevel.Error);
352                return false;
353            }
354
355            try
356            {
357                valueKey.value = CostMaster.calculateCost(valueKey.decryption);
358            }
359            catch (Exception ex)
360            {
361                GuiLogMessage("Cost calculation is not possible: " + ex.Message, NotificationLevel.Error);
362                return false;
363            }
364
365            if (this.costMaster.getRelationOperator() == RelationOperator.LargerThen)
366            {
367                if (valueKey.value > value_threshold)
368                {
369                    if (pattern != null)
370                        valueKey.key = pattern.getKey(counter);
371                    valuequeue.Enqueue(valueKey);
372                }
373            }
374            else
375            {
376                if (valueKey.value < value_threshold)
377                {
378                    if (pattern != null)
379                        valueKey.key = pattern.getKey(counter);
380                    valuequeue.Enqueue(valueKey);
381                }
382            }
383            return true;
384        }
385
386        #endregion
387
388        #endregion
389
390        public void process(IControlEncryption sender)
391        {
392            if (sender == null || costMaster == null)
393                return;
394            if (!Pattern.testWildcardKey(settings.Key))
395            {
396                GuiLogMessage("Wrong key pattern!", NotificationLevel.Error);
397                return;
398            }
399            Pattern.WildcardKey = settings.Key;
400            bruteforcePattern(Pattern, sender);
401        }
402
403        // modified by Christian Arnold 2009.12.07 - return type LinkedList (top10List)
404        // main entry point to the KeySearcher
405        private LinkedList<ValueKey> bruteforcePattern(KeyPattern pattern, IControlEncryption sender)
406        {
407            int maxInList = 10;
408            LinkedList<ValueKey> costList = new LinkedList<ValueKey>();
409            fillListWithDummies(maxInList, costList);
410
411            stop = false;
412            if (!pattern.testWildcardKey(settings.Key))
413            {
414                GuiLogMessage("Wrong key pattern!", NotificationLevel.Error);
415                return null;
416            }
417
418            int bytesToUse = 0;
419
420            try
421            {
422                bytesToUse = CostMaster.getBytesToUse();
423            }
424            catch (Exception ex)
425            {
426                GuiLogMessage("Bytes used not valid: " + ex.Message, NotificationLevel.Error);
427                return null;
428            }
429
430            BigInteger size = pattern.size();
431            KeyPattern[] patterns = splitPatternForThreads(pattern);
432
433            valuequeue = Queue.Synchronized(new Queue());
434
435            BigInteger[] doneKeysA = new BigInteger[patterns.Length];
436            BigInteger[] keycounters = new BigInteger[patterns.Length];
437            BigInteger[] keysleft = new BigInteger[patterns.Length];
438            Stack threadStack = Stack.Synchronized(new Stack());
439            startThreads(sender, bytesToUse, patterns, doneKeysA, keycounters, keysleft, threadStack);
440
441            //update message:
442            while (!stop)
443            {
444                Thread.Sleep(1000);
445
446                updateToplist(costList);
447
448                #region calculate global counters from local counters
449                BigInteger keycounter = 0;
450                BigInteger doneKeys = 0;
451                foreach (BigInteger dk in doneKeysA)
452                    doneKeys += dk;
453                foreach (BigInteger kc in keycounters)
454                    keycounter += kc;
455                #endregion
456
457                if (keycounter > size)
458                    GuiLogMessage("There must be an error, because we bruteforced too much keys...", NotificationLevel.Error);
459
460                #region determination of the thread with most keys
461                if (size - keycounter > 1000)
462                {
463                    maxThreadMutex.WaitOne();
464                    BigInteger max = 0;
465                    int id = -1;
466                    for (int i = 0; i < patterns.Length; i++)
467                        if (keysleft[i] != null && keysleft[i] > max)
468                        {
469                            max = keysleft[i];
470                            id = i;
471                        }
472                    maxThread = id;
473                    maxThreadMutex.ReleaseMutex();
474                }
475                #endregion
476
477                showProgress(costList, size, keycounter, doneKeys);
478
479                #region set doneKeys to 0
480                doneKeys = 0;
481                for (int i = 0; i < doneKeysA.Length; i++)
482                    doneKeysA[i] = 0;
483                #endregion
484
485                if (keycounter >= size)
486                    break;
487            }//end while
488
489            //wake up all sleeping threads, so they can stop:
490            while (threadStack.Count != 0)
491                ((ThreadStackElement)threadStack.Pop()).ev.Set();
492
493            if (!stop)
494                ProgressChanged(1, 1);
495
496            return costList;
497        }
498
499        private void showProgress(LinkedList<ValueKey> costList, BigInteger size, BigInteger keycounter, BigInteger doneKeys)
500        {
501            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
502
503            LinkedListNode<ValueKey> linkedListNode;
504            ProgressChanged(Math.Pow(10, keycounter.log(10) - size.log(10)), 1.0);
505
506            if (QuickWatchPresentation.IsVisible && doneKeys != 0 && !stop)
507            {
508                double time = (Math.Pow(10, (size - keycounter).log(10) - doneKeys.log(10)));
509                TimeSpan timeleft = new TimeSpan(-1);
510
511                try
512                {
513                    if (time / (24 * 60 * 60) <= int.MaxValue)
514                    {
515                        int days = (int)(time / (24 * 60 * 60));
516                        time = time - (days * 24 * 60 * 60);
517                        int hours = (int)(time / (60 * 60));
518                        time = time - (hours * 60 * 60);
519                        int minutes = (int)(time / 60);
520                        time = time - (minutes * 60);
521                        int seconds = (int)time;
522
523                        timeleft = new TimeSpan(days, hours, minutes, (int)seconds, 0);
524                    }
525                }
526                catch
527                {
528                    //can not calculate time span
529                }
530
531                ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
532                {
533                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).keysPerSecond.Text = "" + doneKeys;
534                    if (timeleft != new TimeSpan(-1))
535                    {
536                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).timeLeft.Text = "" + timeleft;
537                        try
538                        {
539                            ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "" + DateTime.Now.Add(timeleft);
540                        }
541                        catch
542                        {
543                            ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "in a galaxy far, far away...";
544                        }
545                    }
546                    else
547                    {
548                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).timeLeft.Text = "incalculable :-)";
549                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).endTime.Text = "in a galaxy far, far away...";
550                    }
551
552                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Clear();
553                    linkedListNode = costList.First;
554                   
555                    int i = 0;
556                    while (linkedListNode != null)
557                    {
558                        i++;
559
560                        ResultEntry entry = new ResultEntry();
561                        entry.Ranking = "" + i;
562                        entry.Value = "" + Math.Round(linkedListNode.Value.value,3);
563                        entry.Key = linkedListNode.Value.key;
564                        entry.Text = enc.GetString(linkedListNode.Value.decryption);
565
566                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Add(entry);
567                        linkedListNode = linkedListNode.Next;
568                    }
569                }
570                , null);
571            }//end if
572
573
574            if (!stop && QuickWatchPresentation.IsVisible)
575            {
576
577                ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
578                {
579                    ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Clear();
580                    linkedListNode = costList.First;                   
581                    int i = 0;
582
583                    while (linkedListNode != null)
584                    {
585                        i++;
586
587                        ResultEntry entry = new ResultEntry();
588                        entry.Ranking = "" + i;
589                        entry.Value = "" + Math.Round(linkedListNode.Value.value, 3);
590                        entry.Key = linkedListNode.Value.key;
591                        entry.Text = enc.GetString(linkedListNode.Value.decryption);
592
593                        ((KeySearcherQuickWatchPresentation)QuickWatchPresentation).entries.Add(entry);
594                        linkedListNode = linkedListNode.Next;
595                    }
596                }
597                , null);
598            }
599        }
600
601        #region For TopList
602
603        private void fillListWithDummies(int maxInList, LinkedList<ValueKey> costList)
604        {
605            ValueKey valueKey = new ValueKey();
606            if (this.costMaster.getRelationOperator() == RelationOperator.LessThen)
607                valueKey.value = double.MaxValue;
608            else
609                valueKey.value = double.MinValue;
610            valueKey.key = "dummykey";
611            valueKey.decryption = new byte[0];
612            value_threshold = valueKey.value;
613            LinkedListNode<ValueKey> node = costList.AddFirst(valueKey);
614            for (int i = 1; i < maxInList; i++)
615            {
616                node = costList.AddAfter(node, valueKey);
617            }
618        }
619
620        private void updateToplist(LinkedList<ValueKey> costList)
621        {
622            LinkedListNode<ValueKey> node;
623            while (valuequeue.Count != 0)
624            {
625                ValueKey vk = (ValueKey)valuequeue.Dequeue();
626                if (this.costMaster.getRelationOperator() == RelationOperator.LargerThen)
627                {
628                    if (vk.value > costList.Last().value)
629                    {
630                        node = costList.First;
631                        while (node != null)
632                        {
633                            if (vk.value > node.Value.value)
634                            {
635                                costList.AddBefore(node, vk);
636                                costList.RemoveLast();
637                                value_threshold = costList.Last.Value.value;
638                                break;
639                            }
640                            node = node.Next;
641                        }//end while
642                    }//end if
643                }
644                else
645                {
646                    if (vk.value < costList.Last().value)
647                    {
648                        node = costList.First;
649                        while (node != null)
650                        {
651                            if (vk.value < node.Value.value)
652                            {
653                                costList.AddBefore(node, vk);
654                                costList.RemoveLast();
655                                value_threshold = costList.Last.Value.value;
656                                break;
657                            }
658                            node = node.Next;
659                        }//end while
660                    }//end if
661                }
662            }
663        }
664
665        #endregion
666
667        private void startThreads(IControlEncryption sender, int bytesToUse, KeyPattern[] patterns, BigInteger[] doneKeysA, BigInteger[] keycounters, BigInteger[] keysleft, Stack threadStack)
668        {
669            for (int i = 0; i < patterns.Length; i++)
670            {
671                WaitCallback worker = new WaitCallback(KeySearcherJob);
672                doneKeysA[i] = new BigInteger();
673                keycounters[i] = new BigInteger();
674                ThreadPool.QueueUserWorkItem(worker, new object[] { patterns, i, doneKeysA, keycounters, keysleft, sender.clone(), bytesToUse, threadStack });
675            }
676        }
677
678        private KeyPattern[] splitPatternForThreads(KeyPattern pattern)
679        {
680            KeyPattern[] patterns = new KeyPattern[settings.CoresUsed + 1];
681            if (settings.CoresUsed > 0)
682            {
683                KeyPattern[] patterns2 = pattern.split();
684                patterns[0] = patterns2[0];
685                patterns[1] = patterns2[1];
686                int p = 1;
687                int threads = settings.CoresUsed - 1;
688                while (threads > 0)
689                {
690                    int maxPattern = -1;
691                    BigInteger max = 0;
692                    for (int i = 0; i <= p; i++)
693                        if (patterns[i].size() > max)
694                        {
695                            max = patterns[i].size();
696                            maxPattern = i;
697                        }
698                    KeyPattern[] patterns3 = patterns[maxPattern].split();
699                    patterns[maxPattern] = patterns3[0];
700                    patterns[++p] = patterns3[1];
701                    threads--;
702                }
703            }
704            else
705                patterns[0] = Pattern;
706            return patterns;
707        }
708
709        private void keyPatternChanged()
710        {
711            Pattern = new KeyPattern(controlMaster.getKeyPattern());
712        }
713
714        // set to protected by Christian Arnold - 2009.12.06
715        protected virtual void onStatusChanged(IControl sender, bool readyForExecution)
716        {
717            if (readyForExecution)
718            {
719                this.process((IControlEncryption)sender);
720            }
721        }
722
723
724        // added by Arnie - 2009.12.07
725        public delegate void BruteforcingEnded(LinkedList<ValueKey> top10List);
726        /// <summary>
727        /// This event gets thrown after Bruteforcing had ended. This is no evidence, that bruteforcing was successful.
728        /// But when the returned List is filled, we have (at least a part) of the possible best keys
729        /// </summary>
730        public event BruteforcingEnded OnBruteforcingEnded;
731
732        // added by Arnie -2009.12.02
733        // for inheritance reasons
734        public void BruteforcePattern(KeyPattern pattern, IControlEncryption encryptControl, IControlCost costControl)
735        {
736            //ControlMaster = encryptControl;
737            //CostMaster = costControl;
738            LinkedList<ValueKey> lstRet = bruteforcePattern(pattern, encryptControl);
739            if(OnBruteforcingEnded != null)
740                OnBruteforcingEnded(lstRet);
741        }
742
743        #endregion
744
745        public void GuiLogMessage(string message, NotificationLevel loglevel)
746        {
747            if (OnGuiLogNotificationOccured != null)
748                OnGuiLogNotificationOccured(this, new GuiLogEventArgs(message, this, loglevel));
749        }
750
751        public void ProgressChanged(double value, double max)
752        {
753            if (OnPluginProgressChanged != null)
754            {
755                OnPluginProgressChanged(this, new PluginProgressEventArgs(value, max));
756
757            }
758        }
759
760        // modified by Christian Arnold - 2009.12.07 (to public)
761        /// <summary>
762        /// used for delivering the results from the worker threads to the main thread:
763        /// </summary>
764        public struct ValueKey
765        {
766            public double value;
767            public String key;
768            public byte[] decryption;
769        };
770    }
771
772    /// <summary>
773    /// Represents one entry in our result list
774    /// </summary>
775    public class ResultEntry
776    {
777        public string Ranking { get; set; }
778        public string Value { get; set; }
779        public string Key { get; set; }
780        public string Text { get; set; }
781
782    }
783}
Note: See TracBrowser for help on using the repository browser.