Windows Workflow Foundation i WCF

Jak wiadomo diagramy w WF można wystawić jako usługi sieciowe (web service). Oczywiście musi spełniać warunek następujący: początek diagramu musi zawierać kontrolkę WebServiceInput a na "wyjściu" diagramu musi być kontrolka WebServiceOutput. Standardowy mechanizm publikacji tworzony prosta usługę (asmx). A co w przypadku gdy chcemy mieć większą kontrole nad usługą? Taką kontrole daje nam WCF. W tym momencie trzeba zrobić własny sposób publikacji.
Musimy zdefiniować interfejs oraz opatrzyć atrybutami tak jakbyśmy robili usługę WCF

[ServiceContract()]
interface IKeyValidator
{
[OperationContract(Name = "ValidateKey")]
string ValidateKey(string key);
}

Następnie tworzymy sobie diagram. Ja przygotowałem następujący (pomijam opis jak go poprawnie skonfigurować w oparciu o powyższy interfejs i własne akcje)



WF jest uruchamiany poprzez klase WorkflowWebService która implementuje tworzenie środowiska WF, przygotowanie diagramu, uruchomienie go, przekazanie parametrów do diagramu oraz pobranie z niego wartości zwrotnej. Musimy utworzyć własna klasę która będzie dziedziczyć z tej klasy oraz interfejsu naszej usługi.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class KeyValidatorService : WorkflowWebService, IKeyValidator
{
public KeyValidatorService()
: base(typeof(Workflow1))
{
}
public string ValidateKey(string key)
{
return (string)base.Invoke(typeof(IKeyValidator), "ValidateKey", true, new object[] { key })[0];
}
}

Cały mechanizm uruchamiający diagram jest to wywołanie metody Invoke gdzie parametrami są typ interfejsu usługi sieciowej, nazwa metody którą wywołujemy, flaga oznaczająca czy usługa aktywuje diagram oraz tablica parametrów która jest przykazywana do diagramu.
Klasa WorkflowWebService niestety wymaga środowiska ASP.NET to poprawnego działania stąd dodatkowy atrybut AspNetCompatibilityRequirements na klasie KeyValidatorService. Teraz możemy przygotować aplikacje webową oraz utworzyć plik dla usługi sieciowej która może wyglądać tak

<%@ ServiceHost Language="C#" Service="WebServiceTest.KeyValidatorService" %>

Musimy jeszcze przygotować plik konfiguracyjny aplikacji webowej. Poniżej fragmenty konfiguracji które należy wstawić do web.config aby móc hostować diagramy WF

<configuration>
<configSections>
<section name="WorkflowRuntime" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<WorkflowRuntime Name="WorkflowServiceContainer">
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add type="System.Workflow.Runtime.Hosting.DefaultWorkflowCommitWorkBatchService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</Services>
</WorkflowRuntime>
<system.web>
<httpModules>
<add type="System.Workflow.Runtime.Hosting.WorkflowWebHostingModule, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="WorkflowHost"/>
</httpModules>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<system.serviceModel>
</configuration>

Przygotowałem też mały program testowy napisany w WPF




Kompletny przykład jest dostępny tutaj WFWebService.zip

Windows Workflow Foundation - State machine - wykorzystanie do kontroli interfejsu użytkownika

W WF mamy do dyspozycji dwa typy diagramów: sekwencyjny i maszyna stanów. Maszyna stanu umożliwia definiowanie akcji określających przechodzenie między poszczególnymi stanami. Każdy stan składa się z następujących bloków :
- działanie inicjujące stan
- lista zdarzeń które mogą być wykonane w tym stanie
- działanie które jest wykonywane przy wyjściu z danego stanu



Każda obsługa zdarzeń jest to sekwencyjny diagram który realizuje przepływ. Pierwszym elementem który musi być w tym diagramie jest to element który ma zaimplementowany interfejs IEventActivity. W WF są dostępne trzy akcje które go mają , są to: Delay, HandleExternalEvent oraz WebServiceInput. Dla naszego przykładu zrobiłem taki diagram stanu



Mamy dwie metody na obsłużenie kontrolek na formatce ze względu na obecny stan diagramu
1) Wykorzystujemy kolejkę akcji danego stanu. W przybliżeniu każdy stan posiada kolejkę działań które może wykonać. Możemy ją odczytać poprzez użycie metody GetWorkflowQueueData z klasy WorkflowInstance. Zwraca jest nam lista zadań czekający w kolejce. Rzutując właściwość QueueName na klasę EventQueueName możemy pobrać te akcje które czekają na poszczególne zdarzenia.

ReadOnlyCollection<WorkflowQueueInfo> queues = stateMachineInstance.WorkflowInstance.GetWorkflowQueueData();

Collection<string> MessagesAllowed = new Collection<string>();

foreach (WorkflowQueueInfo s in queues)
{
EventQueueName eventQueueName = s.QueueName as EventQueueName;
if (eventQueueName != null)
{
MessagesAllowed.Add(eventQueueName.MethodName);
}
}


A tak wygląda diagram który obsługuje konkretne zdarzenie



Tylko te akcje które wykorzystują kontrolkę HandleExternalEvent przekazują nazwę zdarzenia w
eventQueueName.MethodName. Po tym możemy włączyć lub wyłączyć elementy interfejsu na formatce np.

if (MessagesAllowed.Contains("ButtonPlayPressed"))
this.StartButton.IsEnabled = true;

if (MessagesAllowed.Contains("ButtonPausePressed"))
this.PauseButton.IsEnabled = true;

if (MessagesAllowed.Contains("ButtonStopPressed"))
this.StopButton.IsEnabled = true;

2) Ta metoda opiera się na akcji którą możemy wykonać przy wejściu do danego stanu.
Można tam uruchomić dowolną metodę np z parametrem określający jak jest aktualny stan diagramu i w tym momencie uruchomić jakieś zdarzenie które będzie zaimplementowane w serwisie. Formatka może obsługiwać te zdarzenie i ustawić odpowiednio kontrolki.




Która metoda lepsza? Pierwsza daje możliwość ustawienia elementów interfejsu od oczekujących zdarzeń i tak naprawdę nie jest ściśle powiązania z konkretnym stanem. Druga metoda wymaga zaś przy każdym stanie był kod powiadomi aplikacji w jakim jest stanie diagram aby aplikacja mogła ustawić odpowiednio swoje kontrolki.

Przykład który wykorzystuje obie metody do odpowiedniego przełączania kontrolek jest dostępny tutaj StateMachineUI.zip

.NET Framework Rootkits

Ostatnio natknąłem się na dość ciekawy artykuł opisujący możliwość ataku na aplikacje napisane w .NET link :
http://www.applicationsecurity.co.il/english/NETFrameworkRootkits/tabid/161/Default.aspx

Jedna rzecz przykuła mi uwagę. Wiadomo że zestawy są identyfikowane przez silne nazwę. Generuje się ją m.in poprzez użycie prywatnych kluczy. Po wstępnej lekturze zawartego tam artykułu można dowiedzieć o pewnej "słabości" mechanizmu sprawdzania podpisu , mianowicie jeśli uda się umieścić w odpowiednim folderze GAC nowy zestaw który będzie repliką istniejącego zestawu (czyli np poprzez zdekompilowanie, z dołożonym nowym kodem tu czy tam i ponownie skompilowanym z własnym kluczem prywatnym) i umieści się w odpowiednim folderze GAC to mechanizm sprawdzający silną nazwę nie sprawdza tego w wgrywanym zestawie !. Wystarczy mu odpowiednia nazwa folderu w którym jest położonym ten zestaw :]. Oczywiście aby to zrobić trzeba mieć uprawnienia administracyjne...
Praktyczny sposób wykorzystania ?
Proszę bardzo :).Spora cześć aplikacji korzysta z metody FormsAuthentication.HashPasswordForStoringInConfigFile
aby wygenerować np skrót funkcji MD5 i przesyła zakodowane hasło dalej powiedzmy do bazy. Dokładając na początek tej metody kod do przechwytywania argumentów, można zgromadzić spory zbiór haseł...
Pobawię się tym i zobaczę jak to się ma w praktyce.

Game of life

Napisałem sobie w Silverlight 3 prosty automat komórkowy i opublikowałem jego kod na codeplex.com.

Link do projektu : http://gameoflife.codeplex.com/


Projekt wymaga trochę rozbudowania np poprzez obsłużenie innych "klocków" jak trójkąty czy sześciany. Jest zastosowany prosty (opisany wcześniejszym poście) acz łatwy w użyciu mechanizm do lokalizowania elementów interfejsu oraz komunikatów programu.

Inne podejście w IDE

Znalazłem filmik pokazujący pewną zmianę w środowisku edytorów dla programistów. Zapowiada się dość ciekawe podejście




Oryginalny tekst opisujący działanie można znaleźć tutaj

Silverlight i lokalizowanie interfejsu graficznego

Po przeszukaniu kilku stron na temat lokalizowania w Silverlight mam pewną krótką ale dość skuteczną metodę na ten problem. Tworzymy sobie zasoby zawierające tekst oraz zlokalizowane zasoby z identyfikatorami kultur które obsługujemy



Ustawiamy opcjach edytora zasobów dla zasobu którego będziemy się odwoływać (w tym przypadku dla pliku ApplicationStrings.resx ) pole Access Modifier na Public w pozostałych plikach dla poszczególnych kultur na No code generation ponieważ nie jest nam potrzebny ich wygenerowany kod (menadżer zasobów wgrywa odpowiedni plik dla wybranej kultury)

Stworzyłem sobie klasę która zawiera zasób z łańcuchami oraz ma zaimplementowany interfejs INotifyPropertyChanged który będzie informował o zmianach jakie zajdą w przypadku zmiany kultury


using System.ComponentModel;

public class LocalizationString : INotifyPropertyChanged
{
#region Fields
private static GameOfLife.Assets.Resources.ApplicationStrings _strings = new GameOfLife.Assets.Resources.ApplicationStrings();
#endregion Fields
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion Events
#region Properties
public GameOfLife.Assets.Resources.ApplicationStrings Strings
{
get
{
return _strings;
}
set
{
_strings = value;
OnPropertyChanged("Strings");
}
}
#endregion Properties
#region Methods
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion Methods
}

Następnie umieszczamy tą klase jako zasób aplikacji aby była dostępna dla całej aplikacji

<ResourceDictionary>
<res:LocalizationString x:Key="LocalizationString" />
</ResourceDictionary>

Następnie podpinamy poprzez wyrażenie bindowania kontrolkach:

<Button x:Name="PrepareButton" Content="{Binding Path=Strings.PrepareButton, Source={StaticResource LocalizationString}}" />

Co daje nam taka konstrukcja ? Ano daje nam to że zmieniając właściwość Strings w klasie LocalizationString wszystkie kontrolki mające odwołanie do tej właściwości zostaną automatycznie zaktualizowane i wyświetlą tekst zgodnie z wybraną kulturą.

Przykładowy kod który uruchomi ten mechanizm to :

LocalizationString localizationString = Application.Current.Resources["LocalizationString"] as LocalizationString;
if (localizationString != null)
{
CultureInfo newCulture = new CultureInfo(lang);
Thread.CurrentThread.CurrentCulture = newCulture;
Thread.CurrentThread.CurrentUICulture = newCulture;
ApplicationStrings.Culture = newCulture;
localizationString.Strings = new GameOfLife.Assets.Resources.ApplicationStrings();
}

Silverlight i wątki

Swoją przygodę z Silverlight zacząłem poprze napisanie małego programu który później opublikuje na codeplex.com. Miałem tam pewny problem, mianowicie z poziomu wątku musiałem zaktualizować stan kontrolek. W pierwszym podejściu korzystałem klasy BackgroundWorker i z niego poprzez właściwość kontrolki Dispatcher wykonywałem metodę BeginInvoke w której przekazywałem kod do aktualizacji. Jednak to się okazała mało skuteczne, więcej kod do obsługi wątków w Silverlight został trochę uszczuplony więc nie można było poczekać na zakończenie wykonywania kodu przekazanego w BeginInvoke.

Rozwiązaniem okazało się użycie klasy DispatcherTimer który działa tak jak klasa Dispatcher w wątku interfejsu UI więc można było odrazu aktualizować stan kontrolki. Z tym że DispatcherTimer umożliwia wykonywania podanego kodu cyklicznie.