how to set a limited time to my console application - c#-3.0

I was thinking how to set a limited period on my console application.
For instance, I want to give just 10 seconds to users to choose or do something when they just come in.
If time exceeds 10 seconds, then the user should get a warning.
Here is my code. I tried to first convert it to string and then to int, but something is wrong...
using System;
using System.Threading;
namespace exercise
{
class Program
static void Main(string[] args)
{
Timer t = new Timer(TimerCallback, null, 0, 2000);
// Wait for the user to hit <Enter>
Console.ReadLine();
}
private static void TimerCallback(Object o)
{
bool loop = true; // Console.WriteLine("{0:h:mm:ss.fff}.\n", DateTime.Now); ----> Creating timer
string b = DateTime.Now.ToString();
// Display the date/time when this method got called.
Console.WriteLine("In TimerCallback: " + b);
int c = Convert.ToInt32(b);
// Force a garbage collection to occur for this demo.
if (c == 10000)
{
Console.WriteLine(" your time is runnig out , hurry up brow");
}
GC.Collect();
}
}

You can use DateTime.ParseExact("2009-05-08 14:40:52,531", "yyyy-MM-dd HH:mm:ss,fff"
to pass a string to datetime, but you will parse the datetime, not the amounts of time passed.
You can store the number of calls in a static field and check the 10 seconds that way.
class Program
{
private static int timerCounter = 1;
static void Main(string[] args)
{
Timer t = new Timer(TimerCallback, null, 0, 2000);
// Wait for the user to hit <Enter>
Console.ReadLine();
}
private static void TimerCallback(Object o)
{
bool loop = true; // Console.WriteLine("{0:h:mm:ss.fff}.\n", DateTime.Now); ----> Creating timer
string b = DateTime.Now.ToString();
// Display the date/time when this method got called.
Console.WriteLine("In TimerCallback: " + b);
// timer callback is every 2 seconds, so every 5th call is 10 sec
if (timerCounter % 5 == 0)
{
timerCounter = 1;
Console.WriteLine(" your time is runnig out , hurry up brow");
}
timerCounter++;
GC.Collect();
}
}

Related

Ways to avoid having to sleep()/delay() your Katalon Studio code

I have these test cases, see WebUI.delay() and sleep() everywhere, but they break as soon as I remove those delays...
How can I avoid having to delay my test cases?
There are very few legitimate cases where you would want to delay() your test case, or put it to sleep()...
Main legit use case for this, is to poll an API until you get some expected result from it...or to do some performance testing (namely to simulate an impatient user).
It is a code smell, because it:
is literally stopping the execution of your test case for some hard-coded amount of time, which means:
a lot of wasted time, or
situations where "it works on my machine (but not yours)!"
is a brute force solution to something where either a built-in waitForXXX() method exist, or some simple custom keyword can be written to solve the problem
Yet there are so many people using those methods because there seems to be no other choice...
How can we prevent these?
Let's go look at some examples:
Examples of preventable uses of delay()/sleep()
Keep in mind, most if not all of these are based on what I've had to face in testing a Zoho app, and based on my complete overhauling of the Katalon project for that...
With that in mind, let's take a look at the first use case I encountered when being tasked with that project...
Elements that are supposed to be on the page (especially after page load)
Bad way
The laziest way is to just do this:
WebUI.navigateTo(homePage);
WebUI.delay(5);
WebUI.click(findTestObject("Home page/Dashboard item"));
Better way
This is better way to do it...
WebUI.navigateTo(homePage);
final TestObject loader = findTestObject("Home page/loader");
WebUI.waitForElementVisible(loader, 2, FailureHandling.OPTIONAL);
WebUI.waitForElementNotVisible(loader, 5);
final TestObject dashboardItem = findTestObject("Home page/Dashboard item");
WebUI.waitForElementVisible(dashboardItem, 2);
WebUI.click(dashboardItem);
Custom widgets
Auto-complete
In Zoho (namely CRM), an auto-complete has three major parts:
text field
loading icon
dropdown list
Bad way
A noob may handle an autocomplete like:
WebUI.sendKeys(findTestObject("Page/My Autocomplete/input field"), "some text");
WebUI.delay(3); // DON'T DO THIS, PLEASE!!
WebUI.click(findTestObject("Page/My Autocomplete/first dropdown option"));
Heaven forbid it be a more complicated use case, where the autocomplete be fetch-on-scroll, and the dropdown option you're looking for be on the nth page, for n > 1.
Then what will you do? Some scroll, then some hard-coded delay(), inside for loop?
Better way
This strategy-based way involves a lot more code, but is way more correct:
We're going to create Custom Keyword for it!
public final class GeneralWebUIUtils {
public static void HandleAutoComplete(TestObject textField, String input, TestObject loader, TestObject dropdownOption, BaseSelectionStrategy strategy = null) throws StepFailedException {
WebUI.click(textField)
WebUI.sendKeys(textField, input)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementNotVisible(loader, 3)
},
"Loader",
"disappear");
TimeLoggerUtil.LogAction({
return WebUI.waitForElementPresent(dropdownOption, 3, FailureHandling.STOP_ON_FAILURE);
},
"Dropdown option",
"become present")
BaseSelectionStrategy selectionStrategy = strategy;
if (strategy == null)
selectionStrategy = new BaseSelectionStrategy(input);
selectionStrategy.doSelect(dropdownOption);
}
public static boolean WaitForElementCondition(Closure<Boolean> onCheckCondition, Closure onContinue, TestObject to, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
final long startTime = System.currentTimeMillis()
boolean isConditionSatisfied = false;
while ((System.currentTimeMillis() < startTime + timeOut * 1000) && (!isConditionSatisfied)) {
isConditionSatisfied = WebUI.waitForElementPresent(to, 1, failureHandling) && onCheckCondition(to);
if (onContinue != null)
onContinue(isConditionSatisfied, to);
}
if ((!isConditionSatisfied) && (failureHandling.equals(FailureHandling.STOP_ON_FAILURE))) {
KeywordUtil.markFailedAndStop("Condition for TestObject '${to.getObjectId()}' not met after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
return isConditionSatisfied;
}
public static boolean WaitForElementHasText(TestObject to, String expectedText, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForElementCondition({ TestObject testObj ->
return WebUI.getText(testObj).contains(expectedText);
}, { boolean success, TestObject testObj ->
if (!success) {
WebUI.waitForElementNotPresent(testObj, 1, FailureHandling.OPTIONAL);
WebUI.waitForElementPresent(testObj, timeOut);
}
},
to,
timeOut,
failureHandling);
}
}
public class BaseSelectionStrategy {
protected final String input;
public BaseSelectionStrategy(String input) {
this.input = input;
}
public void doSelect(TestObject dropdownOption) {
this.waitForDropdownOption(dropdownOption);
WebUI.click(dropdownOption);
}
public boolean waitForDropdownOption(TestObject dropdownOption, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return GeneralWebUIUtils.WaitForElementHasText(dropdownOption, input, this.getWaitTime(), failureHandling);
}
public int getWaitTime() {
return 5;
}
}
// this is only here to allow time-logging of an action
public final class TimeLoggerUtil {
public static boolean LogAction(Closure<Boolean> onAction, String elementDesc, String expectationDesc) throws StepFailedException {
final long startTime = System.currentTimeMillis();
try {
if (onAction()) {
KeywordUtil.logInfo("${elementDesc} took ${(System.currentTimeMillis() - startTime) / 1000} seconds to ${expectationDesc}");
return true;
} else {
KeywordUtil.markWarning("${elementDesc} didn't ${expectationDesc} after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
} catch (StepFailedException ex) {
KeywordUtil.markFailedAndStop("${elementDesc} didn't ${expectationDesc} after ${(System.currentTimeMillis() - startTime) / 1000} seconds...\n${ex.getMessage()}");
throw ex;
}
return false;
}
}
Then, to use it, simply be like:
GeneralWebUIUtils.HandleAutoComplete(
findTestObject("Page/My Autocomplete/input field"),
"some text", // or whatever
findTestObject("Page/My Autocomplete/loader"),
findTestObject("Page/My Autocomplete/first dropdown option"),
);
I will admit, this is a lot more code, but is way more of a robust solution as it will not hang your test case up!
What, your autocomplete is fetch-on-scroll? No problem!
Simply create a BaseScrollableSelectionStrategy (and an ActionHandler):
#InheritConstructors
// feel free to extend this class as you see fit
public class BaseScrollableSelectionStrategy extends BaseSelectionStrategy {
#Override
public void doSelect(TestObject dropdownOption) {
final long startTime = System.currentTimeMillis();
ActionHandler.Handle({
this.waitForDropdownOption(dropdownOption);
}, { boolean success, _ ->
KeywordUtil.logInfo("${dropdownOption.getObjectId()} ${this.getActionStatus(success, startTime)}")
if (!success) {
final TestObject lastAvailableDropdownItem = new TestObject("Last available dropdown item")
.addProperty("xpath",
ConditionType.EQUALS,
"//lyte-drop-box[not(contains(concat(' ', #class, ' '), ' lyteDropdownHidden '))]//lyte-drop-item[last()]"); // TODO: change this to the XPath for the last dropdown option on your auto complete drop-down
TimeLoggerUtil.LogAction({
if (!WebUI.waitForElementPresent(lastAvailableDropdownItem, 2))
return false;
GeneralWebUIUtils.ScrollDropdownOptionIntoView(lastAvailableDropdownItem);
return true;
}, lastAvailableDropdownItem.getObjectId(),
"scroll into view");
}
}, 15)
GeneralWebUIUtils.ScrollDropdownOptionIntoView(dropdownOption);
WebUI.click(dropdownOption);
}
protected String getActionStatus(boolean success, long startTime) {
if (success)
return "took ${(System.currentTimeMillis() - startTime) / 1000} seconds to show up";
return "didn't show up after ${(System.currentTimeMillis() - startTime) / 1000} seconds";
}
#Override
public int getWaitTime() {
return 2;
}
}
public class ActionHandler {
public static void Handle(Closure onAction, Closure onDone, long timeOut) {
long startTime = System.currentTimeSeconds();
while (System.currentTimeSeconds() < startTime + timeOut) {
try {
onDone(true, onAction());
return;
} catch (Exception ex) {
onDone(false, ex);
}
}
}
}
add a keyword for scrolling the dropdown option into view:
public static void ScrollDropdownOptionIntoView(TestObject to) {
WebUI.executeJavaScript("arguments[0].scrollIntoView({block: 'center'})", [WebUiCommonHelper.findWebElement(to, 3)])
WebUI.waitForElementVisible(to, 2)
}
and then your use case should look like:
final String textFieldInput = "some text" // or whatever
GeneralWebUIUtils.HandleAutoComplete(
findTestObject("Page/My Autocomplete/input field"),
textFieldInput,
findTestObject("Page/My Autocomplete/loader"),
findTestObject("Page/My Autocomplete/first dropdown option"),
new BaseScrollableSelectionStrategy(textFieldInput), // or whatever your derived scrollable selection strategy is
);
New Table Row
This is yet another unjustified use case that I had to deal with, this one much earlier on in the project. It was something like this:
Bad way
WebUI.click(findTestObject("Page/Add Button"));
WebUI.delay(3);
WebUI.click(findTestObject("Page/Second Row Dropdown button"));
Better way
WebUI.click(findTestObject("Page/Add Button"));
final TestObject secondRowDropdownBtn = findTestObject("Page/Second Row Dropdown button");
WebUI.waitForElementPresent(secondRowDropdownBtn, 3);
WebUI.scrollToElement(secondRowDropdownBtn, 2);
WebUI.click(secondRowDropdownBtn);
...or better yet...
WebUI.click(findTestObject("Page/Add Button"));
final TestObject secondRowDropdown = findTestObject("Page/Second Row Dropdown button");
WebUI.waitForElementPresent(secondRowDropdown, 3);
WebUI.scrollToElement(secondRowDropdown, 2);
WebUI.click(findTestObject("Page/Second Row Dropdown button"));
Saving a model to be created, and then doing/checking something on the next page
This is a common one. You are about to click "Submit", to create some record (item/rate card/discount/member/...)...
You wait for the button to disable...
...then you try that WebUI.waitForPageLoad(15); between waiting for the button to disappear, and waiting for the next page to fully load...
Test case fails! WHY.png
Bad way
You pull your hair out in frustration, and then give into that temptation to instead do WebUI.delay(15)...
It works, but it's slow?!
You now have to run that test case to create 10+ items/rate cards/discounts/members/... per a data file...
AnyMinuteNow.gif
Better way
This way handles both the save button and the URL change. It will require more custom keywords to our GeneralWebUIUtils:
public final class GeneralWebUIUtils {
private static boolean WaitForURLCondition(Closure<Boolean> onCheckCondition, int timeOut, Closure<String> onErrorMessage, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
final long startTime = System.currentTimeMillis()
boolean isConditionSatisfied = false;
while ((System.currentTimeMillis() < startTime + timeOut * 1000) && (!isConditionSatisfied)) {
isConditionSatisfied = onCheckCondition(WebUI.getUrl())
}
if ((!isConditionSatisfied) && (failureHandling.equals(FailureHandling.STOP_ON_FAILURE))) {
KeywordUtil.markFailedAndStop("${onErrorMessage(WebUI.getUrl())} after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
return isConditionSatisfied;
}
public static boolean WaitForURLNotEquals(String url, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForURLCondition({ String browserURL ->
return !(browserURL =~ SMDStringUtils.GetURLPattern(url)).matches();
},
timeOut, { String browserURL ->
"URL '${browserURL}' matches unexpected '${url}'"
},
failureHandling)
}
public static boolean WaitForURLEquals(String url, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForURLCondition({ String browserURL ->
return (browserURL =~ SMDStringUtils.GetURLPattern(url)).matches();
},
timeOut, { String browserURL ->
"URL '${browserURL}' does not match expected '${url}'"
},
failureHandling)
}
public static void HandleSaveButton(TestObject saveButton) throws StepFailedException {
this.HandleSaveButton(saveButton, true);
}
public static void HandleSaveButton(TestObject saveButton, boolean shouldSuccessfullySave) throws StepFailedException {
WebUI.scrollToElement(saveButton, 3)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementClickable(saveButton, 3, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"become clickable");
WebUI.click(saveButton)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementHasAttribute(saveButton, GeneralWebUIUtils.DISABLED, 5, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"disable");
if (shouldSuccessfullySave) {
TimeLoggerUtil.LogAction({
return WebUI.waitForElementNotPresent(saveButton, 5, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"disappear from the DOM");
TimeLoggerUtil.LogAction({
WebUI.waitForPageLoad(5);
return true;
},
"Page",
"load")
}
}
}
public final class SMDStringUtils {
public static String GetURLPattern(String url) {
return (/^(http(s)?\:\/\/)?${url}(\/)?$/);
}
}
In the test case, we just do:
// submit the changes
GeneralWebUIUtils.HandleSaveButton(findTestObject('Page/Submit button'))
// verify that the save happened successfully
GeneralWebUIUtils.WaitForURLNotEquals(creationPageURL, 15) // creationPageURL is the URL of the creation page you were just on
or better yet, if you had to click some "Add" button on some initial page, and submit button is supposed to end up sending you back to that initial page:
// submit the changes
GeneralWebUIUtils.HandleSaveButton(findTestObject('Page/Submit button'))
// verify that the save happened successfully
GeneralWebUIUtils.WaitForURLEquals(initPageURL, 15) // initPageURL is the URL of the initial page you were just on
Thoughts? Concerns?
Let me know in the comments section below!

Start highest unlocked level(scene) on button click

Im new in developing so need some help for my game!
On my game I have 2 buttons one is "Play" and other "Level Select"
I stuck at the "Play" button, need to make a script that is always loading the highest level that is unlocked, not current but highest.
Here is the code that im using for level manager
public List<Button> levelButton;
public Sprite lockimage;
public bool delete;
private void Start()
{
int saveIndex = PlayerPrefs.GetInt("SaveIndex");
for (int i = 0; i < levelButton.Count; i++)
{
if (i <= saveIndex)
{
levelButton[i].interactable = true;
}
else
{
levelButton[i].interactable = false;
levelButton[i].GetComponent<Image>().sprite = lockimage;
}
}
}
public void LevelSelect()
{
int level = int.Parse(EventSystem.current.currentSelectedGameObject.name);
SceneManager.LoadScene(level);
}
public void PlayGame()
{
//code here
}
public void ResetGame()
{
PlayerPrefs.SetInt("SaveIndex", 0);
SceneManager.LoadScene(0);
}
public void DontResetGame()
{
SceneManager.LoadScene(0);
}
}
SceneManager.LoadScene(PlayerPrefs.GetInt("SaveIndex"));
edit: adding some info/context.
I realized that you set level buttons interactivity on the start functions based on the int save_index value you get from PlayerPrefs.
From there, I assumed that you could load the level directly using that same value on the PlayGame function.
Do note that the code I wrote will throw an error, if the "SaveIndex" key is not yet on PlayerPrefs

why esper_ext timed does not filter out old entries

I need help with understanding of the win_ext window in Esper (CEP). I'm wondering why older (first 2) events still popup on the update-method even though they have been "expired"
public class MyCepTest {
public static void main(String...args) throws Exception{
System.out.println("starting");
MyCepTest ceptest = new MyCepTest();
ceptest.execute();
System.out.println("end");
}
public void execute() throws Exception{
Configuration config = new Configuration();
config.addEventType(MyPojo.class);
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);
EPAdministrator admin = epService.getEPAdministrator();
EPStatement x1 = admin.createEPL(win);
EPStatement x2 = admin.createEPL(win2);
x1.setSubscriber(this);
x2.setSubscriber(this);
EPRuntime runtime = epService.getEPRuntime();
ArrayList<MyPojo> staffToSendToCep = new ArrayList<MyPojo>();
staffToSendToCep.add(new MyPojo(1, new Date(1490615719497L)));
staffToSendToCep.add(new MyPojo(2, new Date(1490615929497L)));
for(MyPojo pojo : staffToSendToCep){
runtime.sendEvent(pojo);
}
Thread.sleep(500);
System.out.println("round 2...");//why two first Pojos are still found? Shouldn't ext_timed(pojoTime.time, 300 seconds) rule them out?
staffToSendToCep.add(new MyPojo(3, new Date(1490616949497L)));
for(MyPojo pojo : staffToSendToCep){
runtime.sendEvent(pojo);
}
}
public void update(Map<String,Object> map){
System.out.println(map);
}
public static String win = "create window fiveMinuteStuff.win:ext_timed(pojoTime.time, 300 seconds)(pojoId int, pojoTime java.util.Date)";
public static String win2 = "insert into fiveMinuteStuff select pojoId,pojoTime from MyPojo";
}
class MyPojo{
int pojoId;
Date pojoTime;
MyPojo(int pojoId, Date date){
this.pojoId = pojoId;
this.pojoTime = date;
}
public int getPojoId(){
return pojoId;
}
public Date getPojoTime(){
return pojoTime;
}
public String toString(){
return pojoId+"#"+pojoTime;
}
}
I've been puzzled with this for a while and help would be greatly appreciated
See the processing model in docs. http://espertech.com/esper/release-6.0.1/esper-reference/html/processingmodel.html
All incoming insert-stream events are delivered to listeners and subscribers. regardless of your window. A window, if one is in the query at all, defines the subsets of events to consider and therefore defines what gets aggregated, pattern-matched or is available for iteration. Try "select * from MyPojo" for reference. My advice to read up on external time, see http://espertech.com/esper/release-6.0.1/esper-reference/html/api.html#api-controlling-time
Usually when you want "external time window" you want event time to drive engine time.

Android Activity and Service communication: how to keep the UI up-to-date

I have an Activity A (not the main Activity) that launches a Service S that does some stuff in the background and, in the meanwhile, should made some changes to the UI of A.
Let's just say that S count from 0 to 100 and A should display this count in Real-Time. Since the real job of S is quite more complicated and CPU-consuming, I do not want to use AsyncTask for it (indeed "AsyncTasks should ideally be used for short operations (a few seconds at the most.) [...]") but just a regular Service started in a new Thread (an IntentService would be fine as well).
This is my Activity A:
public class A extends Activity {
private static final String TAG = "Activity";
private TextView countTextView; // TextView that shows the number
Button startButton; // Button to start the count
BResultReceiver resultReceiver;
/**
* Receive the result from the Service B.
*/
class BResultReceiver extends ResultReceiver {
public BResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch ( resultCode ) {
case B.RESULT_CODE_COUNT:
String curCount = resultData.getString(B.RESULT_KEY_COUNT);
Log.d(TAG, "ResultReceived: " + curCount + "\n");
runOnUiThread( new UpdateUI(curCount) ); // NOT WORKING AFTER onResume()!!!
break;
}
}
}
/**
* Runnable class to update the UI.
*/
class UpdateUI implements Runnable {
String updateString;
public UpdateUI(String updateString) {
this.updateString = updateString;
}
public void run() {
countTextView.setText(updateString);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.counter);
countTextView = (TextView) findViewById(R.id.countTextView);
startButton = (Button) findViewById(R.id.startButton);
resultReceiver = new BResultReceiver(null);
}
public void startCounting(View view) {
startButton.setEnabled(false);
//Start the B Service:
Intent intent = new Intent(this, B.class);
intent.putExtra("receiver", resultReceiver);
startService(intent);
}
}
And this is my Service B:
public class B extends Service {
private static final String TAG = "Service";
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private ResultReceiver resultReceiver;
private Integer count;
static final int RESULT_CODE_COUNT = 100;
static final String RESULT_KEY_COUNT = "Count";
/**
* Handler that receives messages from the thread.
*/
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
while ( count < 100 ) {
count++;
//Sleep...
sendMessageToActivity(RESULT_CODE_COUNT, RESULT_KEY_COUNT, count.toString());
}
//Stop the service (using the startId to avoid stopping the service in the middle of handling another job...):
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
//Start up the thread running the service:
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
this.count = 0;
//Get the HandlerThread's Looper and use it for our Handler
serviceLooper = thread.getLooper();
serviceHandler = new ServiceHandler(serviceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.resultReceiver = intent.getParcelableExtra("receiver");
//For each start request, send a message to start a job and deliver the start ID so we know which request we're stopping when we finish the job:
Message msg = serviceHandler.obtainMessage();
msg.arg1 = startId;
serviceHandler.sendMessage(msg);
//If we get killed, after returning from here, restart:
return START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Send a message from to the activity.
*/
protected void sendMessageToActivity(Integer code, String name, String text) {
Bundle bundle = new Bundle();
bundle.putString(name, text);
//Send the message:
resultReceiver.send(code, bundle);
}
}
Everything works fine but if I click the back button (or the home button) and then I re-open the Activity A then the UI of A is not updated anymore (it just shows the initial configuration of A - i.e. the "startButton" is still clickable and the count is not showed - it seems runOnUiThread(...) is not working anymore). However, the Service B is still running in the background and the I can see the correct count is passed to the Activity A in the Log.d(...). Finally, if I click on the "startButton" again, the counting does not start from the beginning (0) but from where B has been arrived (I've double checked it by showing it in the notification bar).
How can I fix this behaviour? I would like that, when I re-open the Activity A, it automatically continues to receive and update the data from the Service B. Or, in other words, that the Service keeps the UI of the Activity A up-to-date.
Please give me some hints, links or piece of code. Thanks!
When you click back button your Activity is destroyed. When you start the Activity again you get a new Activity. That also happen when you rotate the device. This is Android lifecycle event
The Activity is not good for heavy Business Logic only to show stuff/control stuf.
What you have to do is create a simple MVC, Model View Controller. The view (Activity) should only be used for showing results and controlling the eventflow.
The Service can hold an Array of the count and when your Activity start it will onBind() your Service that is running (or if not running will start the Service since you bind to it) Let the Activity(View) get the Array of results and show it. This simple setup exclude the (M)Model Business Logic.
Update
Following up a bit read this it's Android official docs and perfect start since it do kind of what you asking. As you see in the example in the onStart() the Activity establish a connection with the service and in the onStop() the connection is removed. There's no point having a connection after on onStop(). Just like you asking for. I would go with this setup and not let the Service continuously sending data because that would drain resources and the Activity is not always listening because it will stop when in the background.
Here's an activity that binds to LocalService and calls getRandomNumber() when a button is clicked:

Powershell monitoring for file system changes [duplicate]

I have an application where I am looking for a text file and if there are any changes made to the file I am using the OnChanged eventhandler to handle the event. I am using the NotifyFilters.LastWriteTime but still the event is getting fired twice. Here is the code.
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
}
In my case the OnChanged is called twice, when I change the text file version.txt and save it.
I am afraid that this is a well-known bug/feature of the FileSystemWatcher class. This is from the documentation of the class:
You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.
Now this bit of text is about the Created event, but the same thing applies to other file events as well. In some applications you might be able to get around this by using the NotifyFilter property, but my experience is says that sometimes you have to do some manual duplicate filtering (hacks) as well.
A while ago I bookedmarked a page with a few FileSystemWatcher tips. You might want to check it out.
I've "fixed" that problem using the following strategy in my delegate:
// fsw_ is the FileSystemWatcher instance used by my application.
private void OnDirectoryChanged(...)
{
try
{
fsw_.EnableRaisingEvents = false;
/* do my stuff once asynchronously */
}
finally
{
fsw_.EnableRaisingEvents = true;
}
}
Any duplicated OnChanged events from the FileSystemWatcher can be detected and discarded by checking the File.GetLastWriteTime timestamp on the file in question. Like so:
DateTime lastRead = DateTime.MinValue;
void OnChanged(object source, FileSystemEventArgs a)
{
DateTime lastWriteTime = File.GetLastWriteTime(uri);
if (lastWriteTime != lastRead)
{
doStuff();
lastRead = lastWriteTime;
}
// else discard the (duplicated) OnChanged event
}
Here is my solution which helped me to stop the event being raised twice:
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
Here I have set the NotifyFilter property with only Filename and size.
watcher is my object of FileSystemWatcher. Hope this will help.
Here's my approach :
// Consider having a List<String> named _changedFiles
private void OnChanged(object source, FileSystemEventArgs e)
{
lock (_changedFiles)
{
if (_changedFiles.Contains(e.FullPath))
{
return;
}
_changedFiles.Add(e.FullPath);
}
// do your stuff
System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
{
lock (_changedFiles)
{
_changedFiles.Remove(e.FullPath);
}
};
timer.Start();
}
This is the solution I used to solve this issue on a project where I was sending the file as attachment in a mail.
It will easily avoid the twice fired event even with a smaller timer interval but in my case 1000 was alright since I was happier with missing few changes than with flooding the mailbox with > 1 message per second.
At least it works just fine in case several files are changed at the exact same time.
Another solution I've thought of would be to replace the list with a dictionary mapping files to their respective MD5, so you wouldn't have to choose an arbitrary interval since you wouldn't have to delete the entry but update its value, and cancel your stuff if it hasn't changed.
It has the downside of having a Dictionary growing in memory as files are monitored and eating more and more memory, but I've read somewhere that the amount of files monitored depends on the FSW's internal buffer, so maybe not that critical.
Dunno how MD5 computing time would affect your code's performances either, careful =\
My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :
private Hashtable fileWriteTime = new Hashtable();
private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
string path = e.FullPath.ToString();
string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();
// if there is no path info stored yet
// or stored path has different time of write then the one now is inspected
if ( !fileWriteTime.ContainsKey(path) ||
fileWriteTime[path].ToString() != currentLastWriteTime
)
{
//then we do the main thing
log( "A CHANGE has occured with " + path );
//lastly we update the last write time in the hashtable
fileWriteTime[path] = currentLastWriteTime;
}
}
Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.
I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done. It discards all the changed events exept the last and it raise it only when the file become available for read.
Download FileSystemSafeWatcher and add it to your project.
Then use it as a normal FileSystemWatcher and monitor when the events are triggered.
var fsw = new FileSystemSafeWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;
Try with this code:
class WatchPlotDirectory
{
bool let = false;
FileSystemWatcher watcher;
string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";
public WatchPlotDirectory()
{
watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "*.*";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
watcher.EnableRaisingEvents = true;
}
void OnChanged(object sender, FileSystemEventArgs e)
{
if (let==false) {
string mgs = string.Format("File {0} | {1}",
e.FullPath, e.ChangeType);
Console.WriteLine("onchange: " + mgs);
let = true;
}
else
{
let = false;
}
}
void OnRenamed(object sender, RenamedEventArgs e)
{
string log = string.Format("{0} | Renamed from {1}",
e.FullPath, e.OldName);
Console.WriteLine("onrenamed: " + log);
}
public void setPath(string path)
{
this.path = path;
}
}
I know this is an old issue, but had the same problem and none of the above solution really did the trick for the problem I was facing. I have created a dictionary which maps the file name with the LastWriteTime. So if the file is not in the dictionary will go ahead with the process other wise check to see when was the last modified time and if is different from what it is in the dictionary run the code.
Dictionary<string, DateTime> dateTimeDictionary = new Dictionary<string, DateTime>();
private void OnChanged(object source, FileSystemEventArgs e)
{
if (!dateTimeDictionary.ContainsKey(e.FullPath) || (dateTimeDictionary.ContainsKey(e.FullPath) && System.IO.File.GetLastWriteTime(e.FullPath) != dateTimeDictionary[e.FullPath]))
{
dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);
//your code here
}
}
One possible 'hack' would be to throttle the events using Reactive Extensions for example:
var watcher = new FileSystemWatcher("./");
Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
.Throttle(new TimeSpan(500000))
.Subscribe(HandleChangeEvent);
watcher.EnableRaisingEvents = true;
In this case I'm throttling to 50ms, on my system that was enough, but higher values should be safer. (And like I said, it's still a 'hack').
I spent some significant amount of time using the FileSystemWatcher, and some of the approaches here will not work. I really liked the disabling events approach, but unfortunately, it doesn't work if there is >1 file being dropped, second file will be missed most if not all times.
So I use the following approach:
private void EventCallback(object sender, FileSystemEventArgs e)
{
var fileName = e.FullPath;
if (!File.Exists(fileName))
{
// We've dealt with the file, this is just supressing further events.
return;
}
// File exists, so move it to a working directory.
File.Move(fileName, [working directory]);
// Kick-off whatever processing is required.
}
I have a very quick and simple workaround here, it does work for me, and no matter the event would be triggered once or twice or more times occasionally, check it out:
private int fireCount = 0;
private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
{
fireCount++;
if (fireCount == 1)
{
MessageBox.Show("Fired only once!!");
dowork();
}
else
{
fireCount = 0;
}
}
}
Here is a new solution you can try. Works well for me. In the event handler for the changed event programmatically remove the handler from the designer output a message if desired then programmatically add the handler back. example:
public void fileSystemWatcher1_Changed( object sender, System.IO.FileSystemEventArgs e )
{
fileSystemWatcher1.Changed -= new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
MessageBox.Show( "File has been uploaded to destination", "Success!" );
fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
}
The main reason was
first event's last access time was current time(file write or changed time).
then second event was file's original last access time.
I solve under code.
var lastRead = DateTime.MinValue;
Watcher = new FileSystemWatcher(...)
{
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
Filter = "*.dll",
IncludeSubdirectories = false,
};
Watcher.Changed += (senderObject, ea) =>
{
var now = DateTime.Now;
var lastWriteTime = File.GetLastWriteTime(ea.FullPath);
if (now == lastWriteTime)
{
return;
}
if (lastWriteTime != lastRead)
{
// do something...
lastRead = lastWriteTime;
}
};
Watcher.EnableRaisingEvents = true;
This code worked for me.
private void OnChanged(object source, FileSystemEventArgs e)
{
string fullFilePath = e.FullPath.ToString();
string fullURL = buildTheUrlFromStudyXML(fullFilePath);
System.Diagnostics.Process.Start("iexplore", fullURL);
Timer timer = new Timer();
((FileSystemWatcher)source).Changed -= new FileSystemEventHandler(OnChanged);
timer.Interval = 1000;
timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
timer.Start();
}
private void t_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer)sender).Stop();
theWatcher.Changed += new FileSystemEventHandler(OnChanged);
}
mostly for future me :)
I wrote a wrapper using Rx:
public class WatcherWrapper : IDisposable
{
private readonly FileSystemWatcher _fileWatcher;
private readonly Subject<FileSystemEventArgs> _infoSubject;
private Subject<FileSystemEventArgs> _eventSubject;
public WatcherWrapper(string path, string nameFilter = "*.*", NotifyFilters? notifyFilters = null)
{
_fileWatcher = new FileSystemWatcher(path, nameFilter);
if (notifyFilters != null)
{
_fileWatcher.NotifyFilter = notifyFilters.Value;
}
_infoSubject = new Subject<FileSystemEventArgs>();
_eventSubject = new Subject<FileSystemEventArgs>();
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Changed").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Created").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Deleted").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Renamed").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
// this takes care of double events and still works with changing the name of the same file after a while
_infoSubject.Buffer(TimeSpan.FromMilliseconds(20))
.Select(x => x.GroupBy(z => z.FullPath).Select(z => z.LastOrDefault()).Subscribe(
infos =>
{
if (infos != null)
foreach (var info in infos)
{
{
_eventSubject.OnNext(info);
}
}
});
_fileWatcher.EnableRaisingEvents = true;
}
public IObservable<FileSystemEventArgs> FileEvents => _eventSubject;
public void Dispose()
{
_fileWatcher?.Dispose();
_eventSubject.Dispose();
_infoSubject.Dispose();
}
}
Usage:
var watcher = new WatcherWrapper(_path, "*.info");
// all more complicated and scenario specific filtering of events can be done here
watcher.FileEvents.Where(x => x.ChangeType != WatcherChangeTypes.Deleted).Subscribe(x => //do stuff)
Try this, It's working fine
private static readonly FileSystemWatcher Watcher = new FileSystemWatcher();
static void Main(string[] args)
{
Console.WriteLine("Watching....");
Watcher.Path = #"D:\Temp\Watcher";
Watcher.Changed += OnChanged;
Watcher.EnableRaisingEvents = true;
Console.ReadKey();
}
static void OnChanged(object sender, FileSystemEventArgs e)
{
try
{
Watcher.Changed -= OnChanged;
Watcher.EnableRaisingEvents = false;
Console.WriteLine($"File Changed. Name: {e.Name}");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
finally
{
Watcher.Changed += OnChanged;
Watcher.EnableRaisingEvents = true;
}
}
I wanted to react only on the last event, just in case, also on a linux file change it seemed that the file was empty on the first call and then filled again on the next and did not mind loosing some time just in case the OS decided to do some file/attribute change.
I am using .NET async here to help me do the threading.
private static int _fileSystemWatcherCounts;
private async void OnChanged(object sender, FileSystemEventArgs e)
{
// Filter several calls in short period of time
Interlocked.Increment(ref _fileSystemWatcherCounts);
await Task.Delay(100);
if (Interlocked.Decrement(ref _fileSystemWatcherCounts) == 0)
DoYourWork();
}
I think the best solution to solve the issue is to use reactive extensions
When you transform event into observable, then you can just add Throttling(..) (originally called Debounce(..))
Sample code here
var templatesWatcher = new FileSystemWatcher(settingsSnapshot.Value.TemplatesDirectory)
{
NotifyFilter = NotifyFilters.LastWrite,
IncludeSubdirectories = true
};
templatesWatcher.EnableRaisingEvents = true;
Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
addHandler => templatesWatcher.Changed += addHandler,
removeHandler => templatesWatcher.Changed -= removeHandler)
.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(args =>
{
_logger.LogInformation($"Template file {args.EventArgs.Name} has changed");
//TODO do something
});
You could try to open it for write, and if successful then you could assume the other application is done with the file.
private void OnChanged(object source, FileSystemEventArgs e)
{
try
{
using (var fs = File.OpenWrite(e.FullPath))
{
}
//do your stuff
}
catch (Exception)
{
//no write access, other app not done
}
}
Just opening it for write appears not to raise the changed event. So it should be safe.
FileReadTime = DateTime.Now;
private void File_Changed(object sender, FileSystemEventArgs e)
{
var lastWriteTime = File.GetLastWriteTime(e.FullPath);
if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
{
// code
FileReadTime = DateTime.Now;
}
}
Sorry for the grave dig, but I've been battling this issue for a while now and finally came up with a way to handle these multiple fired events. I would like to thank everyone in this thread as I have used it in many references when battling this issue.
Here is my complete code. It uses a dictionary to track the date and time of the last write of the file. It compares that value, and if it is the same, it suppresses the events. It then sets the value after starting the new thread.
using System.Threading; // used for backgroundworker
using System.Diagnostics; // used for file information
private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events
private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
{
try
{
//check if we already have this value in our dictionary.
if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
{
//compare timestamps
if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
{
//lock the table
lock ( fileModifiedTable )
{
//make sure our file is still valid
if ( File.Exists( e.FullPath ) )
{
// create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
BackgroundWorker newThreadWork = new BackgroundWorker();
newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );
// capture the path
string eventFilePath = e.FullPath;
List<object> arguments = new List<object>();
// add arguments to pass to the background worker
arguments.Add( eventFilePath );
arguments.Add( newEvent.File_Modified );
// start the new thread with the arguments
newThreadWork.RunWorkerAsync( arguments );
fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
}
}
}
}
}
catch ( IOException IOExcept )
{
//catch any errors
postError( IOExcept, "fswFileWatch_Changed" );
}
}
Event if not asked, it is a shame there are no ready solution samples for F#.
To fix this here is my recipe, just because I can and F# is a wonderful .NET language.
Duplicated events are filtered out using FSharp.Control.Reactive package, which is just a F# wrapper for reactive extensions. All that can be targeted to full framework or netstandard2.0:
let createWatcher path filter () =
new FileSystemWatcher(
Path = path,
Filter = filter,
EnableRaisingEvents = true,
SynchronizingObject = null // not needed for console applications
)
let createSources (fsWatcher: FileSystemWatcher) =
// use here needed events only.
// convert `Error` and `Renamed` events to be merded
[| fsWatcher.Changed :> IObservable<_>
fsWatcher.Deleted :> IObservable<_>
fsWatcher.Created :> IObservable<_>
//fsWatcher.Renamed |> Observable.map renamedToNeeded
//fsWatcher.Error |> Observable.map errorToNeeded
|] |> Observable.mergeArray
let handle (e: FileSystemEventArgs) =
printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath
let watch path filter throttleTime =
// disposes watcher if observer subscription is disposed
Observable.using (createWatcher path filter) createSources
// filter out multiple equal events
|> Observable.distinctUntilChanged
// filter out multiple Changed
|> Observable.throttle throttleTime
|> Observable.subscribe handle
[<EntryPoint>]
let main _args =
let path = #"C:\Temp\WatchDir"
let filter = "*.zip"
let throttleTime = TimeSpan.FromSeconds 10.
use _subscription = watch path filter throttleTime
System.Console.ReadKey() |> ignore
0 // return an integer exit code
In my case need to get the last line of a text file that is inserted by other application, as soon as insertion is done. Here is my solution. When the first event is raised, i disable the watcher from raising others, then i call the timer TimeElapsedEvent because when my handle function OnChanged is called i need the size of the text file, but the size at that time is not the actual size, it is the size of the file imediatelly before the insertion. So i wait for a while to proceed with the right file size.
private FileSystemWatcher watcher = new FileSystemWatcher();
...
watcher.Path = "E:\\data";
watcher.NotifyFilter = NotifyFilters.LastWrite ;
watcher.Filter = "data.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
...
private void OnChanged(object source, FileSystemEventArgs e)
{
System.Timers.Timer t = new System.Timers.Timer();
try
{
watcher.Changed -= new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = false;
t.Interval = 500;
t.Elapsed += (sender, args) => t_Elapsed(sender, e);
t.Start();
}
catch(Exception ex) {
;
}
}
private void t_Elapsed(object sender, FileSystemEventArgs e)
{
((System.Timers.Timer)sender).Stop();
//.. Do you stuff HERE ..
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
Here is another approach. Instead of propagating the first event of a quick succession of events and suppressing all that follow, now all are suppressed except from the last one. I think that the scenarios that can benefit from this approach are more common.
To make this happen we must use a sliding delay. Every incoming event cancels the timer that would fire the previous event, and restarts the timer. This opens the possibility that a never-ending series of events will delay the propagation forever. To keep things simple, there is no provision for this abnormal case in the extension methods below.
public static class FileSystemWatcherExtensions
{
public static IDisposable OnAnyEvent(this FileSystemWatcher source,
WatcherChangeTypes changeTypes, FileSystemEventHandler handler, int delay)
{
var cancellations = new Dictionary<string, CancellationTokenSource>(
StringComparer.OrdinalIgnoreCase);
var locker = new object();
if (changeTypes.HasFlag(WatcherChangeTypes.Created))
source.Created += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Deleted))
source.Deleted += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Changed))
source.Changed += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Renamed))
source.Renamed += FileSystemWatcher_Event;
return new Disposable(() =>
{
source.Created -= FileSystemWatcher_Event;
source.Deleted -= FileSystemWatcher_Event;
source.Changed -= FileSystemWatcher_Event;
source.Renamed -= FileSystemWatcher_Event;
});
async void FileSystemWatcher_Event(object sender, FileSystemEventArgs e)
{
var key = e.FullPath;
var cts = new CancellationTokenSource();
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing))
{
existing.Cancel();
}
cancellations[key] = cts;
}
try
{
await Task.Delay(delay, cts.Token);
// Omitting ConfigureAwait(false) is intentional here.
// Continuing in the captured context is desirable.
}
catch (TaskCanceledException)
{
return;
}
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing)
&& existing == cts)
{
cancellations.Remove(key);
}
}
cts.Dispose();
handler(sender, e);
}
}
public static IDisposable OnAllEvents(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.All, handler, delay);
public static IDisposable OnCreated(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Created, handler, delay);
public static IDisposable OnDeleted(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Deleted, handler, delay);
public static IDisposable OnChanged(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Changed, handler, delay);
public static IDisposable OnRenamed(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Renamed, handler, delay);
private struct Disposable : IDisposable
{
private readonly Action _action;
internal Disposable(Action action) => _action = action;
public void Dispose() => _action?.Invoke();
}
}
Usage example:
myWatcher.OnAnyEvent(WatcherChangeTypes.Created | WatcherChangeTypes.Changed,
MyFileSystemWatcher_Event, 100);
This line combines the subscription to two events, the Created and the Changed. So it is roughly equivalent to these:
myWatcher.Created += MyFileSystemWatcher_Event;
myWatcher.Changed += MyFileSystemWatcher_Event;
The difference is that the two events are regarded as a single type of event, and in case of a quick succession of these events only the last one will be propagated. For example if a Created event is followed by two Changed events, and there is no time gap larger than 100 msec between these three events, only the second Changed event will be propagated by invoking the MyFileSystemWatcher_Event handler, and the previous ones will be discarded.
I have changed the way I monitor files in directories. Instead of using the FileSystemWatcher I poll locations on another thread and then look at the LastWriteTime of the file.
DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);
Using this information and keeping an index of a file path and it's latest write time I can determine files that have changed or that have been created in a particular location. This removes me from the oddities of the FileSystemWatcher. The main downside is that you need a data structure to store the LastWriteTime and the reference to the file, but it is reliable and easy to implement.
I was able to do this by added a function that checks for duplicates in an buffer array.
Then perform the action after the array has not been modified for X time using a timer:
- Reset timer every time something is written to the buffer
- Perform action on tick
This also catches another duplication type. If you modify a file inside a folder, the folder also throws a Change event.
Function is_duplicate(str1 As String) As Boolean
If lb_actions_list.Items.Count = 0 Then
Return False
Else
Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim
If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
Return False
Else
Return True
End If
End If
End Function
Public Module extentions
<Extension()>
Public Function parentDir(ByVal aString As String) As String
Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
End Function
End Module
This solution worked for me on production application:
Environment:
VB.Net Framework 4.5.2
Set manually object properties: NotifyFilter = Size
Then use this code:
Public Class main
Dim CalledOnce = False
Private Sub FileSystemWatcher1_Changed(sender As Object, e As IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
If (CalledOnce = False) Then
CalledOnce = True
If (e.ChangeType = 4) Then
' Do task...
CalledOnce = False
End If
End Sub
End Sub
Try this!
string temp="";
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
if(temp=="")
{
//do thing you want.
temp = e.name //name of text file.
}else if(temp !="" && temp != e.name)
{
//do thing you want.
temp = e.name //name of text file.
}else
{
//second fire ignored.
}
}
I had to combine several ideas from the posts above and add file locking check to get it working for me:
FileSystemWatcher fileSystemWatcher;
private void DirectoryWatcher_Start()
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher
{
Path = #"c:\mypath",
NotifyFilter = NotifyFilters.LastWrite,
Filter = "*.*",
EnableRaisingEvents = true
};
fileSystemWatcher.Changed += new FileSystemEventHandler(DirectoryWatcher_OnChanged);
}
private static void WaitUntilFileIsUnlocked(String fullPath, Action<String> callback, FileAccess fileAccess = FileAccess.Read, Int32 timeoutMS = 10000)
{
Int32 waitMS = 250;
Int32 currentMS = 0;
FileInfo file = new FileInfo(fullPath);
FileStream stream = null;
do
{
try
{
stream = file.Open(FileMode.Open, fileAccess, FileShare.None);
stream.Close();
callback(fullPath);
return;
}
catch (IOException)
{
}
finally
{
if (stream != null)
stream.Dispose();
}
Thread.Sleep(waitMS);
currentMS += waitMS;
} while (currentMS < timeoutMS);
}
private static Dictionary<String, DateTime> DirectoryWatcher_fileLastWriteTimeCache = new Dictionary<String, DateTime>();
private void DirectoryWatcher_OnChanged(Object source, FileSystemEventArgs ev)
{
try
{
lock (DirectoryWatcher_fileLastWriteTimeCache)
{
DateTime lastWriteTime = File.GetLastWriteTime(ev.FullPath);
if (DirectoryWatcher_fileLastWriteTimeCache.ContainsKey(ev.FullPath))
{
if (DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath].AddMilliseconds(500) >= lastWriteTime)
return; // file was already handled
}
DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath] = lastWriteTime;
}
Task.Run(() => WaitUntilFileIsUnlocked(ev.FullPath, fullPath =>
{
// do the job with fullPath...
}));
}
catch (Exception e)
{
// handle exception
}
}