|
Hi all,
I have several methods, with many different signatures, that I would like to allow the user to cancel in the middle of their operation. This wouldn't be a backgroundworker situation, however---these are methods in my library that are called synchronously from a certain thread, and need to operate synchronously (i.e., the calling thread should be waiting on them to finish), then canceled from the UI thread. Also, since these methods by default operate just by a method call, and not any other invocation, I don't want to make them all conform to the backgroundworker's dowork event handler signature.
So, is there some kind of built-in way to do this, perhaps with an attribute? How would the method check that it's time to cancel? How can I specify, preferably at compile time of course, that only methods x, y, and z can be canceled?
Otherwise, my solution is as follows:
- If you want to call the method normally (without canceling capabilities), just call it.
- If you want to call a method in a cancelable fashion, call
object Controller.PerformOngoingOperation(method, string id, params object[] args) ; another thread can later call Controller.CancelOngoingOperation(string id) .PerformOngoingOperation checks that the method passed (more on that later) has the appropriate OngoingOperationAttribute ; if so, it invokes it. It also stores in an internal dictionary a map from ids to boolean "should cancel now" variables.- The method in question finds its ID. This is kludgy... it involves reflections to find the calling method, check if the calling method is
PerformOngoingOperation , and if so, ready the id parameter. - It then uses this id to performs a series of checks throughout its operation against that
internal map from ids to "should cancel now"; if at any point the should-cancel bit is true, it finishes what it's doing and returns. - Then,
PerformOngoingOperation will return the results of the method, whether canceled prematurely or not. So it acts just like invoking the method (good for users!), but can allow cancellation. - The should-cancel bit is set by
Control.CancelOngoingOperation(string id) , with consequences as above.
IF the above approach is reasonable---and it's very nice from a user's perspective, except for the upcoming detail---then I have a question. How should I pass the method to PerformOngoingOperation ? Is there any way to do so without awkwardness? What I mean is that, as far as I can tell, I want to use a delegate. This necessitates that the user of my library declare a new delegate type for every method he wants to call in a cancelable fashion! And he can't even declare delegate types locally; he has to pollute his class with them. Like this:
class C
{
delegate int annoying(int param1, bool param2, string param3);
void Method()
{
string id;
int retVal = (int)Control.PerformOngoingOperation(new annoying(Library.Object.Method), out id, param1, param2, param3);
}
}
I suppose an alternative is requiring that the delegate be of type MethodInvoker (basically void return type, no parameters), and forcing the user to do this:
int retVal;
string id = Control.PerformOngoingOperation((MethodInvoker)(new delegate() { retVal = Library.Object.Method(param1, param2, param3); }));
But that's awkward at best.
Ideally I would like to have something like this instead:
string id;
int retVal = Control.PerformOngoingOperation(Library.Object.Method, out id, param1, param2, param3);
Is that possible?
-Domenic Denicola- [CPUA 0x1337]
“I was born human. But this was an accident of fate—a condition merely of time and place. I believe it's something we have the power to change…”
|
|
|
|
|
|
I think the basic problem you are facing is that you are trying to introduce state (the cancel variable) into the methods. Would it be possible to put each method in a class? Something like
abstract class Operation
{
bool cancelIssued;
void Cancel() { cancelIssued = true; }
void Execute();
}
class A : Operation
{
int a; string b;
public A(int a, string b)
{
this.a = a;
this.b = b;
}
public override void Execute() { MethodA(a, b); }
void MethodA(int a, string b)
{
if (this.cancelIssued)
return;
}
}
This allows the user to do something like
A a = new A(1, "Foo")
Control.PerformOperation(a);
Control.PerformOperation(someOtherClassInstance);
a.Cancel()
from another thread.
|
|
|
|
|
That's actually a really good idea. However, I'd want to avoid having to create a new class for each ongoing operation, and I'd prefer to be able to leave the methods in their original class for the normal callers... hmm. There must be some way to hybridize this... if only methods were also objects, heh.
-Domenic Denicola- [CPUA 0x1337]
“I was born human. But this was an accident of fate—a condition merely of time and place. I believe it's something we have the power to change…”
|
|
|
|
|
Hi,
I was wondering if anyone could push me in the right direction on this one.
I got a file with multiple files inside it like an archive, its not compressed it just seems like the files are stacked inside the file.
I can read the headers of head indevidual file with a hex reader, so I have indetified that the file contain several wav files, some dds files and jpeg, avi etc.
what I have visioned is a loop that goes trough the file and each time it identifies a header it dumps that header and the following data to a file and continues to read on sortof splitting the file up again.
Im unsure if I should have a list of just the data headers im expecting to find or if I should make a struct with the structure setup of all the file spesification's im expecting and have the loop match against that.
Also what would be the most efficient way to go trough the file, its pretty large (about 4-500mb)?
structure example:
typedef struct _avimainheader {<br />
FourCC fcc;<br />
DWORD cb;<br />
DWORD dwMicroSecPerFrame;<br />
DWORD dwMaxBytesPerSec;<br />
DWORD dwPaddingGranularity;<br />
DWORD dwFlags;<br />
DWORD dwTotalFrames;<br />
DWORD dwInitialFrames;<br />
DWORD dwStreams;<br />
DWORD dwSuggestedBufferSize;<br />
DWORD dwWidth;<br />
DWORD dwHeight;<br />
DWORD dwReserved[4];<br />
} AVIMAINHEADER;
|
|
|
|
|
Do you have any other files that accompany this file? I'm asking because a data file like you are describing would commonly have an index, either embedded in the data file or in a separate file. If you had the index you could position to the offsets and read. If you don't have an index you'll probably have to read sequentially and detect headers as you go. As far as efficiency is concerned... don't optimize unless the method you're using is too slow. In that case you want to find the bottlenecks and open them up.
|
|
|
|
|
As far as I can tell its just that file, there are some compiled pyton files there that I have'nt had any luck prying information from, it might be that they hold a sort of a catalog but im unsure.
I have been playing around trying to extract the raw data, but I have some problems getting the loop to determine where the data ends and where a new header begins.
|
|
|
|
|
I am building a Windows Service and keep getting a System.IO.Exception {"The process cannot access the file 'J:\\Projects\\RTalarms_20071008.txt' because it is being used by another process."}.
Nothing has the file open. It is just sitting in the directory. The Bolded area is where the error occurs.
Any Idea why?
This the code;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.Data.SqlClient;
namespace MCPAlarmMonitor
{
public partial class MCPAlarmMonitorService : ServiceBase
{
public MCPAlarmMonitorService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
fileSystemWatcher1.EnableRaisingEvents = true;
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
fileSystemWatcher1.EnableRaisingEvents = false;
}
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
using (StreamReader reader = new StreamReader(e.FullPath)) {
string text = reader.ReadLine();
while (text != null)
{
text = text.TrimEnd(' ');
string[] tokens = text.Split(',');
InsertRecord(tokens);
text = reader.ReadLine();
}
}
}
private void InsertRecord(string[] tokens)
{
using (SqlConnection connection = new SqlConnection("server=localhost;database=MCPAlarms;integrated security=true"))
{
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "insert into AlarmsList values(@DateTime,@Address,@Location,@State,@notused1,@notused2,@AlarmMessage)";
for (int i = 0; i < 8; i++)
{
string parameterName = string.Format("@Column{0}", i + 1);
string parameterValue = tokens.Length > i ? tokens[i] : string.Empty;
command.Parameters.AddWithValue(parameterName, parameterValue);
}
connection.Open();
command.ExecuteNonQuery();
}
}
}
}
|
|
|
|
|
Your code is reacting to the Created event immediately after the file is created, but probably before the file is closed by the process that created it, so yes, the file is being held open with DenyShareAll while it's being written by the other process. By the time you realize it and look at the file, it's done being written and has been closed.
That's one of the pitfalls of the FSW. Your code has to be written to retry file operations if your going to start modifying the file while it's still being written. If you can't open the file, you'll have to wait a second and retry the operation. Keep retying until you either reach some retry limit your've put in, or the file successfully opens.
|
|
|
|
|
So if I insert put a thread sleep entry before this line
Thread.Sleep(500);
using (StreamReader reader = new StreamReader(e.FullPath))
would that solve it?
|
|
|
|
|
Since you have no idea how long the other application will have the file open, maybe and maybe not. You will STILL have to repeatedly try to open the file until your either exhaust your retry count or it opens.
|
|
|
|
|
With a little experimentation I just kept bumping up the time until I no longer got that error.
Now I am getting {"Must declare the scalar variable \"@DateTime\"."}.
I am not as savvy with SQL Commands. I am missing something simple I feel.
|
|
|
|
|
That solution will only work so long as the other process cooperates and performs EXACTLY as expected. Once the system gets booged down or the other process changes, your solution breaks instantly. That's why I said you MUST write your code to retry opening the file, NOT wait a predetermined amount of time. Your code is not written defensively and is actually hoping the future NEVER changes. Good luck with that.
Your SQL statement is written incorrectly. It's much better practice to specify the column names in your table and then the parameter names assigned to them. If your SQL table changes, it won't immediately break your code. You also have incorrectly created the parmater objects. Change the SQL to: (I'm making assumptions about your column names and types!!)
INSERT INTO AlarmsList (DateTime, Address, Location, State, AlarmMessage)
VALUES (@DateTime,@Address,@Location,@State,@AlarmMessage)
The names with the @ in front of them are your parameter place holders. You have to create these exact parameters in your code and give them the appropriate values. BTW, why are you storing your DateTimes as a string in the database?? Bad practice!
command.Parameters.AddWithValue("@DateTime, tokens[0]);
command.Parameters.AddWithValue("@Address", tokens[1]);
command.Parameters.AddWithValue("@Address", tokens[2]);
command.Parameters.AddWithValue("@Address", tokens[3]);
// 4 and 5 get skipped because you're not using them, so why take up the bandwidth passing them?
command.Parameters.AddWithValue("@Address", tokens[6]);
Putting in that line with the ?: in it was useless. Go back to the tokenizing code and validate you can fill in all the fields back there.
|
|
|
|
|
The DateTime Column is an odd format from the log file.
I think I can figure it out from here. Thanks for you suggestions.
Brian
|
|
|
|
|
Hi, I am trying to write a console program that among other things will use the sqlplus utility to run PL/SQL script in a file.
To this end I have the following bit of code:
<pre>
static void execute(string command, string args, int timeout, string process_title)
{
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(command, args);
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
System.Diagnostics.Process ps;
try
{
ps = System.Diagnostics.Process.Start(psi);
if(!ps.WaitForExit(timeout))
{
try
{
ps.Kill();
}
catch(Exception e)
{
log("Error while trying to terminate process");
log("Error: "+e.Message);
}
throw new Exception(process_title+" operation has timed out.");
}
}
catch(Exception e)
{
log("Error Running "+process_title);
throw e;
}
}
</pre>
However when I run
execute("sqlplus", @"mylogin/mypassword@sid @filepath", 50000, "my process");
I get a timeout, even if the file only contains a simple and small select clause.
Any ideas?
I suspect this is not sqlplus related since running
execute("dir", @"C:\", 1000, "dir");
results in a "The system cannot find the file specified" error
|
|
|
|
|
Can you not use the oracle drivers in .NET and execute the script through the common runtime?
The reason you get the time out is because the application is likely waiting for input. I believe there is a WaitForInputIdle method which might suit you better.
Need a C# Consultant? I'm available.
Happiness in intelligent people is the rarest thing I know. -- Ernest Hemingway
|
|
|
|
|
"dir" cannot be executed because there is no "dir.exe", it is just a command supported by cmd.exe.
Can you try to disable RedirectStandardOutput? If you don't get a timeout then, your application is not handling the output fast enough. (the "sqlplus" process will pause when the output buffer is full)
|
|
|
|
|
Yes, disabling redirect standard output works fine.
Is there a way to deal with this short of editing the script file itself to turn spooling on?
I would like the equivalent of 'sqlplus user/pass@sid @scriptfilename > logfilename', but not surprisingly, appending '> logfilename' to the arguments string doesn't work.
|
|
|
|
|
Found the answer here:
http://weblogs.asp.net/israelio/archive/2004/08/31/223447.aspx how to pipe to a process
just launch cmd.exe and pass everything to it as a parameter.
Works great for me, anyone have any idea if this might be inferior for any reason? Why should I not just write a wrapper to launch all my prcoesses this way?
|
|
|
|
|
How do i set a menu button to change the webpage to another.
Not a ComboBox, just a regular Menu Button to change my web browsers page.
Also I've seen programs with a Check box button that if clicked the window will always be on top. What code would i need to put into that?
|
|
|
|
|
The answer to your second question:
Add a checkbox to your form, and set it's CheckStateChanged event handler.
Write the following code within the event handler:
if (checkBox1.Checked == true)
{
this.TopMost = true;
}
else
{
this.TopMost = false;
}
And that's it.
Virtual1ty
"Any fool can learn from his own mistakes, but a wise man learns from mistakes of others"
|
|
|
|
|
Thanks Virtual1ty
|
|
|
|
|
You're welcome
Virtual1ty
"Any fool can learn from his own mistakes, but a wise man learns from mistakes of others"
|
|
|
|
|
What about
this.TopMost = checkBox1.Checked
?
modified 12-Sep-18 21:01pm.
|
|
|
|
|
Oh yeah, I didn't think about that
Virtual1ty "Any fool can learn from his own mistakes, but a wise man learns from mistakes of others"
|
|
|
|
|