When is a Kofax Workflow Agent run? - c#

When are workflow agents actually called?
I've installed my own workflow agent (this one) and write to a log on the second line in ProcessWorkflow (the first one being the log4net XmlConfigurator.Configure call with a newly created FileInfo instance.
The log is always written after the KTM Server module. This WOULD make sense, because I read a configuration which prompts the WFA to do something with the workflow data. But after the KTM Validation module (where the WFA is also configured to do something) the log is not written.
Is there an explanation, why I don't see any log entries? I've checked the kofax logs too, but I found no evidence there.
The exact code snippet looks like this:
public void ProcessWorkflow(ref IACWorkflowData workflowData)
{
XmlConfigurator.Configure(new FileInfo(#"C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Configuration Files\log4net.config"));
log.Info("Workflow Agent started ...");
// rest of the code

So, since I kind of figured out how to use Workflow Agents, I decided to answer this question for future reference.
A Workflow Agent is being run every time a module has been executed. IIRC this includes viewing the properties with Batch Manager. The Workflow Agent will be called on the site where the module has been executed. So if you execute your automatic modules (i.e. PDF Generator, Export) on a server and Scan and Validation on client sites, the Workflow Agent will be executed on the server or the client station which executed the module respectively.
I actually forgot what didn't work in my original question, but I also ran into problems because I didn't register the DLL using RegAsm.exe. See my other Kofax-related question for more information about this: How to correctly install Workflow Agents in Kofax?

You can also use this in your code so that it only runs the logic when you want it to:
if (workflowData.CurrentModule.Name != "Scan" || workflowData.get_NextState().Name != "Ready")
{
return;
}

Related

System.Net.WebClient does not handle cache when launched from SYSTEM account

I am having trouble with a script I am writing that is using System.Net.WebClient (called from Powershell but I guess the problem should occur with everything that is using the same cache as System.Net.WebRequest):
For context (as there may be a better solution than what I found):
I made an extension for IE (yes, some clients still use it) in C# (yes, it's not recommended but I had no choice)
this extension needs to run with EPM activated (so low-privileged).
it needs a configuration file that is available on a server accessed by HTTPS.
the configuration needs to be available when IE is launched so we have to cache it (also, each tab has its own instance of the extension)
that cached configuration have to stay in a privileged folder (the extension injects code to some of the pages according to that configuration, so you don't want the user or any process to have write access to it)
To solve the problem of caching the configuration, I wrote a Powershell script that is launched through the task scheduler. The script uses System.Net.WebClient to download the file, and I set it to respect the cache of the file:
$webclient = New-Object System.Net.WebClient
$cacheLevel = [System.Net.Cache.RequestCacheLevel]::CacheIfAvailable
$webclient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy($cacheLevel)
When I launch the script using "Run As Administrator", the cache is respected (providing the server is well configured).
When I launch the script from the task scheduler (user NT AUTHORITY\SYSTEM, as I need privilege to be able to save the file in the extension installation dir), the cache is not respected and the file is downloaded every single time.
Any idea on how to solve this issue? I need the caching to able to be poll the file without having to do a full download (the file is small, but the number of users is high :D).
Maybe it would be possible to use the date of the file that was previously downloaded?

How to attach an Event Receiver to a List/Document Library on Production server?

I have created/bound an Event Receiver to a document library in Share Point 2010 to read the content of an excel file and load a list from the content.
Everything works well in development, I don't have access to the production servers so I logged a ticket to I.T deploy to production server (attaching the .wsp of ER).
The guy has deployed/activated the ER in the Production, Now I am not sure how to attach it to particular document library and how to validate if/when it is attached.
If you used the VS2010 SharePoint templates and it asked you for a specific library, you probably have to modify it for your production library. In your VS solution look at the elements.xml file. You should see a at the top. If your Event Handler is scoped to web, you can change the "..." to the actual library name. Example: .
Rebuild the WSP. Your support group should be running the add-spsolution and install-spsolution powershell commands to add it and install it to the farm.
If you can get to the sub site in questions features (Site settings->Manage Site features) you should see your event handler present but deactivated.
Click to activate it.
Without server access (to see the logs in the 14 hive) it is tough to really see if it is attached. If your support team uses SharePoint Manager they can look to see if the handler is really attached to the library in question. Short of that, what I've seen people do is put a small chunk of code in their handler (item added/updated/etc.) that will update a status column on the library when it fires (e.g. "Handler Fired"). This way you can actually see if it is firing. Additionally, you could put any error message you might get in the code into this column (e.g. "Could not load file to list", etc.").
If with your WSP the dll that contains the event receiver is deployed in GAC on production then you can attach the event receiver to a list/library with powershell.
$type = "ItemAdding" #or any other type, like ItemDeleting, ItemAdded, ItemUpdating ...
$assembly = "YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5eff...(here goes assebly's token"
$class = "Your Class"
$list.EventReceivers.Add($type, $assembly, $class)
Source: http://naimmurati.wordpress.com/2012/03/22/add-modify-or-delete-list-event-receivers-with-powershell/

Converting Microsoft EWS StreamingNotification Example to a service

I've been working to try and convert Microsoft's EWS Streaming Notification Example to a service
( MS source http://www.microsoft.com/en-us/download/details.aspx?id=27154).
I tested it as a console app. I then used a generic service template and got it to the point it would compile, install, and start. It stops after about 10 seconds with the ubiquitous "the service on local computer started and then stopped."
So I went back in and upgraded to C# 2013 express and used NLog to put a bunch of log trace commands to so I could see where it was when it exited.
The last place I can find it is in the example code, SynchronizationChanges function,
public static void SynchronizeChanges(FolderId folderId)
{
logger.Trace("Entering SynchronizeChanges");
bool moreChangesAvailable;
do
{
logger.Trace("Synchronizing changes...");
//Console.WriteLine("Synchronizing changes...");
// Get all changes since the last call. The synchronization cookie is stored in the
// _SynchronizationState field.
// Only the the ids are requested. Additional properties should be fetched via GetItem
//calls.
logger.Trace("Getting changes into var changes.");
var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly, null, 512,
SyncFolderItemsScope.NormalItems,
_SynchronizationState);
// Update the synchronization cookie
logger.Trace("Updating _SynchronizationState");
the log file shows the trace message ""Getting changes into var changes." but not the "Updating _SynchronizationState" message.
so it never gets past var changes = _ExchangeService.SyncFolderItems
I cannot for the life figure out why its just exiting. There are many examples of EWS streaming notifications. I have 3 that compile and run just fine but nobody as far as I can tell has posted an example of it done as a service.
If you don't see the "Updating..." message it's likely the sync threw an exception. Wrap it in a try/catch.
OK, so now that I see the error, this looks like your garden-variety permissions problem. When you ran this as a console app, you likely presented the default credentials to Exchange, which were for your login ID. For a Windows service, if you're running the service with one of the built-in accounts (e.g. Local System), your default credentials will not have access to Exchange.
To rectify, either (1) run the service under the account you did the console app with, or (2) add those credentials to the Exchange Service object.

Keep C# application running

I'm building a Windows Service that uses FileSystemWatcher, and runs in the background.
I don't want to keep on uninstalling and installing the service every time I want to debug, so would like to do most of my development in a normal program before moving it into a service. But I'm quite new to this, and when I run it, it just runs through the block and exits.
What would be a good way to keep the program running?
http://einaregilsson.com/run-windows-service-as-a-console-program/
I've used this before to debug my service as a Console application based on whether its running in an interactive user environment.
public partial class DemoService : ServiceBase
{
static void Main(string[] args)
{
DemoService service = new DemoService();
if (Environment.UserInteractive)
{
service.OnStart(args);
Console.WriteLine("Press any key to stop program");
Console.Read();
service.OnStop();
}
else
{
ServiceBase.Run(service);
}
}
while (true)
{
// Execute your program's functionality here.
}
I wrote a 7 part series a while ago titled: Building a Windows Service. It covers all the intricacies of building services, making them friendly to debug, and self-installing.
The basic feature set I was looking for was as follows:
Building a service that can also be used from the console
Proper event logging of service startup/shutdown and other activities
Allowing multiple instances by using command-line arguments
Self installation of service and event log
Proper event logging of service exceptions and errors
Controlling of start-up, shutdown and restart options
Handling custom service commands, power, and session events
Customizing service security and access control
The final result was a Visual Studio project template that creates a working service, complete with all of the above, in a single step. It's been a great time saver for me.
see Building a Windows Service – Part 7: Finishing touches for a link to the project template and install instructions.
Here’s documentation from MSDN # http://msdn.microsoft.com/en-us/library/7a50syb3(v=vs.80).aspx?ppud=4 . I have tried it before and it works under .NET Framework 3.x. I could not find my descriptive notes on it, at the moment.
Use the pragma #If DEBUG for debugging purposes like console outputs. Another is using the Debug object.
If you have any trouble with this, say so. I may be able to find my notes or make a Windows Service app myself, just to see if the steps on MSDN still work.

How do you debug a Windows Service?

I read the MSDN article on the topic. To quote:
Because a service must be run from
within the context of the Services
Control Manager rather than from
within Visual Studio, debugging a
service is not as straightforward as
debugging other Visual Studio
application types. To debug a service,
you must start the service and then
attach a debugger to the process in
which it is running. You can then
debug your application using all of
the standard debugging functionality
of Visual Studio.
Now my problem is that my service fails to start in the first place. First it crashes, and says:
An unhandled exception
(System.Runtime.InteropServices.COMException)
occurred in MyServiceName.exe[3596])
and suggests me to debug it (the debugger instance instantly crashes when I choose one). Then it says
Could not start the MyServiceName
service on Local Computer. Error
1053: The service did not respond to
the start or control request in a
timely fashion
So, how can I investigate/debug the reason that my service won't start? The thing is I created a console application that does EXACTLY what the service does and it works fine. (I mean I just copied the OnStart() method's and the main loop's contents to main).
Any help would be appreciated.
The Service is written in C# with heavy use of interop. I am using VS2008
You could use a parameter to let your application decide whether to start as service or regular app (i.e. in this case show a Form or start the service):
static void Main(string[] args)
{
if ((1 == args.Length) && ("-runAsApp" == args[0]))
{
Application.Run(new application_form());
}
else
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
}
Now if you pass the parameter "-runAsApp" you can debug the application normally - the SCM won't pass this parameter, so you can also use it as service w/o any code change (provided you derive from ServiceBase)
Edit:
The other difference with windows services is identity (this might be especially important with InterOp) - you want to make sure you are testing under the same identity in "app" mode as well as service mode.
To do so you can use impersonation (I can post a C# wrapper if it helps, but this can be easily googled) in app mode to use the same identity your windows service will be running under i.e. usually LocalService or NetworkService.
If another identity is required you can add settings to the app.config that allow you to decide whether to use credentials, and if so which user to impersonate - these settings would be active when running as app, but turned off for the windows service (since the service is already running under the desired identity):
<appSettings>
<add key="useCredentials" value="false"/>
<add key="user" value="Foo"/>
<add key="password" value="Bar"/>
</appSettings>
I usually just manually set a breakpoint, then point it to the currently open project in c#. The code to set a breakpoint is:
System.Diagnostics.Debugger.Break();
That should get you started, then you can just step through your code and see what's really happening.
I stole this from C. Lawrence Wenham, so I can't really take credit, but you can programmatically attach a debugger to a service, WITHOUT breaking execution at that point, with the following code:
System.Diagnostics.Debugger.Launch();
Put this in your service's OnStart() method, as the first line, and it will prompt you to choose an instance of VS to attach its debugger. From there, the system will stop at breakpoints you set, and on exceptions thrown out. I would put an #if DEBUG clause around the code so a Release build won't include it; or you can just strip it out after you find the problem.
You can use WinDbg/NTSD (another debugger from the "Debugging tools for windows" package) to start a debugger together with your service.
To do this open "gflags" (also available in the above mentioned package) to the "Image file" tab and set the path to debugger executable for your image file (service);
If your service is marked as interactive (only possible if it runs under the SYSTEM account) you can directly start WinDbg, just set the debugger to something like "PATH_TO_WINDBG\windbg.exe -g -G" (the -g / -G are needed so that the debugger doesn't break execution on application start or end - the default behaviour). Now when starting your service the windbg window should pop-up and will catch any unhandled exception.
If your service is not interactive you can start the NTSD debugger (a command line debugger) in remote mode and connect to it from WinDbg (that can even be running in another PC). To do this set the debugger in gflags to something like "PATH_TO_NTSD\ntsd -remote tcp:port=6666,server=localhost". Then connect to the remote debugger by starting windbg with something like "windbg -remote tcp:port=6666,server=localhost" and you should have complete control over the other debugging session.
As for finding the source of the exception itself a windbg tutorial is over the topic here but as a start try to execute the "!analyze -v" command after the exception was caught - with some luck this is all information you'll need..
Note: maybe this is overkill for your case but with this approach you can even debug services during system start-up (I had once a timing problem with a service had an issue only when starting the first time with the system)
One thing I do (which may be kind of a hack) is put a Thread.Sleep(10000) right at the beginning of my OnStart() method. This gives me a 10-second window to attach my debugger to the service before it does anything else.
Of course I remove the Thread.Sleep() statement when I'm done debugging.
One other thing you may do is the following:
public override void OnStart()
{
try
{
// all your OnStart() logic here
}
catch(Exception ex)
{
// Log ex.Message
if (!EventLog.SourceExists("MyApplication"))
EventLog.CreateEventSource("MyApplication", "Application");
EventLog.WriteEntry("MyApplication", "Failed to start: " + ex.Message);
throw;
}
}
When you log ex.Message, you may get a more detailed error message. Furthermore, you could just log ex.ToString() to get the whole stack trace, and if your .pdb files are in the same directory as your executable, it will even tell you what line the Exception occurred on.
Add lots of verbose logging in your OnStart. It's painful and old school, but it works.
Seems like the problem is with the user context. Let me confirm whether my assumptions are right.
When you say that the code works perfectly from console application, I assume you are executing the Console application under the same user which you had logged in.
When you say that the same code crashes while called from the windows service, I assume the service is running in "Local System" account in your development machine.
If both my assumptions are right, please try out the following steps.
In the services listing right-click your service, select properties and then "Log On" tab.
Select the option "This account" and provide the existing username and password.
Now try starting the service. It should now start without any errors.
Following could be the root cause of your error
If you are using SQL Server make sure you are not using SSPI authentication.
If you are trying to read any shared folder\resource which you don't have permission when using "local system" account.
If any of the required dependencies required by the application is in a different folder which the "Local System" user doesn't have permission to access.
If you are using VBA automation which wont work in "Local System" account.
Try disabling your firewall or antivirus.
You could add some logging around the interop calls to find out which one fails.
Also services by default aren't associated with a desktop; if you open the services.msc control panel applet, get the properties of your service, go to the "Log On" tab, you could check "Allow service to interact with desktop". This could fix the problem for you in some cases.
I would assume the reason could be causing because of heavy use of interops. So you need to tackle this problem differently. I would suggest create a windows or console app with same logic of you service and make sure that it works first without any issues, and then you may want to go with creation of the Win service.
Debugging services is a pain, particularly since startup seems to be when many of the problems manifest (at least for us).
What we typically do is extract as much of the logic as possible to a single class that has start and stop methods. Those class methods are all that the service calls directly. We then create a WinForm application that has two buttons: one to invoke start, another to invoke stop. We can then run this WinForm applicaiton directly from the debugger and see what is happening.
Not the most elegant solution, but it works for us.
Check out this question, which discusses how to catch unhandled exceptions in a window service.
In order to attach a debugger to the Windows Service, it needs to be started first. The reason why the service failed to start can be checked in Windows Event Log.
After that the process of attaching a debugger is pretty straight forward from Visual Studio Debug->Attach To Process.
What I've done is implemented by OnStart() to look something like this:
_myBusinessObject = new MyBusinessObject();
After the Business Object has been constructed, timers and IPC handlers do all the real (Service) work.
Doing it like this allows you to create a Forms/WPF application that call the same code above in the Form_Loaded handler. This way, debugging the Forms application is the exact same as debugging the Service.
The only issue is that if you are using app.config values, there will be a second app.config file that needs to be kept up-to-date.
Use following Code in Service OnStart Method:
System.Diagnostics.Debugger.Launch();
Choose Visual Studio option from Pop Up message
read the 2 articles mentioned here:
http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx
Step 1 - Add #if region to your Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new StockInfoService()
};
ServiceBase.Run(ServicesToRun);
#if (!DEBUG)
ServiceBase[] ServicesToRun = new ServiceBase[] { new SqlBackupService() };
ServiceBase.Run(ServicesToRun);
#else
StockInfoService service = new StockInfoService();
service.OnStart();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#endif
}
Step 2 - In Service.cs change your OnStart(string[] args) method without parameter one. (I commended mine.)
public void OnStart()
//protected override void OnStart(string[] args)
{
**Do your thing.
}
Step 3 - Simply hit Start (F5) and debug your code.

Categories