How to provide PIN to access smartcard programmatically? - wcf

I'm using certificates to secure my communications between client and server (no code, just endpoint configuration). Certificates are currently stored in ACOS5 smart cards. Everything works very well except that every time when WCF creates a new channel to access the server, the ACOS5 driver asks user to enter “User PIN”. Unfortunately, it happens quite often.
Is there any way to configure driver to cache PIN that user has already entered within current process at least for some time or how can I cache pin and provide it every time programmatically within same session?
I have found some useful in this article:
This is because in previous versions
of Windows each CSP would cache the
PIN you entered, but Windows 7
actually converts the PIN to a secure
token and caches that. Unfortunately
there’s only one global token cache
but the CSPs can’t use tokens
generated by others, so first the
smart card CSP prompts you and caches
a token, then SSL prompts you and
caches its own token (overwriting the
first one), then the smart card system
prompts you again (because its cached
token is gone).
But I can't use solution that was proposed by author. So what should I do?

This is a way we found and use from many years in our main application:
static class X509Certificate2Extension
{
public static void SetPinForPrivateKey(this X509Certificate2 certificate, string pin)
{
if (certificate == null) throw new ArgumentNullException("certificate");
var key = (RSACryptoServiceProvider)certificate.PrivateKey;
var providerHandle = IntPtr.Zero;
var pinBuffer = Encoding.ASCII.GetBytes(pin);
// provider handle is implicitly released when the certificate handle is released.
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptAcquireContext(ref providerHandle,
key.CspKeyContainerInfo.KeyContainerName,
key.CspKeyContainerInfo.ProviderName,
key.CspKeyContainerInfo.ProviderType,
SafeNativeMethods.CryptContextFlags.Silent));
SafeNativeMethods.Execute(() => SafeNativeMethods.CryptSetProvParam(providerHandle,
SafeNativeMethods.CryptParameter.KeyExchangePin,
pinBuffer, 0));
SafeNativeMethods.Execute(() => SafeNativeMethods.CertSetCertificateContextProperty(
certificate.Handle,
SafeNativeMethods.CertificateProperty.CryptoProviderHandle,
0, providerHandle));
}
}
internal static class SafeNativeMethods
{
internal enum CryptContextFlags
{
None = 0,
Silent = 0x40
}
internal enum CertificateProperty
{
None = 0,
CryptoProviderHandle = 0x1
}
internal enum CryptParameter
{
None = 0,
KeyExchangePin = 0x20
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptAcquireContext(
ref IntPtr hProv,
string containerName,
string providerName,
int providerType,
CryptContextFlags flags
);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CryptSetProvParam(
IntPtr hProv,
CryptParameter dwParam,
[In] byte[] pbData,
uint dwFlags);
[DllImport("CRYPT32.DLL", SetLastError = true)]
internal static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
CertificateProperty propertyId,
uint dwFlags,
IntPtr pvData
);
public static void Execute(Func<bool> action)
{
if (!action())
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
The full post and author is here:
http://www.infinitec.de/post/2010/11/22/Setting-the-PIN-of-a-smartcard-programmatically.aspx

Actually I have found answer on my question: the described behavior caused by bug in Advanced Card Systems CSP v1.9. After switching to Alladin eToken application works as it should.
So I can't provide PIN from code but it is remembered by CSP after entering and providing from code is not required. More good news: user sees PIN request in familiar dialog from CSP in this case.

Related

Accessing a C/C++ structure of callbacks through a DLL's exported function using JNA

I have a vendor supplied .DLL and an online API that I am using to interact with a piece of radio hardware; I am using JNA to access the exported functions through Java (because I don't know C/C++). I can call basic methods and use some API structures successfully, but I am having trouble with the callback structure. I've followed the TutorTutor guide here and also tried Mr. Wall's authoritative guide here, but I haven't been able to formulate the Java side syntax for callbacks set in a structure correctly.
I need to use this exported function:
BOOL __stdcall SetCallbacks(INT32 hDevice,
CONST G39DDC_CALLBACKS *Callbacks, DWORD_PTR UserData);
This function references the C/C++ Structure:
typedef struct{
G39DDC_IF_CALLBACK IFCallback;
//more omitted
} G39DDC_CALLBACKS;
...which according to the API has these Members (Note this is not an exported function):
VOID __stdcall IFCallback(CONST SHORT *Buffer, UINT32 NumberOfSamples,
UINT32 CenterFrequency, WORD Amplitude,
UINT32 ADCSampleRate, DWORD_PTR UserData);
//more omitted
I have a G39DDCAPI.java where I have loaded the DLL library and reproduced the API exported functions in Java, with the help of JNA. Simple calls to that work well.
I also have a G39DDC_CALLBACKS.java where I have implemented the above C/C++ structure in a format works for other API structures. This callback structure is where I am unsure of the syntax:
import java.util.Arrays;
import java.util.List;
import java.nio.ShortBuffer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD.DWORD_PTR;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
public class G39DDC_CALLBACKS extends Structure {
public G39DDC_IF_CALLBACK IFCallback;
//more omitted
protected List getFieldOrder() {
return Arrays.asList(new String[] {
"IFCallback","DDC1StreamCallback" //more omitted
});
}
public static interface G39DDC_IF_CALLBACK extends StdCallCallback{
public void invoke(ShortBuffer _Buffer,int NumberOfSamples,
int CenterFrequency, short Amplitude,
int ADCSampleRate, DWORD_PTR UserData);
}
}
Edit: I made my arguments more type safe as Technomage suggested. I am still getting a null pointer exception with several attempts to call the callback. Since I'm not sure of my syntax regarding the callback structure above, I can't pinpoint my problem in the main below. Right now the relevant section looks like this:
int NumberOfSamples=65536;//This is usually 65536.
ShortBuffer _Buffer = ShortBuffer.allocate(NumberOfSamples);
int CenterFrequency=10000000;//Specifies center frequency (in Hz) of the useful band
//in received 50 MHz wide snapshot.
short Amplitude=0;//The possible value is 0 to 32767.
int ADCSampleRate=100;//Specifies sample rate of the ADC in Hz.
DWORD_PTR UserData = null;
G39DDC_CALLBACKS callbackStruct= new G39DDC_CALLBACKS();
lib.SetCallbacks(hDevice,callbackStruct,UserData);
//hDevice is a handle for the hardware device used-- works in other uses
//lib is a reference to the library in G39DDCAPI.java-- works in other uses
//The UserData is a big unknown-- I don't know what to do with this variable
//as a DWORD_PTR
callbackStruct.IFCallback.invoke(_Buffer, NumberOfSamples, CenterFrequency,
Amplitude, ADCSampleRate, UserData);
EDIT NO 2:
I have one callback working somewhat, but I don't have control over the buffers. More frustratingly, a single call to invoke the method will result in several runs of the custom callback, usually with multiple output files (results vary drastically from run to run). I don't know if it is because I am not allocating memory correctly on the Java side, because I cannot free the memory on the C/C++ side, or because I have no cue on which to tell Java to access the buffer, etc. Relevant code looks like:
//before this, main method sets library, starts DDCs, initializes some variables...
//API call to start IF
System.out.print("Starting IF... "+lib.StartIF(hDevice, Period)+"\n")
G39DDC_CALLBACKS callbackStructure = new G39DDC_CALLBACKS();
callbackStructure.IFCallback = new G39DDC_IF_CALLBACK(){
#Override
public void invoke(Pointer _Buffer, int NumberOfSamples, int CenterFrequency,
short Amplitude, int ADCSampleRate, DWORD_PTR UserData ) {
//notification
System.out.println("Invoked IFCallback!!");
try {
//ready file and writers
File filePath = new File("/users/user/G39DDC_Scans/");
if (!filePath.exists()){
System.out.println("Making new directory...");
filePath.mkdir();
}
String filename="Scan_"+System.currentTimeMillis();
File fille= new File("/users/user/G39DDC_Scans/"+filename+".txt");
if (!fille.exists()) {
System.out.println("Making new file...");
fille.createNewFile();
}
FileWriter fw = new FileWriter(fille.getAbsoluteFile());
//callback body
short[] deBuff=new short[NumberOfSamples];
int offset=0;
int arraySize=NumberOfSamples;
deBuff=_Buffer.getShortArray(offset,arraySize);
for (int i=0; i<NumberOfSamples; i++){
String str=deBuff[i]+",";
fw.write(str);
}
fw.close();
} catch (IOException e1) {
System.out.println("IOException: "+e1);
}
}
};
lib.SetCallbacks(hDevice, callbackStructure,UserData);
System.out.println("Main, before callback invocation");
callbackStructure.IFCallback.invoke(s_Pointer, NumberOfSamples, CenterFrequency, Amplitude, ADCSampleRate, UserData);
System.out.println("Main, after callback invocation");
//suddenly having trouble stopping DDCs or powering off device; assume it has to do with dll using the functions above
//System.out.println("StopIF: " + lib.StopIF(hDevice));//API function returns boolean value
//System.out.println("StopDDC2: " + lib.StopDDC2( hDevice, Channel));
//System.out.println("StopDDC1: " + lib.StopDDC1( hDevice, Channel ));
//System.out.println("test_finishDevice: " + test_finishDevice( hDevice, lib));
System.out.println("Program Exit");
//END MAIN METHOD
You need to extend StdCallCallback, for one, otherwise you'll likely crash when the native code tries to call the Java code.
Any place you see a Windows type with _PTR, you should use a PointerType - the platform package with JNA includes definitions for DWORD_PTR and friends.
Finally, you can't have a primitive array argument in your G39DDC_IF_CALLBACK. You'll need to use Pointer or an NIO buffer; Pointer.getShortArray() may then be used to extract the short[] by providing the desired length of the array.
EDIT
Yes, you need to initialize your callback field in the callbacks structure before passing it into your native function, otherwise you're just passing a NULL pointer, which will cause complaints on the Java or native side or both.
This is what it takes to create a callback, using an anonymous instance of the declared callback function interface:
myStruct.callbackField = new MyCallback() {
public void invoke(int arg) {
// do your stuff here
}
};

Check if WCF(namedpipes) host is available?

Hi,
We have a winform application that is only to be executed as a singelton, If a second instance try to start this new instance will connect to the current and transmit parameters over namedpipes.
The problem is that when starting the first instance there will be a try to connect to existing host. If the host is not existing(like in this case) an exception will be thrown. There is no problem to handle this exception but our developers is often using "Break on Exception" and that means that every time we startup the application the developer will get two(in this case) breaks about exception. Thay will have to hit F5 twice for every start.
Is there any way to check if the service is available without throw exception if its not?
BestRegards
Edit1:
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);
The following code says : Error 152 Cannot implicitly convert type 'System.IntPtr' to 'Orbit.Client.Main.Classes.Controllers.MyClientController.SafeFileMappingHandle'
using (SafeFileMappingHandle fileMappingHandle
= OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
{
If there is already a WCF server listening on the named pipe endpoint, there will be a shared memory object created, via which the server publishes the actual name of the pipe. See here for details of this.
You can check for the existence of this shared memory object with code something like the following, which will not throw, just return false, if there is no server running already. (I've extracted this from code I already have working, and then edited it to do what you want - but without testing the edited version, so apologies if you have to fix up assembly/namespace refs etc to get it running.)
public static class ServiceInstanceChecker
{
public static bool DoesAServerExistAlready(string hostName, string path)
{
return IsNetNamedPipeSharedMemoryMetaDataPublished(DeriveSharedMemoryName(hostName, path));
}
private static string DeriveSharedMemoryName(string hostName, string path)
{
StringBuilder builder = new StringBuilder();
builder.Append(Uri.UriSchemeNetPipe);
builder.Append("://");
builder.Append(hostName.ToUpperInvariant());
builder.Append(path);
byte[] uriBytes = Encoding.UTF8.GetBytes(builder.ToString());
string encodedNameRoot;
if (uriBytes.Length >= 0x80)
{
using (HashAlgorithm algorithm = new SHA1Managed())
{
encodedNameRoot = ":H" + Convert.ToBase64String(algorithm.ComputeHash(uriBytes));
}
}
else
{
encodedNameRoot = ":E" + Convert.ToBase64String(uriBytes);
}
return Uri.UriSchemeNetPipe + encodedNameRoot;
}
private static bool IsNetNamePipeSharedMemoryMetaDataPublished(string sharedMemoryName)
{
const uint FILE_MAP_READ = 0x00000004;
const int ERROR_FILE_NOT_FOUND = 2;
using (SafeFileMappingHandle fileMappingHandle
= OpenFileMapping(FILE_MAP_READ, false, sharedMemoryName))
{
if (fileMappingHandle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
if (ERROR_FILE_NOT_FOUND == errorCode) return false;
throw new Win32Exception(errorCode); // The name matched, but something went wrong opening it
}
return true;
}
}
private class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeFileMappingHandle() : base(true) { }
public SafeFileMappingHandle(IntPtr handle) : base(true) { base.SetHandle(handle); }
protected override bool ReleaseHandle()
{
return CloseHandle(base.handle);
}
}
}
The host name and path you pass in are derived from the WCF service url. Hostname is either a specific hostname (e.g. localhost) or +, or *, depending on the setting for HostNameComparisonMode.
EDIT: You'll also need a couple of P/Invoke declarations for the Win API functions:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileMappingHandle OpenFileMapping(
uint dwDesiredAccess,
bool inheritHandle,
string name
);
EDIT2: We need to tweak the return value of DeriveSharedMemoryName to specify the Local kernel namespace, assuming that your application is not run with elevated privileges. Change the last line of this function to read:
return #"Local\" + Uri.UriSchemeNetPipe + encodedNameRoot;
You also need to specify the hostname parameter correctly to match the hostNameComparisonMode setting used in your binding. As far as I recall, this defaults to StrongWildcard matching in the NetNamedPipeBinding, so you probably need to pass in "+" rather than "localhost".
Can you try to list the named pipes available using
String[] listOfPipes = System.IO.Directory.GetFiles(#"\.\pipe\");
and then determine is your named pipe is amongst them?
My solution is the following :
if (Debugger.IsAttached)
return true;
This will make sure that the code for checking the service is never runned during debugging.
BestRegards

How to use interlocked operations against memory-mapped files in .Net

Is there any way to use the Interlocked.CompareExchange(); and Interlocked.Increment(); methods against values stored in a memory-mapped file?
I'd like to implement a multi-threaded service that will store its data in a memory-mapped file, but since it's multi-threaded I need to prevent conflicting writes, therefore I wonder about the Interlocked operations rather than using explicit locks.
I know it's possible with native code, but can it be done in managed code on .NET 4.0?
OK, this is how you do it! We had to figure this out, and I figured we could give some back to stackoverflow!
class Program
{
internal static class Win32Stuff
{
[DllImport("kernel32.dll", SetLastError = true)]
unsafe public static extern int InterlockedIncrement(int* lpAddend);
}
private static MemoryMappedFile _mmf;
private static MemoryMappedViewStream _mmvs;
unsafe static void Main(string[] args)
{
const int INT_OFFSET = 8;
_mmf = MemoryMappedFile.CreateOrOpen("SomeName", 1024);
// start at offset 8 (just for example)
_mmvs = _mmf.CreateViewStream(INT_OFFSET, 4);
// Gets the pointer to the MMF - we dont have to worry about it moving because its in shared memory
var ptr = _mmvs.SafeMemoryMappedViewHandle.DangerousGetHandle();
// Its important to add the increment, because even though the view says it starts at an offset of 8, we found its actually the entire memory mapped file
var result = Win32Stuff.InterlockedIncrement((int*)(ptr + INT_OFFSET));
}
}
This does work, and works across multiple processes! Always enjoy a good challenge!
TravisWhidden, actually you can use Interlocked.Increment Static method as dan-gph said, you just have to be careful with pointer casting and operator priority, plus parenthesis usage, in facts...
You'll cast a memory pointer (plus the desired offset), into a pointer to an int variable, then you'll use that pointer as a variable. Then you'll have to use it as a variable reference.
Below you'll find the corresponding snippet of yours using .net library instead of external static import.
P&L
class Program
{
private static MemoryMappedFile _mmf;
private static MemoryMappedViewStream _mmvs;
static void Main(string[] args)
{
const int INT_OFFSET = 8;
_mmf = MemoryMappedFile.CreateOrOpen("SomeName", 1024);
_mmvs = _mmf.CreateViewStream(INT_OFFSET, 4);
unsafe
{
IntPtr ptr = _mmvs.SafeMemoryMappedViewHandle.DangerousGetHandle();
Interlocked.Increment(ref (*((int*)(ptr + INT_OFFSET)))
}
}
}

.NET CF - MSMQ ActivateDevice() Crash

I have a .NET 3.5 Compact Framework application that uses MSMQ.
We are running this application on an Intermec CN3, Windows Mobile 5.0 device.
However, when our application first tries to active the MSMQ service with ActivateDevice (pinvoke), the application crashes and we get the error report message:
A problem has occuurred with myApp.exe
Please tell Microsoft about this problem, at not cost to you. ect..
What we have done is this:
Hard Reset the Device
Install NETCFv35.wm.armv4i.cab
Install msmq.arm.CAB
*Run a CF console app that sets up MSMQ and the registry
Soft reset the PDA
*Run our application which calls ActivateDevice() on startup
After doing a soft reset, the first time that ActivateDevice() is called, the application crashes.
However, now that we have called ActivateDevice(), MSMQ services are working on the device atleast until it is soft reset again.
Also, any calls to ActivateDevice() will not crash the application.
The console app that we run after a hard reset is basically this:
class InstallRegister
{
public void Main()
{
RunMsmqAdmin("install");
RunMsmqAdmin("register install");
RunMsmqAdmin("register");
SetQuotaValueRegistry("MachineQuota");
SetQuotaValueRegistry("DefaultLocalQuota");
SetQuotaValueRegistry("DefaultQuota");
RunMsmqAdmin("enable binary");
RunMsmqAdmin("enable srmp");
RunMsmqAdmin("start");
RegFlushKey(0x80000002);
}
private void SetQuotaValueRegistry(string quotaValueName)
{
Microsoft.Win32.Registry.SetValue(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSMQ\\SimpleClient\\"
, quotaValueName
, 100000);
}
private void RunMsmqAdmin(string command)
{
using (Process _process = new Process())
{
_process.StartInfo.FileName = #"\windows\msmqadm.exe";
_process.StartInfo.Arguments = command;
_process.StartInfo.UseShellExecute = true;
_process.Start();
_process.WaitForExit();
}
}
[System.Runtime.InteropServices.DllImport("CoreDll.dll", EntryPoint = "RegFlushKey", SetLastError = true)]
private static extern uint RegFlushKey(uint hKey);
}
Our applications call to ActivateDevice() is basically this:
class ActivateMSMQ
{
public void Active()
{
var handle = ActivateDevice("Drivers\\BuiltIn\\MSMQD", 0);
CloseHandle(handle);
}
[System.Runtime.InteropServices.DllImport("CoreDll.dll", SetLastError = true)]
private static extern IntPtr ActivateDevice(string lpszDevKey, Int32 dwClientInfo);
[System.Runtime.InteropServices.DllImport("CoreDll.dll", SetLastError = true)]
private extern static Int32 CloseHandle(IntPtr hProcess);
}
ActivateDevice() still causes our app the crash whenever the device is soft reset.
Has anyone else experienced this with MSMQ on the compact framework?
Yes this problem occurs. the quick and easy fix for this is to put the code into a separate executable, then on the start of you app launch this process and wait for completion. The process will terminate due to the crash but will return with your calling app still intact. Then just make sure the executable is deployed in your cab so you app can call it.

Regularly invoking an interactive process (WatiN tests) via Task Scheduler

I'm using a suite of WatiN tests driving IE to do some periodic sanity checking for the purposes of monitoring a site.
The suite works fine when I invoke it interactively and/or when I configure the task in Task Scheduler to "Run only when the user is logged on".
However, when I set it to "Run whether the user is logged on or not", and check the "Run with highest privileges" option (WatiN can't talk to the browser satisfactorily under Windows Server 2008 and many other OSes without having admin privileges), WatiN can't communicate with it's iexplore.exe instances satisfactorily (they start, but I get a timeout exception as detailed in this post). I have added the site I'm hitting to the Trusted sites for both admin and non-admin contexts of IE. I've tried with and without elevation, with and without disabling ESC and with and with and without turning off Protected Mode for the internet zone. As my non-GUI tests are happy, I assume it's a limitation of the type of interactivity that's possible in the context of a non-interactive Scheduled Task, even when "Run with highest privileges".
Right now, my temporary workaround is to require a [TS] session to remain open at all times, ready to run the scheduled task.
If I was to persist with this, I'd at a minimum add a heartbeat notification to allow something to monitor that the task is actually getting to run [e.g., if someone logs the session off or reboots the box].
However, I'm looking for something more permanent -- something that is capable of regularly invoking my WatiN tests [run using xunit-console.x86.exe v 1.5] on my Windows Server 2008 [x64] box, just like Task Scheduler but with a proper Interactive session.
I'd prefer not to use psexec or remcom if possible, and can't see how creating a Windows Service would do anything other than add another point of failure but I'd be interested to hear of all proven solutions out there.
I was able to run Watin tests using Scheduled Task in the "Run whether user is logged on or not" mode.
In my case I tracked the issue down to m_Proc.MainWindowHandle being always 0 when IE is created from a scheduled task running without a logged on user.
In Watin sources this is in the IE.cs:CreateIEPartiallyInitializedInNewProcess function
My workaround is to manually enumerate top level windows and find a window with className == "IEFrame" that belongs to the process instead of using Process.MainWindowHandle property.
Here is the code snippet. All pinvoke i copied directly from Watin source.
public static class IEBrowserHelper
{
private static Process CreateIExploreInNewProcess()
{
var arguments = "about:blank";
arguments = "-noframemerging " + arguments;
var m_Proc = Process.Start("IExplore.exe", arguments);
if (m_Proc == null) throw new WatiN.Core.Exceptions.WatiNException("Could not start IExplore.exe process");
return m_Proc;
}
class IeWindowFinder
{
#region Interop
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32", EntryPoint = "GetClassNameA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
internal static extern int GetClassName(IntPtr handleToWindow, StringBuilder className, int maxClassNameLength);
#endregion
readonly Process IeProcess;
IntPtr HWnd = IntPtr.Zero;
public IeWindowFinder(Process ieProcess)
{
this.IeProcess = ieProcess;
}
public IntPtr Find()
{
EnumWindows(FindIeWindowCallback, IntPtr.Zero);
return HWnd;
}
bool FindIeWindowCallback(IntPtr hWnd, IntPtr lParam)
{
uint processId;
GetWindowThreadProcessId(hWnd, out processId);
if (processId == IeProcess.Id)
{
int maxCapacity = 255;
var sbClassName = new StringBuilder(maxCapacity);
var lRes = GetClassName(hWnd, sbClassName, maxCapacity);
string className = lRes == 0 ? String.Empty : sbClassName.ToString();
if (className == "IEFrame")
{
this.HWnd = hWnd;
return false;
}
}
return true;
}
}
public static WatiN.Core.IE CreateIEBrowser()
{
Process ieProcess = CreateIExploreInNewProcess();
IeWindowFinder findWindow = new IeWindowFinder(ieProcess);
var action = new WatiN.Core.UtilityClasses.TryFuncUntilTimeOut(TimeSpan.FromSeconds(WatiN.Core.Settings.AttachToBrowserTimeOut))
{
SleepTime = TimeSpan.FromMilliseconds(500)
};
IntPtr hWnd = action.Try(() =>
{
return findWindow.Find();
});
ieProcess.Refresh();
return WatiN.Core.IE.AttachTo<WatiN.Core.IE>(
new WatiN.Core.Constraints.AttributeConstraint("hwnd", hWnd.ToString()), 5);
}
}
then instead of new IE() use IEBrowserHelper.CreateIEBrowser()
From the command-prompt, you can schedule an interactive task like this:
C:\Users\someUser>schtasks /create /sc once /st 16:28 /tn someTask /tr cmd.exe
... where sc is the schedule, st is the start-time, tn is the taskname you choose (can be anything), and tr is the command you want to run. Obviously, for a recurring task, you would change sc to monthly, weekly, etc. Just type "schtasks /create /?" for more info.