here we using if statement and it's work fine for first time
then when i enter barcode and pressed enter again it's going direct to else statement
it's mean shown me error message under as we see on else statement and i think it's not repeat whole statement as new process because i'm sure from condition it's true and it shouldn't go to else statement
but in third time when i try enter barcode and check from condition it's work fine like first time and then in forth time show me error again
why error shown on every second time
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (checkBox1.Checked)
{
if (qtystate == true)
{
DataTable dt;
barcode = textBox1.Text;
dt = pro.GetProdcutBar(textBox1.Text);
if (dt.Rows.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
barcode = row[0].ToString();
itemno = row[1].ToString();
itemname = row[2].ToString();
salespric = row[3].ToString();
costprice = row[4].ToString();
}
cont.addprodcuttocount(Convert.ToString(barcode), Convert.ToString(itemno), Convert.ToString(itemname),
Convert.ToString(salespric), Convert.ToString(costprice), "1", DateTime.Now, SystemInformation.ComputerName);
refershgridview();
//sum qty
try
{
int sum = 0;
for (int i = 0; i < dataGridView1.Rows.Count; ++i)
{
if (Convert.ToString(dataGridView1.Rows[i].Cells[1].Value) == textBox1.Text)
{
sum += Convert.ToInt32(dataGridView1.Rows[i].Cells[6].Value);
}
}
label3.Text = "Barcode " + barcode + " - " + "Item NO" + itemno + " - " + "item name " + itemname + " - " + "qty " + sum.ToString();
}
catch
{
}
barcode = "";
itemno = "";
itemname = "";
salespric = "";
costprice = "";
qtyitem = "";
textBox1.Clear();
textBox1.Focus();
}
else
{
MessageBox.Show("00barcode not found", "barcode", MessageBoxButtons.OK, MessageBoxIcon.Stop);
barcode = "";
itemno = "";
itemname = "";
salespric = "";
costprice = "";
qtyitem = "";
textBox1.Clear();
textBox1.Focus();
}
}
}
}
}
The discriminator for your if...else block is this line:
if (dt.Rows.Count > 0)
Every other call to the function, the body of the if clause is executed. For the remaining calls, the else clause is executed.
Based on these symptoms, it appears that something is changing the result of this line:
dt = pro.GetProdcutBar(textBox1.Text)
In half the calls, you get at least one row; in the others, you get zero rows. The question for you is, Why are the results changing every other call?
A couple of causes spring to mind:
Your GetProdcutBar method doesn't simply retrieve data, it modifies it (a side-effect). This is undesirable, as it hides the fact that your result set changes with each call.
Another method somewhere in the program is changing the data. This is possibly in response to user activity (a mouse click, for example).
The underlying code that retrieves the data is written in such a way that it returns a set of data that cannot be reliably predicted (that is, it is non-deterministic). Perhaps one of the selection criteria uses the time of day, or clock ticks, thread ID, or some other volatile value.
If any of these are the case, your data may change from one call to the next, potentially returning no results. Examine the function and make sure it reliably returns the data you expect every time you call it, and that you are not using volatile function arguments (unless you absolutely must).
Also, as a parting thought: Is it truly an error condition if no records are returned by a query? In the vast majority of cases, it is not (unless the query is for required lookup data). The key thing to do is process all available rows, and do nothing if there is no data to process.
Related
I will be amazed if I find a solution for this, since it is very specific and vague, but I figured I would try. I'll try to give as much information as humanly possible, since I've been searching for answers for some time now.
I am building a utility in C# which copies records from a file in a library on the i-series/AS400 and builds an encrypted text file with each record from the AS400 as a comma separated string. In the file, it will have values like filename, fieldvalue1, fieldvalue2, fieldvalue3. I then take that text file to another PC, and run a C# utility which copies that record into the same file name in a library over there on a different i-series machine. Unfortunately, I receive the outside bounds of the array exception in some cases, but I cannot determine why. In the record just prior to the exception, the record looks pretty much the same and it works fine. My code is below in a nutshell. I usually don't give up, but I don't expect to ever figure this out. If someone does, I'll probably sing karaoke tonight.
// Select records from AS400 file and write them to text file
Recordset rs = new Recordset();
sqlQuery = "SELECT * FROM " + dataLibrary + "." + fileName;
try
{
rs.Open(sqlQuery, con);
while (!rs.EOF)
{
int[] fieldLengths;
fieldLengths = new int[rs.Fields.Count];
String[] fieldValues;
fieldValues = new String[rs.Fields.Count];
String fullString = "";
for (i = 0; i < rs.Fields.Count; i++)
{
fieldLengths[i] += rs.Fields[i].DefinedSize;
fieldValues[i] += rs.Fields[i].Value;
}
fullString = fileName + "," + String.Join(",", fieldValues);
fullString = Functions.EncryptString(fullString);
File.AppendAllText(savefile.FileName, fullString + Environment.NewLine);
rs.MoveNext();
}
}
catch (Exception ex)
{
}
cmd.Dispose();
// This gives me a text file of filename, fieldvalue1, fieldvalue2, etc...
// Next, I take the file to another system and run this process:
while ((myString = inputFile.ReadLine()) != null)
{
int stringLength = myString.Length;
String[] valuesArray = myString.Split(',');
for (i = 0; i < valuesArray.Length; i++)
{
if (i == 0)
{
fileName = valuesArray[0];
// Create file if it doesn't exist already
createPhysicalFile(newLibrary, fileName);
SQLStatement = "INSERT INTO " + newLibrary + "." + fileName + "VALUES(";
}
else
{
if (i == valuesArray.Length - 1)
{
SQLStatement += "#VAL" + i + ")";
}
else
{
SQLStatement += "#VAL" + i + ", ";
}
}
}
try
{
using (connection)
{
try
{
connection.Open();
}
catch (Exception ex)
{
}
// Create a new SQL command
iDB2Command command = new iDB2Command(SQLStatement, connection);
for (i = 1; i < valuesArray.Length; i++)
{
try
{
command.Parameters.AddWithValue("#VAL" + i, (valuesArray[i]));
}
catch (Exception ex)
{
}
}
// Just split the array into a string to visually check
// differences in the records
String arraySplit = ConvertStringArrayToString(valuesArray);
// The query gets executed here. The command looks something
// like:
// INSERT INTO LIBNAME.FILENAME VALUES(#VAL!, #VAL2, #VAL3, #VAL4)
// There are actually 320 fields in the file I'm having a problem with,
// so it's possible I'm overlooking something. I have narrowed it down to
// field # 316 when the exception occurs, but in both cases
// field 316 is blanks (when it works and when it doesn't).
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
// Here I get the exception out of bounds error in MSCORLIB.DLL.
// Some records are added fine, while others cause this exception.
// I cannot visibly tell any major differences, nor do I see any
// errors in the AS400 job log or anything in C# that would lead me
// down a certain path.
String error = ex.Message;
}
}
For what it's worth, I found this happening one a smaller file in the system and was able to figure out what going on, after painstaking research into the code and the net. Basically, the file file has numeric fields on the i-series. Somehow, the records were written to the file on the original system with null values in the numeric fields instead of numeric values. When storing the original records, I had to do this calculation:
String fieldType = rs.Fields[i].Type.ToString();
object objValue = rs.Fields[i].Value;
if (fieldType == "adNumeric" && objValue is DBNull)
{
fieldValues[i] += "0";
}
else
{
fieldValues[i] += rs.Fields[i].Value;
}
After this, if null values were found in one of the numeric fields, it just put "0" in it's place so that when writing to the new machine, it would put a valid numeric character in there and continue on writing the rest of the values. Thanks for all the advice and moral support. :)
This application is a message board type of app. You post a message, which is stored in an Oracle database. The user's photo, name, date and message are displayed in a listview. Everything works however a feature request for font color changing came in. Now testing things below I know I can display ALL of the messages as a certain color.
What I'm going to do is add a 'color' column to my table. Then if a user selects the text of their message to be 'red' for example, I am going to store the hex color for red in the color column of the table for that post.
So what I'm trying to figure out is when that message is retrieved, how I can set this code to 'If color column empty, post message as black, else if hex color exist for each message post that particular message in that color' Without changing the color of every message to red as the code below currently does.
List<MsgBoard> Messages = MsgBoard.find_ActiveByBoardName(Convert.ToString(cmbgroup.SelectedItem));
int i = 0;
imageList1.Images.Clear();
foreach (MsgBoard m in Messages)
{
AddImages(m.EmpPic);
ListViewItem Message = new ListViewItem("", i);
if (m.AlertNo == 0) //Default Message Post (non Alert)
{
Message.UseItemStyleForSubItems = false;
Message.SubItems.Add(m.EmpName.First);
Message.SubItems.Add(m.MessageText + Environment.NewLine).ForeColor = Color.Red;
Message.SubItems.Add(m.PostDate.ToString());
Message.SubItems.Add(m.EmpName.EmpNo.ToString());
Message.SubItems.Add(m.EmpName.Name);
listView1.Items.Add(Message);
}
i++;
}
}
Just to clarify. If 20 people post a message and all 20 select a different color to post their message in. So in the DB all 20 rows have a different color in the DB column, I need to have those messages displayed in listview individually based on the color they posted as. I currently am only aware of applying a single color to all messages in listview as shown in my example above.
Looks like thanks to Stephan, once I add
Message.SubItems.Add(m.MessageText + Environment.NewLine).ForeColor = System.Drawing.ColorTranslator.FromHtml(m.Color);
I then need help adjusting my query to add the color once colors are added to the color column. Here is that code.
Had DB admin add MESSAGE_COLOR varchar column.
I edit this below to existing code, for the newly created column MESSAGE_COLOR when posting..
public string Create()
{
try
{
OleDbDataReader result = Database.Conn.Execute(
"INSERT INTO MPCS.MEYER_BOARD (" +
"EMPLOYEE_ID, " +
"POST_DATE, " +
"BOARD_NAME," +
"ALERT_NO," +
"MESSAGE_TEXT," +
"MESSAGE_COLOR," +
"ACTIVE_FLAG" +
") VALUES (?,SYSDATE,?,?,?,?)",
new List<OleDbParameter> {
new OleDbParameter("EMPLOYEE_ID",EmpName.EmpNo),
new OleDbParameter("BOARD_NAME",BoardName),
new OleDbParameter("ALERT_NO",AlertNo),
new OleDbParameter("MESSAGE_TEXT",MessageText),
new OleDbParameter("MESSAGE_COLOR",MessageColor),
new OleDbParameter("ACTIVE_FLAG",ActiveFlag ? "Y" : "N")
}, Query.ReaderType.Reader);
result.Read();
result.Close();
return null;
}
catch (Exception ex)
{
return ex.Message;
}
}
I then editing DB read w/ the new column as well.
public static MsgBoard DBRead(OleDbDataReader result,string alias=null)
{
return new MsgBoard
{
EmpName = Employee.DBRead(result, "EMPLOYEE"),
MessageText = result[alias + "MESSAGE_TEXT"].ToString(),
MessageColor = result[alias + "MESSAGE_COLOR"].ToString(),
BoardName = result[alias +"BOARD_NAME"].ToString(),
AlertNo = (int)(decimal)result[alias +"ALERT_NO"],
PostDate = (DateTime)result[alias +"POST_DATE"],
ActiveFlag = result[alias +"ACTIVE_FLAG"].ToString().ToString() == "Y",
EmpPic = ImageResource.DBRead(result, "IR")
};
}
But do I need to edit my build_query?
public static String build_query(String where, string OrderBy = null)
{
List<String> cols = new List<String>();
cols.AddRange(db_columns.ConvertAll(c => "MPCS.MEYER_BOARD." + c + " AS MBOARD_" + c));
cols.AddRange(Employee.db_columns.ConvertAll(c => "MPCS.EMPLOYEE." + c + " AS EMPLOYEE_" + c));
cols.AddRange(ImageResource.db_columns.ConvertAll(c => "MPCS.IMAGE_RESOURCE." + c + " AS IR_" + c));
String sql =
"SELECT " + String.Join(", ", cols) + " " +
"FROM MPCS.MEYER_BOARD " +
"LEFT OUTER JOIN MPCS.EMPLOYEE " +
"ON MPCS.MEYER_BOARD.EMPLOYEE_ID=MPCS.EMPLOYEE.EMPLOYEE_ID " +
"LEFT OUTER JOIN MPCS.IMAGE_RESOURCE " +
"ON MPCS.IMAGE_RESOURCE.IR_ID=MPCS.EMPLOYEE.IMAGE_RESOURCE_ID " +
"WHERE ";
sql += where;
sql += OrderBy;
return sql;
}
UPDATE
I've gotten everything coded and I am submitting the color to the DB now. However when displaying the listview below each post still just has a default black text color. Rather than the color saved for example as "#FFF000" being converted and displayed.
foreach (MsgBoard m in Messages)
{
AddImages(m.EmpPic);
ListViewItem Message = new ListViewItem("", i);
if (m.AlertNo == 0) //Default Message Post (non Alert)
{
Message.SubItems.Add(m.EmpName.First);
Message.SubItems.Add(m.MessageText + Environment.NewLine).ForeColor = System.Drawing.ColorTranslator.FromHtml(m.MessageColor);
Message.SubItems.Add(m.PostDate.ToString());
Message.SubItems.Add(m.EmpName.EmpNo.ToString());
Message.SubItems.Add(m.EmpName.Name); //Displayed only on 'Show Details'
listView1.Items.Add(Message);
}
Final update
Turns out the last piece I was missing was to change the listview property UseItemStyleForSubItems = false; now its working.
You can convert your hex value (assuming that it is stored as a string in the format "#RRGGBB") into a color using ColorTranslator
Using this approach, you can also use named colors (like "Violet")
Message.SubItems.Add(m.MessageText + Environment.NewLine).ForeColor =
System.Drawing.ColorTranslator.FromHtml(m.Color); //sth. like "#FFCC66"
Furthermore you have to retrieve the value of the Color column. It seems that you have to add "MESSAGE_COLOR" (or whatever your column is called) to your db_columns. When you got the values from the DB, you have to map the column to the property of your Message:
return new MsgBoard
{
// ...
Color = System.Drawing.ColorTranslator.FromHtml([alias+"MESSAGE_COLOR"]),
// ...
}
(In other words: You already save it to db, now you also have to read it from DB)
I have this small method that pops up a message box warning , problem is that it pops up 3 message box instead of one!
I've tried several ways to counter this issue (including the bool variables in the code and using Distinct in the sql query , though the database doesn't contain any repeatable rows).
The idea is to have the messagebox pop up once for each row that violates my if condition and not 3 times for each row.
So , why is this message box pops up 3 times instead of once? and how to fix it?
void msds_update()
{
SqlConnection con = new SqlConnection();
con.ConnectionString = "server=(local);database=PhilipsMaterials;Integrated Security=SSPI;";
con.Open();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
string sql = "Select * from [PhilipsMaterials].[dbo].[Materials]";
SqlDataAdapter da = new SqlDataAdapter(sql, con);
da.Fill(ds);
dt = ds.Tables[0];
DateTime longupdate;
DateTime shortupdate;
foreach (DataRow row in dt.Rows)
{
longupdate = Convert.ToDateTime(dt.Rows[0]["Long MSDS Update"]);
shortupdate = Convert.ToDateTime(dt.Rows[0]["Short MSDS Update"]);
TimeSpan longsince = DateTime.Now.Subtract(longupdate);
int longyears = (int)(longsince.Days / 365.25);
TimeSpan shortsince = DateTime.Now.Subtract(shortupdate);
int shortyears = (int)(shortsince.Days / 365.25);
bool flag = false ;
bool shown = false;
if (longyears > 4.5) { flag = true; }
if (flag && !shown)
{
string longmsdsname = Convert.ToString(dt.Rows[0]["Name"]);
string msg = "Long Msds " + longmsdsname + " must be updated";
MessageBox.Show(msg);
shown = true;
}
flag = false;
shown = false;
if (shortyears > 4.5) { flag = true; }
if (flag && !shown)
{
string shortmsdsname = Convert.ToString(dt.Rows[0]["Name"]);
string msg = "Short Msds " + shortmsdsname + " must be updated";
MessageBox.Show(msg);
shown = true;
}
}
con.Close();
}
The values used to perform the if tests and to build the error message always are the values from the row at index zero, you should use the current row indexer used in the foreach loop
Also, do not react immediatly to the error condition, instead build up an error message, wait to end one loop and show the message only if there is an error. No need to use and keep updated two status variables.
StringBuilder sb = new StringBuilder();
int rowCounter = 0;
foreach (DataRow row in dt.Rows)
{
rowCounter++;
longupdate = Convert.ToDateTime(row["Long MSDS Update"]);
shortupdate = Convert.ToDateTime(row["Short MSDS Update"]);
TimeSpan longsince = DateTime.Now.Subtract(longupdate);
int longyears = (int)(longsince.Days / 365.25);
TimeSpan shortsince = DateTime.Now.Subtract(shortupdate);
int shortyears = (int)(shortsince.Days / 365.25);
if (longyears <= 4.5)
{
string longmsdsname = Convert.ToString(row["Name"]);
sb.AppendFormat("Long Msds {0} must be updated\r\n", longmsdsname);
}
if (shortyears <= 4.5)
{
string shortmsdsname = Convert.ToString(row["Name"]);
sb.AppendFormat("Short Msds {0} must be updated\r\n", shortmsdsname);
}
// If we have errors, show them and reset the builder for the next loop
if(sb.Length > 0)
{
string msg = string.Format("Error in row {0}\r\n{1}",
rowCounter.ToString(), sb.ToString());
MessageBox.Show(msg);
sb.Length = 0;
}
}
In this way you have just one message for each wrong row also if there are 2 or more wrong values.
You set the variable shown to false before the if statement, move shown outside of the for-loop.
use "break;" after your messagebox. Your program will come out of the loop.
The data reader named eventreader can be empty and i'm trying to work out an appropriate statement to detect if the datareader contains rows and if the require record exists.
oledbexecute("SELECT [Unit No] FROM UnitOpenAtEvent WHERE [Event ID]='" + strDate + "'");
eventReader = dbcommand.ExecuteReader();
for (int i = 0; i < checkboxEvent.Items.Count; i++)
{
if (checkboxEvent.GetItemCheckState(i) == CheckState.Checked)
{
if (eventReader["Unit No"] != DBNull.Value)
{
while (eventReader.Read())
{
if (eventReader["Unit No"].ToString() == checkboxEvent.Items[i].ToString())
{
oledbexecute("INSERT INTO UnitOpenAtEvent ([Event ID],[Unit No]) VALUES ('" + strDate + checkboxEvent.Items[i].ToString() + "'");
intReader = dbcommand.ExecuteNonQuery();
}
}
}
else
{
oledbexecute("INSERT INTO UnitOpenAtEvent ([Event ID],[Unit No]) VALUES ('" + strDate + checkboxEvent.Items[i].ToString() + "'");
intReader = dbcommand.ExecuteNonQuery();
}
}
else if (checkboxEvent.GetItemCheckState(i) == CheckState.Unchecked)
{
// this is effectively a copy of above
}
}
You can check if the DataReader has any rows using e.g.
if (eventReader.HasRows)
Update:
Following from comment below re: IDataReader...
You could do something like this (shortened to include the most relevant bits for conciseness)
eventReader = dbcommand.ExecuteReader();
bool hasRow = eventReader.Read();
if (hasRow)
{
for (int i = 0; i < checkboxEvent.Items.Count; i++)
{
...
...
while (hasRow)
{
// Code in here to deal with each row
hasRow = eventReader.Read();
}
}
}
You may be misunderstanding how IDataReader.Read() works.
From that help page:
Return Value
Type: System.Boolean
true if there are more rows; otherwise, false.
and
The default position of the IDataReader is prior to the first record.
Therefore you must call Read to begin accessing any data.
So when you first get a datareader it won't be pointing to any data. If you call .Read() it will return true if there is a row to move to and move to it and if there are no rows it will return false.
Background: I'm writing an application in C# (.NET 3.5), that looks at multiple users Outlook 2003 calendars (using COM objects), gets the appointments and inserts the data for those appointments into a database.
Problem: After the first users calendar, any recurring items on the following calendars, will always have a start and end time of the first occurrence of that item. I'm releasing the COM Objects between users (and during, if the user has a lot of items), and The item collection is being restricted correctly (due to there only being a handful of the recurring tasks inserted (albeit the wrong start/end) instead of infinite from the "no end" tasks). The correct start/end time is part of the requirements, having the information available for this or another application to work out how much free time a user has for a given range of dates, and working hours.
Code: (Variable declarations omitted, they're at the top of the relevant functions)
Looping through the users (in Main()):
foreach (DataRow drUserCalendar in dtCalendars.Rows)
{
//for each calendar we're looking at, export their calendar data and put it in the database
try
{
appOutlook = new Outlook.Application();
ExportCalendar(drUserCalendar);
Marshal.FinalReleaseComObject(appOutlook);
GC.Collect();
}
catch (Exception ex)
{
//report error
}
}
Extracting information from the calendar
static void ExportCalendar(DataRow drUser)
{
strDisplayName = drUser["DisplayName"].ToString();
strUserID = drUser["ID"].ToString();
int.TryParse(drUser["PreviousDays"].ToString(), out intPrevious);
int.TryParse(drUser["FutureDays"].ToString(), out intFuture);
dtmAllowedPreviousStart = DateTime.Now.AddDays(-intPrevious);
dtmAllowedFutureStart = DateTime.Now.AddDays(intFuture);
nsOne = appOutlook.GetNamespace("MAPI");
nsOne.Logon(null, null, false, false);
rcpOne = nsOne.CreateRecipient(strDisplayName);
intCount = 0;
if (rcpOne.Resolve())
{
fldOne = nsOne.GetSharedDefaultFolder(rcpOne, Outlook.OlDefaultFolders.olFolderCalendar);
strRestrict = "[Start] > '" + MIN_START_DATE.ToString("g") + "' And [End] < '" + MAX_START_DATE.ToString("g") + "'";
itms = fldOne.Items;
itms.Sort("[Start]", Type.Missing);
itms.IncludeRecurrences = true;
itmsRestricted = itms.Restrict(strRestrict);
itmsRestricted.Sort("[Start]", Type.Missing);
itmsRestricted.IncludeRecurrences = true;
blnIsRecurring = false;
dicRecurringTaskTracker = new Dictionary<string, int>();
foreach (object objOne in itmsRestricted)
{
if (intCount >= 100 || blnIsRecurring)
{
//release COM objects. Outlook doesn't like you having more than 250 ish items without cleaning up.
Marshal.FinalReleaseComObject(appOutlook);
appOutlook = new Outlook.Application();
GC.Collect();
intCount = 0;
}
if (objOne is Outlook.AppointmentItem)
{
appItem = (Outlook.AppointmentItem)objOne;
blnException = false;
//get data from the item
strEntryID = appItem.EntryID;
strSubject = appItem.Subject;
strBody = appItem.Body;
dtmStart = appItem.Start;
dtmEnd = appItem.End;
blnException = EXCEPTIONS.Contains(strSubject);
//if the item is an exception we're done with it.
if (!blnException)
{
strRecurrenceInterval = "";
strRecurrenceType = "";
strRecurrenceInfo = "";
//check if it's a recurring task.
blnIsRecurring = appItem.IsRecurring;
if (blnIsRecurring)
{
//check to see if we've already had a task from this series
if (!dicRecurringTaskTracker.Keys.Contains(strEntryID))
{
//Start at 0 so the first (this) task
//is number 1.
dicRecurringTaskTracker.Add(strEntryID, 0);
}
//update number
dicRecurringTaskTracker[strEntryID] += 1;
//change the subject to add the count on the end
strEntryID = strEntryID + '-' + dicRecurringTaskTracker[strEntryID].ToString();
//it's a recurring task, so we need to find out when/how often.
rpTaskRecurrence = appItem.GetRecurrencePattern();
rtTaskRecurrenceType = rpTaskRecurrence.RecurrenceType;
strRecurrenceType = rtTaskRecurrenceType.ToString();
strRecurrenceInterval = rpTaskRecurrence.Interval.ToString();
switch (strRecurrenceType)
{
case "olRecursDaily":
case "olRecursMonthNth":
case "olRecursWeekly":
strRecurrenceInfo = rpTaskRecurrence.DayOfWeekMask.ToString();
break;
case "olRecursMonthly":
strRecurrenceInfo = rpTaskRecurrence.DayOfMonth.ToString();
break;
}
}
if (strEntryID != null && strSubject != null && dtmStart != null && dtmEnd != null
&& (intPrevious == 0 || (dtmStart > dtmAllowedPreviousStart)) && (intFuture == 0 || (dtmStart < dtmAllowedFutureStart)))
{
//build up the SQL
strSQL = "EXEC UpdateCalendarEntry ";
strSQL += "#EntryID='" + strEntryID + "', ";
strSQL += "#Subject='" + strSubject.Replace("'", "''") + "', ";
strSQL += "#Body='" + strSubject.Replace("'", "''") + "', ";
strSQL += "#StartDate='" + dtmStart.ToString("dd-MMM-yyyy HH:mm:ss") + "', ";
strSQL += "#EndDate='" + dtmEnd.ToString("dd-MMM-yyyy HH:mm:ss") + "', ";
strSQL += "#UserCalendarID=" + strUserID + ",";
strSQL += "#Recurring = " + blnIsRecurring.ToString() + ",";
strSQL += "#RecurrenceType = '" + strRecurrenceType + "',";
strSQL += "#RecurrenceInterval = '" + strRecurrenceInterval + "',";
strSQL += "#RecurrenceInfo = '" + strRecurrenceInfo + "';";
try
{
//Execute SQL
}
catch (Exception ex)
{
//Print error message
MessageBox.Show(ex.ToString());
}
}
}
Marshal.FinalReleaseComObject(appItem);
GC.Collect();
}
strEntryID = null;
strSubject = null;
strBody = null;
intCount++;
}
//finished looping, do some clean up.
Marshal.FinalReleaseComObject(nsOne);
Marshal.FinalReleaseComObject(rcpOne);
Marshal.FinalReleaseComObject(fldOne);
Marshal.FinalReleaseComObject(itms);
Marshal.FinalReleaseComObject(itmsRestricted);
GC.Collect();
}
else
{
throw new Exception("Could not resolve name");
}
}
I can't see an obvious problem with your code I'm afraid but as I expect your well aware there is probably some other stuff happening behind the scenes that is causing your problem.
I have covered some things I found to be best practice when I was working with this stuff in a blog article - http://jynxeddevelopment.blogspot.com. It might be worth a read to see if anything seems different to what you are doing, I think the section 'Keep references to everything' might be useful.
I'm not sure your COM objects will be collected with the GC call as you're not setting them to null first, however either way this shouldn't make a difference. I got this sort of thing working without any GC calls.
Things to look at:
objOne should be release EVERY loop if it's a COM object (I expect it is)
Closing your Outlook application before releasing it (appOutlook.Close()), I'm surprised your not getting lots of them hanging around
Examine every field you are using on the COM object, if they are COM objects too they might need Finalising too
Sorry this is nothing concrete but working with this stuff is hard work :/ Good luck!
-Jynx
After testing I've found that the issue relates to my (or whoever runs the application) permissions on the users (shared) calendar(s) that are being looked at.
It works on the first user because that's myself in this case. It doesn't work on users after that because i don't have sufficient rights it seems (confirmed by having my colleague changing it so default users are "owners" on his calendar and running the application again, and it working for his calendar).
I have since tried to use GetOccurrence(DateTime) (surrounded with a while loop), however that lead to the same issue. The function would error when there was no occurrence (as expected), but when it found an occurrence it would return a null object.
However because I wasn't using the object for anything other than getting the start and end date of the task, I worked it out manually (I had the original task, meaning i could get the duration, by incrementing each day until I get an occurrence I'd get the reccurring tasks start date, and using the duration of the task I calculated the end date).
It's not an ideal solution, but if you're just wanting to get recurring tasks it's simple enough to do (though resource consuming if you have a lot of reccurring tasks and are looping for a long period of time)