Programatically tell windows firewall to use the Private Network - c#

When I run my c# console app the windows firewall pops up requesting access for vshost32 (my app listens for incomming messages over port 1234 using TCP and UDP). I accept the offered suggestion (private network). The console app then works fine.
I dont want the user to deal with this, so I have added the code below. However when I investigate what this has done in Control Panel > Firewall, it seems to have enabled it for the 'public network' rather than the private network. This is no use as far as allowing my app to work.
Is there an adjustment in the code below to force it to the private network?
INetFwOpenPorts ports3;
INetFwOpenPort port3 = (INetFwOpenPort)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWOpenPort"));
port3.Port = 1234;
port3.Name = "vshost32.exe";
port3.Enabled = true;
//**UPDATE** added for suggestion in answer below - still doesnt change anything though
port3.Scope = NetFwTypeLib.NET_FW_SCOPE_.NET_FW_SCOPE_LOCAL_SUBNET;
Type NetFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
INetFwMgr mgr3 = (INetFwMgr)Activator.CreateInstance(NetFwMgrType);
ports3 = (INetFwOpenPorts)mgr3.LocalPolicy.CurrentProfile.GloballyOpenPorts;
ports3.Add(port3);

INetFwRule firewallRule = (INetFwRule)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWRule"));
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallRule.ApplicationName = "<path to your app>";
firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
firewallRule.Description = " My Windows Firewall Rule";
firewallRule.Enabled = true;
firewallRule.InterfaceTypes = "All";
firewallRule.Name = "<your rule name>";
// Should really check that rule is not already present before add in
firewallPolicy.Rules.Add(firewallRule);

Refer to my answer to your previous question.
Have a look at the following lines:
private static int Main (string [] args)
{
var application = new NetFwAuthorizedApplication()
{
Name = "MyService",
Enabled = true,
RemoteAddresses = "*",
Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL,
IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY,
ProcessImageFileName = "ServiceAssemblyName.dll",
};
return (FirewallUtilities.AddApplication(application, out exception) ? 0 : -1);
}
The NET_FW_SCOPE_ enumeration has the following values:
NET_FW_SCOPE_ALL = 0,
NET_FW_SCOPE_LOCAL_SUBNET = 1,
NET_FW_SCOPE_CUSTOM = 2,
NET_FW_SCOPE_MAX = 3,
You can further limit the ports, protocol as well as remote addresses to the rule.
UPDATE:
Here is the missing ReleaseComObject function. Place it whatever namespace and remove the reference to ComUtilities.
public static void ReleaseComObject (object o)
{
try
{
if (o != null)
{
if (Marshal.IsComObject(o))
{
Marshal.ReleaseComObject(o);
}
}
}
finally
{
o = null;
}
}
Here is the NetFwAuthorizedApplication class:
namespace MySolution.Configurator.Firewall
{
using System;
using System.Linq;
using NetFwTypeLib;
public sealed class NetFwAuthorizedApplication:
INetFwAuthorizedApplication
{
public string Name { get; set; }
public bool Enabled { get; set; }
public NET_FW_SCOPE_ Scope { get; set; }
public string RemoteAddresses { get; set; }
public string ProcessImageFileName { get; set; }
public NET_FW_IP_VERSION_ IpVersion { get; set; }
public NetFwAuthorizedApplication ()
{
this.Name = "";
this.Enabled = false;
this.RemoteAddresses = "";
this.ProcessImageFileName = "";
this.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
this.IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY;
}
public NetFwAuthorizedApplication (string name, bool enabled, string remoteAddresses, NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipVersion, string processImageFileName)
{
this.Name = name;
this.Scope = scope;
this.Enabled = enabled;
this.IpVersion = ipVersion;
this.RemoteAddresses = remoteAddresses;
this.ProcessImageFileName = processImageFileName;
}
public static NetFwAuthorizedApplication FromINetFwAuthorizedApplication (INetFwAuthorizedApplication application)
{
return (new NetFwAuthorizedApplication(application.Name, application.Enabled, application.RemoteAddresses, application.Scope, application.IpVersion, application.ProcessImageFileName));
}
}
}

Related

Having Difficulty Connecting to PTZ Camera via Onvif Unity Library

I am trying to develop a unity application that, among other things, controls a PTZ camera remotely. I'm having trouble actually getting the connection set up though.
I am using the Onvif Unity library made by Oneiros90: https://github.com/Oneiros90/UnityOnvif
When I go into the test scene in Unity-OnvifMain > core > test > scenes (which only has an empty game object and the test script) and hit play, the script initializes all clients without errors but upon reaching the mediaClient.getProfiles() line, it throws an error "WebException: There was an error on processing web request: Status code 400(BadRequest): Bad Request"
Here is the relevant test script code for reference:
using System.Linq;
using Onvif.Core;
using UnityEngine;
public class OnvifClientTest : MonoBehaviour
{
[SerializeField]
private string host = "";
[SerializeField]
private string username = "admin";
[SerializeField]
private string password = "";
[SerializeField]
private string ptzServiceAddress = "";
[SerializeField]
private string mediaServiceAddress = "";
private Vector2 ptzPanSpeedLimits;
private Vector2 ptzTiltSpeedLimits;
private Vector2 ptzZoomSpeedLimits;
private OnvifClient onvif;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private async void Start()
{
onvif = new OnvifClient(host, username, password);
var success = await onvif.Init();
if (!success)
{
Debug.LogError($"Cannot connect to {host}");
return;
}
ptzClient = await onvif.CreatePTZClient(ptzServiceAddress);
mediaClient = await onvif.CreateMediaClient(mediaServiceAddress);
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
var ptzConf = mediaClient.GetProfile(currentProfile).PTZConfiguration;
Space2DDescription panTiltSpace = null;
Space1DDescription zoomSpace = null;
//other stuff that does not matter yet because it doesn't get this far
}
}
And the OnvifClient code also:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
using Onvif.Security;
using UnityEngine;
namespace Onvif.Core
{
public class OnvifClient
{
private DeviceClient device;
private Binding binding;
private SoapSecurityHeaderBehavior securityHeader;
public Uri OnvifUri { get; private set; }
public string Host { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public Capabilities Capabilities { get; private set; }
public OnvifClient(string host, string username, string password)
{
Host = host;
OnvifUri = new Uri(host); //($"http://{host}/onvif/device_service");
Username = username;
Password = password;
}
public async Task<bool> Init()
{
try
{
//Have to set http continue to false since the server is not expecting it: https://forum.unity.com/threads/http-error-417-expectation-failed-unitywebrequest.651256/
System.Net.ServicePointManager.Expect100Continue = false;
binding = CreateBinding();
var endpoint = new EndpointAddress(OnvifUri);
device = new DeviceClient(binding, endpoint);
var timeShift = await GetDeviceTimeShift(device);
securityHeader = new SoapSecurityHeaderBehavior(Username, Password, timeShift);
device = new DeviceClient(binding, endpoint);
device.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
device.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
device.Open();
Capabilities = device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
return true;
}
catch (Exception e)
{
Debug.LogError(e.Message);
return false;
}
}
public void Ping()
{
device.GetCapabilities(new CapabilityCategory[] { CapabilityCategory.All });
}
public void Close()
{
if (device != null)
device.Close();
}
public async Task<DeviceClient> GetDeviceClient()
{
if (device == null)
await Init();
return device;
}
//original
public async Task<MediaClient> CreateMediaClient()
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(Capabilities.Media.XAddr)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//custom
public async Task<MediaClient> CreateMediaClient(string endpoint)
{
if (device == null)
await Init();
var media = new MediaClient(binding, new EndpointAddress(new Uri(endpoint)));
media.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
media.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return media;
}
//original
public async Task<PTZClient> CreatePTZClient()
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(Capabilities.PTZ.XAddr)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
//custom
public async Task<PTZClient> CreatePTZClient(string endpoint)
{
if (device == null)
await Init();
var ptz = new PTZClient(binding, new EndpointAddress(new Uri(endpoint)));
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
ptz.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return ptz;
}
public async Task<ImagingPortClient> CreateImagingClient()
{
if (device == null)
await Init();
var imaging = new ImagingPortClient(binding, new EndpointAddress(new Uri(Capabilities.Imaging.XAddr)));
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Clear();
imaging.ChannelFactory.Endpoint.EndpointBehaviors.Add(securityHeader);
return imaging;
}
private static Binding CreateBinding()
{
var binding = new CustomBinding();
var textBindingElement = new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
var httpBindingElement = new HttpTransportBindingElement
{
AllowCookies = true,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue
};
binding.Elements.Add(textBindingElement);
binding.Elements.Add(httpBindingElement);
return binding;
}
private static Task<TimeSpan> GetDeviceTimeShift(DeviceClient device)
{
return Task.Run(() =>
{
var utc = device.GetSystemDateAndTime().UTCDateTime;
var dt = new System.DateTime(utc.Date.Year, utc.Date.Month, utc.Date.Day,
utc.Time.Hour, utc.Time.Minute, utc.Time.Second);
return dt - System.DateTime.UtcNow;
});
}
}
}
I'm very new to .net client connection and so forth, so I'm really not sure why it would throw a 400 error, as the username and password are both correct. I also know for a fact that connecting to a camera works as I am working off of an existing project that does it through c# (outside of unity though).
Alternatively, I have tried avoiding using the helper scripts provided by Oneiros90 and connecting to the devices using the core onfiv scripts, as per this post: C# .NET Framework 4.52 Zoom/Focus for PELCO Camera via ONVIF
But when I do that, it reaches the mediaClient.getProfiles() line and instead produces an "InvalidOperationException: Use ClientCredentials to specify a user name for required HTTP Digest authentication." error, even though I am clearly setting the http digest client credentials just above. This is also how it works in the aforementioned existing project, although there isn't as much going on with the security headers and so on in that.
My Code in unity:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Onvif.Core;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
public class PTZController_Local : MonoBehaviour
{
public string hostAddress = "http://<secret>/onvif/device_service";
public string mediaAddress = "http://<secret>/onvif/media_service";
public string PTZAddress = "http://<secret>/onvif/ptz_service";
public string username = "";
public string password = "";
private OnvifClient onvifClient;
private string currentProfile;
private PTZClient ptzClient;
private MediaClient mediaClient;
private DeviceClient device;
private CustomBinding binding;
private CustomBinding mediaBinding;
private void Start()
{
////connect to onvif device service
//onvifClient = new OnvifClient(hostAddress, username, password);
//var success = await onvifClient.Init();
//if (!success)
//{
// Debug.LogError($"PTZController: Cannot connect to {hostAddress}");
// return;
//}
//Debug.Log($"PTZController: Successfully connected to {hostAddress}");
//device = await onvifClient.GetDeviceClient();
System.Net.ServicePointManager.Expect100Continue = false;
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
httpTransport.AuthenticationScheme = System.Net.AuthenticationSchemes.Digest;
httpTransport.MaxReceivedMessageSize = Int32.MaxValue;
httpTransport.MaxBufferSize = Int32.MaxValue;
httpTransport.ProxyAddress = null;
httpTransport.BypassProxyOnLocal = true;
httpTransport.UseDefaultWebProxy = false;
httpTransport.TransferMode = TransferMode.StreamedResponse;
binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, System.Text.Encoding.UTF8), httpTransport);
binding.CloseTimeout = TimeSpan.FromSeconds(30.0);
binding.OpenTimeout = TimeSpan.FromSeconds(30.0);
binding.SendTimeout = TimeSpan.FromMinutes(10.0);
binding.ReceiveTimeout = TimeSpan.FromMinutes(3.0);
device = new DeviceClient(binding, new EndpointAddress(hostAddress));
device.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
device.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
device.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement mediaHttpTransport = new HttpTransportBindingElement()
{
AuthenticationScheme = System.Net.AuthenticationSchemes.Digest
};
mediaBinding = new CustomBinding(messageElement, mediaHttpTransport);
mediaClient = new MediaClient(mediaBinding, new EndpointAddress(mediaAddress));
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = username;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var profiles = mediaClient.GetProfiles();
currentProfile = profiles.First().token;
//other code that is never reached
}
}
Original project code (i've tried copying this into unity exactly and get the same error about setting the http credentials):
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.ServiceModel;
using OnvifPipeClient.OnvifMedia10;
using OnvifPipeClient.OnvifPTZService;
using System.Timers;
using System.Diagnostics;
using System.Threading;
namespace OnvifPipeClient
{
public class PTZConnection
{
public static PTZClient ptzClient;
private static MediaClient mediaClient;
public static Profile profile;
// establish connection to PTZ
public static void OnvifLogin()
{
// create binding
System.Net.ServicePointManager.Expect100Continue = false;
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
// connect to PTZ interface
ptzClient = new PTZClient(binding, new EndpointAddress("http://<secret>/onvif/ptz_service"));
ptzClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
ptzClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
ptzClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// connect to media client
mediaClient = new MediaClient(binding, new EndpointAddress("http://<secret>/onvif/media_service"));
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = "secret";
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = "secret";
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
// load profiles and configurations
var profiles = mediaClient.GetProfiles();
profile = mediaClient.GetProfile(profiles[0].token);
//other code that is reached without issues
}
}
The only thread of a possible explanation I could find is this github issue(https://github.com/jamidon/ONVIF-test/issues/1) saying that another onvif c# library that oneiros90 had previously tried did not work with pelco cameras for some reason (the camera I'm trying to connect to is a pelco camera).
So, yeah, sorry for the big code dumps here but it's very difficult to find resources about this. Thanks in advance for any help.

'PingCompletedEventArgs.reply' is null

What causes the Reply to be null? I am building a game via Unity3D.
The code works on my PC - ping successfully completes, and I can access the reply.RoundtripTime. But when building and running on iOS (iPhone), the reply remains null, even though the _respose.Cancelled is false and Error is null? The phone is connected to internet.
class MyPing{
public SNNI.Ping ping{ get; private set;} = null;
public Ip_Array ip{ get; private set;} = null;
public SNNI.PingCompletedEventArgs _response{ get; private set;} = null;
public bool _isDone{
get{ return _response != null; }
}
public MyPing( Ip_Array ip, int ms_timeout=1000 ){
this.ip = ip;
ping = new SNNI.Ping();
ping.PingCompleted += (object sender, SNNI.PingCompletedEventArgs r)=>{ _response = r; };
System.Net.IPAddress addr = new System.Net.IPAddress( ip.ip );
ping.SendAsync(addr, ms_timeout, null);
}
}
.
//somewhere else:
p = new MyPing( new Ip_Array("0.0.0.0") );//<--actual ip instead of this placeholder
.
//later, checking every frame of the game:
if(p._isDone == false){ continue; }
if(p._response.Cancelled || p._response.Error != null){ continue; }
else{
Debug.Log(p._response.reply.Status);//throws exception because .reply is somewhow 'null'
}
Interestingly there is an example on using PingCompletedEventArgs, specifically the DisplayReply() method. In there they check if reply==null but never explain why it might be null...

Parser.Default.ParseArguments always returns false

I have added CommandLineParser library into my project and I have configure all the arguments which should be provided to my project for to support silent installation of the same.
An InstallOptions class is being created with some "Option" attributes for each of the required and non-required arguments to the same e.g. below
public class InstallOptions
{
[Option("path", Required = true, HelpText = "The installation path where you want the application installed.")]
public string InstallPath { get; set; }
[Option("dbname", Required = true, HelpText = "Database name.")]
public string DbName { get; set; }
[Option("dbserver", Required = true, HelpText = "Database server name or IP address.")]
public string DbServer { get; set; }
[HelpOption]
public string DisplayHelpOnParseError()
{
var help = new HelpText()
{
AddDashesToOption = true
};
var errors = "";
if (LastParserState.Errors.Any())
{
errors = help.RenderParsingErrorsText(this, 0);
}
//HACK fix where if help.addoptions is called more than once it truncates the output
if (_help == null)
{
help.AddOptions(this);
_help = help;
}
else
{
return String.IsNullOrEmpty(errors) ? _help : "ERROR(S):" + errors + _help;
}
return help;
}
}
From my program.cs file I want to debug I am running my project as below
public static void Main(string[] args)
{
args = new string[3];
args[0] = "--path C:\\Program files\MyProject";
args[1] = "--dbname myDBName";
args[2] = "--dbserver myDBServer";
var result = Parser.Default.ParseArguments(args, installOptions);
if (!result) throw new ArgumentException(installOptions.DisplayHelpOnParseError());
}
in the above code I all the time getting result = false and states throws below error message
--path required. The installation path where you want the application installed.
--dbname required. Database name.
--dbserver required. Database server name or IP address.
Please help me how to pass all 3 parameter to my project to test it is working correctly.
Thanks in advance
Arguments should be passed as below

Get the string with parent of device

In device manager of windows7 in com port's branch I choose menu "properties" one of port. In tab of "details" I chose property "parent" and see the string:
How I can obtain this string from vb .net or another language in visual studio in cmd also will be good?
I tried to use win32_ clases: pnpentity, serialPort etc but this no solved to my problem even output of Get-WMIObject Win32_SerialPort in PS had not property "parent".
Dim objService = GetObject("winmgmts://./root/cimv2")
For Each objPort In objService.ExecQuery("SELECT * FROM Win32_PnPEntity WHERE ClassGuid='{4d36e978-e325-11ce-bfc1-08002be10318}'")
Console.WriteLine(objPort.Caption & vbCrLf)
Console.Write(objPort.DeviceID & vbCrLf)
Console.ReadLine()
Next
Except Device ID I try Caption and all syntax that available in List.
Do you have any idea, please?
My solution was the following:
Get all active ports with their PnpDeviceIds:
private static List<PortInfo> GetActivePorts()
{
var ports = new List<PortInfo>();
using (var searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_SerialPort"))
using (var collection = searcher.Get())
{
foreach (var device in collection)
{
var portInfo = new PortInfo
{
Port = (string)device.GetPropertyValue("DeviceID"),
PnPDeviceId = (string)device.GetPropertyValue("PNPDeviceID")
};
if (!string.IsNullOrEmpty(portInfo.Port) && !string.IsNullOrEmpty(portInfo.PnPDeviceId))
{
ports.Add(portInfo);
}
}
}
return ports;
}
where PortInfo is
private class PortInfo
{
public string Port { get; set; }
public string PnPDeviceId { get; set; }
public string ParentDeviceId { get; set; }
}
fill ParentDeviceIds :
private static async void FillParentIds(IReadOnlyCollection<PortInfo> ports)
{
var propertiesToQuery = new List<string> {
"System.Devices.DeviceInstanceId",
"System.Devices.Parent"
};
var aqs = string.Join(" OR ", ports.Select(p => $"System.Devices.DeviceInstanceId:={p.PnPDeviceId}"));
var pnpDevices = await PnpObject.FindAllAsync(PnpObjectType.Device, propertiesToQuery, aqs);
foreach (var pnpDevice in pnpDevices)
{
var port = ports.FirstOrDefault(p => string.Compare(p.PnPDeviceId, pnpDevice.Id, StringComparison.InvariantCultureIgnoreCase) == 0);
if (port != null && pnpDevice.Properties.TryGetValue("System.Devices.Parent", out var parentId))
{
port.ParentDeviceId = parentId?.ToString();
}
}
}
ParentDeviceId will be that string you are looking for.

Writing a Workflow Foundation workflow with C#

I'm trying to write some activities with C# instead of the designer and XAML. VS2010 has been buggy and very slow for that, and it also has very poor compilation support (for variables names, properties and so on).
So I'm trying to create activities by inheriting from the Activity class directly, but I'm encountering a snag.
Here's my code:
public class TestActivity : Activity
{
public InArgument<string> Username { get; set; }
public InArgument<string> Password { get; set; }
public OutArgument<bool> ValidCredential { get; set; }
public OutArgument<ProvisioningRole> Role { get; set; }
public OutArgument<Guid> Guid { get; set; }
protected override Func<Activity> Implementation
{
get
{
return () =>
{
return new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username,
Password = this.Password,
Guid = this.Guid,
Result = this.ValidCredential
},
new If()
{
Condition = this.ValidCredential,
Then = new GetUserRoleActivity()
{
Username = this.Username,
Password = this.Password,
Result = this.Role
}
},
}
};
};
}
set { base.Implementation = value; }
}
}
The problem is with the If(), the condition. It's supposed to be an InArgument, but this.ValidCredential is an OutArgument. I've tried creating a Variable, assign the value of ValidCredential to it. I also tried to put the result of AuthenticateUserActivity in the variable and then assign it to ValidCredential, but I get an error saying the To property of Assign needs to be specified.
I've looked around for proper tutorials, but all I found was an MSDN article that had a quick and dirty code implementation, and it used literals instead of the passed arguments, so no help from there.
I found out how to do it. You just need to create new InArgument from the original one. There is a constructor that takes an expression for it.
Username = new InArgument<bool>((ActivityContext c) => this.ValidCredential.Get(c))
So I changed my whole activity to
return new CompensableActivity()
{
Body = new Sequence()
{
Activities =
{
new AuthenticateUserActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Guid = this.Guid.Out(),
Result = this.ValidCredential.Out()
},
new If(this.ValidCredential.In())
{
Then = new GetUserRoleActivity()
{
Username = this.Username.In(),
Password = this.Password.In(),
Result = this.Role.Out()
},
Else = new Assign<ProvisioningRole>()
{
To = this.Role.Out(),
Value = ProvisioningRole.User
}
}
}
},
};
In and Out being extension methods I wrote:
public static class WorkflowExtensions
{
#region In
public static InArgument<T> In<T>(this InArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
public static InArgument<T> In<T>(this OutArgument<T> self)
{
return new InArgument<T>(context => self.Get(context));
}
#endregion
#region Out
public static OutArgument<T> Out<T>(this InArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
public static OutArgument<T> Out<T>(this OutArgument<T> self)
{
return new OutArgument<T>(context => self.Get(context));
}
#endregion
}
And now all is well!
You should be able to get this to work. The basic approach should be to use a Variable to store data, use an OutArgument to get data out of activities into the Variable and InArguments to get data from a Variable into an activity.
Also note that the expressions to tie InArguments to Variables are VisualBasicValue expressions. So something like:
Condition = new VisualBasicValue("System.DateTime.Now.Hour < 12")
This blog post isn't about using arguments and variables but shows a couple of examples.
Going to shamelessly plug my own library that I ended up making for this:
http://code.google.com/p/system-transactions/
Allows basic compensation of code without the ginormous hassle of WF. Also, compiles properly and is easily debuggable.

Categories

Resources