I'm trying to build a TAPI based phone call system using JulMar's Atapi x86. One of the functions is to pop a specific form on an inbound call. However, whenever the form pops, it comes up incorrect, as shown below (I have tried several forms as a test and they all do the same thing). There is no error, nothing in the output window to suggest what the issue is.
Code:
private void incomingcall(object sender, NewCallEventArgs e)
{
string phonenumber = e.Call.CallerId; //get the phone number of the call
SqlCommand getincoming = new SqlCommand(Querystrings.getincomingquery(), DB);
getincoming.Parameters.AddWithValue("##TELEPHONE", phonenumber);
DataTable results = new DataTable();
try
{
DB.Open();
using (var results = getincoming.ExecuteReader())
{
results.Load(results);
}
}
catch (Exception ex)
{
Inbound ib = new Inbound(phonenumber, null);
ib.Show();
}
finally
{
DB.Close();
}
if (results.Rows.Count == 1)
{
loadcontactrequest(Convert.ToInt32(results.Rows[0].ItemArray[0]), phonenumber);
}
else
{
loadinbound(phonenumber, results);
}
}
I have loaded these forms outside of this function at other points, meaning it is something to do with this function. Does anybody know where I'm going wrong?
EDIT:
private void loadcontactrequest(int ContactID, string phonenumber)
{
ContactRequest cr = new ContactRequest(ContactID, Global.loginbound("Single customer found", phonenumber));
cr.Show();
}
These functions have been tested elsewhere and work correctly individually, I believe it might be TAPI related.
EDIT 2 - Delegate:
public static void inittapi()
{
if (TestOptions.notapi)
return;
tapi = new TapiManager("Omitted");
tapi.Initialize();
foreach (TapiLine ad in tapi.Lines) //Get all lines available to this PC
{
if (ad.Name.ToUpper().Contains("Omitted"))
{
phoneline = ad;
phoneline.Open(MediaModes.All); //Open the phone line for making and receiving calls
phoneline.NewCall += new EventHandler<NewCallEventArgs>(new TAPI().incomingcall); //Add the incoming call event handler
}
}
}
It's possible that this event is triggered on a different thread than the UI thread of your application.
Modify the method like this to test whether this is the problem:
private void incomingcall(object sender, NewCallEventArgs e)
{
Form form;
if(Application.OpenForms.Count > 0)
{
form = Application.OpenForms[0];
}
if (form != null && form.InvokeRequired)
{
form.BeginInvoke(new Action(() => { incomingcall(sender, e); }));
return;
}
// Your current code goes here
}
This will identify that we are in a different thread than your main form (form) was created on and then execute the function again on the main form's thread.
Related
I'm currently working on a method that gives the user the possibility to add a handscanner to a dicitionary in order to scan some barcodes with it. (before i started the scanners were hardcoded in the dictionary). my colleague from which i got this project, implemented the rawinput_dll in order to get all of the necessary data from the barcode scanner. The method to get the data is shown below:
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (!Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
return;
}
else if (Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
if (e.KeyPressEvent.KeyPressState == "MAKE")
{
return;
}
if (e.KeyPressEvent.VKeyName != "\n")
{
scanNumber += e.KeyPressEvent.VKeyName;
return;
}
devID = e.KeyPressEvent.DeviceName;
Debug.Print(devID);
Aufrufen(scanNumber);
scanNumber = "";
}
}
Basically there are three classes in this program (FrmMenu, FrmSettings and a Class for the Scanner itself). If you want to add settings for the program you click on a button that opens up a new instance of FrmSettings
private void BtnSettings_Click(object sender, EventArgs e)
{
FrmSettings settings = new FrmSettings();
settings.ShowDialog();
settings.BtnSave_Click(sender, e);
settings.Dispose();
}
In this form there 2 buttons where you can choose if you want to add a scanner that scans even numbers or one that scans odd ones. If you press one of the buttons you need to scan a barcode in order to get the information (VID of Scanner) which is used as key to add the new scanner to the dictionary.
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (newScanner == true)
{
devIDnew = e.KeyPressEvent.DeviceName;
scannerAnlegen(devIDnew);
}
}
scannerAnlegen is the methode that adds the scanner to the dict.
public void scannerAnlegen(string devIDnew)
{
if(EvenOrOdd == true)
{
Scanner ger = new Scanner("dev3", "even");
FrmMenu.Scanners.Add(devIDnew, ger);
newScanner = false;
}
else
{
Scanner ug = new Scanner("dev4", "odd");
FrmMenu.Scanners.Add(devIDnew, ug);
newScanner = false;
}
}
my problem rn is, that it seems like i cant get out of this OneKeyPressed method of the Settings class. the logic of the OneKeyPressed method of the FrmMenu Class is that it can only proceed if the scanner is in the dictionary. Adding the scanner seems to work because when i debug and try to add one scanner the second time it throws and exception and says something like "element with this key already added". But why does this code doesn't continue then?
I'm using AutoUpdater.Net and I start the check on the application launch (this one work well). A second time from the MainWindow.
This is the function that I call :
public void CheckUpdate()
{
if (IsConnectedToInternet())
{
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
Update.InstalledVersion = new Version(fvi.FileVersion);
Update.Synchronous = true;
//comment the line below to show the AutoUpdater.NET forms
Update.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
Update.Start("http:/myurl/");
}
else
IminaDialog.ShowMessage("Update error", "It seems you're not connected to internet.", ModalWindowButtons.Ok);
}
And the function AutoUpdateOnCheckForUpdateEvent is executing twice. It come to the last line of the method and directly restart the method. The call stack from the loading or from the MainWindow still the same. From the MainWindow I call it from the Instance of the parent class.
I hope someone can help and tell me if you want more informations.
EDIT
This is the call that execute twice AutoUpdateOnCheckForUpdateEvent (CheckUpdate execute once every time):
public static WpfCommand CommandCheckUpdate
{
get
{
if (_commandCheckUpdate == null)
{
_commandCheckUpdate = new WpfCommand();
_commandCheckUpdate.Executed += (sender, e) =>
{
FrameworkController.Instance.CheckUpdate();
};
}
return _commandCheckUpdate;
}
}
If I only call FrameworkController.Instance the CheckUpdate don't execute.
And here it's were the method is called on load (inside FrameworkController) :
public void Start(string[] mainArgs)
{
InitializeLog();
if (!License.RegisterLicense(null))
return;
DefaultMenuItems.PopulateMenuService(MenuService.Instance);
IminaUtils.Initialize(); // Initialize helper methods
CheckUpdate();
_firstConfigurationLoading = Configuration.Load();
MainWindowViewModel.ClearDiagnostic();
if (RightRepository.Instance.CurrentUser == null)
MainWindowViewModel.OpenLogin();
if (Configuration.ShutdownApp)
return;
ConnectConfiguration(mainArgs);
}
In my Add-in for Visio, I have set a handler for 'ShapeAdded'.
This fired for the first 2 or 3 shapes that are added, but then just stop firing altogether.
Here's the basic outline of how my add-in functions:
User adds shape to page
On ShapeAdded, a form is displayed.
User enters text in form, presses search button
Call to stored Procedure (parameter = user text)
Form's datagridview is populated with results.
User double-clicks result row required.
Form closes, selected value becomes shape text.
If I comment out my code after item (3) - then my event handler continues firing without issue. I can add new shapes all day long.
BUT - once I let code call the stored procedure (step 4), then that is where problems arise.
Very specifically : da.Fill(dt)
I may manage 1 to 6 shape adds, but sooner or later, the event just stops firing.
(*update 8th Jan: Recordset size seems to affect the issue. When 1100 rows are returned each time, I manage to add around 6 shapes to my page. When 3 rows returned each time, I get to add up to 18 shapes before the events stop firing.
That tells me there is something not 'clean' about the way I am handling my data calls - but I cannot see what it is !!)
I am totally baffled as to why calling the stored procedure would interfere with my event handler !?!?
Especially without any error messages.
Has anyone any ideas whatsoever as to what I might be doing wrong ?
Or even, ideas around how to debug this in a better manner ?
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.MarkerEvent += new Visio.EApplication_MarkerEventEventHandler(Application_MarkerEvent);
}
private void Application_MarkerEvent(Visio.Application visapp, int SequenceNum, string ContextString)
{
if (ContextString.Contains("soln=myApplication") && ContextString.Contains("/cmd=DocCreated"))
{
SetDocEvents();
}
}
public void SetDocEvents()
{
Microsoft.Office.Interop.Visio.Document doc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
doc.ShapeAdded += new Microsoft.Office.Interop.Visio.EDocument_ShapeAddedEventHandler(onShapeAdded);
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}
private void onShapeAdded(Visio.Shape Shape)
{
Form_Entity fe = new Form_Entity(Shape.Text);
fe.ShowDialog();
fe.Dispose();
}
// ... other stuff
}
Form Code:
public partial class Form_Entity : Form
{
public Int32 eid { get { return m_id; } }
public string ename { get { return m_name; } }
public string eabbr { get { return m_abbr; } }
private Int32 m_id;
private string m_name;
private string m_abbr;
public Form_Entity()
{
InitializeComponent();
}
public Form_Entity(String search)
{
InitializeComponent();
txt_search.Text = search;
}
private void Cmd_Search_Click(object sender, EventArgs e)
{
String sample = txt_search.Text;
DataTable dt = new DataTable();
SqlConnection myConn = new SqlConnection("Server=xxxxxxx;Database=xxxxxxxx;Trusted_Connection=True;");
myConn.Open();
SqlCommand myCmd = new SqlCommand("[dbo].[mySearch]", myConn);
myCmd.Parameters.Add(new SqlParameter("#searchText", sample));
myCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(myCmd);
da.Fill(dt);
dataGridView1.DataSource = dt;
myCmd.Dispose();
myConn.Close();
}
}
** Files for this project
Visual Studio Solution
T-SQL to create sample table/data.procedure
Viso Template
ReadMe.txt
http://www.netshed.co.uk/temp/Vis_Sample.zip
I think the problem here is that you're not keeping the doc object in scope and Visio will stop reporting events for which there is no reference.
You can add a field (or property) as follows and then the reference and associated events should be maintained:
public partial class ThisAddIn
{
private Visio.Document _targetDoc = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.MarkerEvent += Application_MarkerEvent;
}
public void SetDocEvents()
{
_targetDoc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
_targetDoc.ShapeAdded += onShapeAdded;
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}
I am using the TAPI 2.0 wrapper from JulMar (https://atapi.codeplex.com/) and I'm having trouble with it.
The Initialization
void initTAPI()
{
myTAPI = new TapiManager("GetCaller");
if (!myTAPI.Initialize())
{
MessageBox.Show("FAILED!");
}else
{
name = myTAPI.Lines[0].Name;
lineName = (myTAPI != null && myTAPI.Lines.Length > 0 ? name : string.Empty);
foreach(TapiLine line in myTAPI.Lines)
{
line.NewCall += this.OnNewCall;
line.Ringing += this.OnRinging;
line.CallStateChanged += this.OnCallState;
line.CallInfoChanged += this.OnCallInfo;
}
MessageBox.Show(lineName);
}
}
So I get the lineName. When I now dial a number through the program, it fires
OnCallState
private void OnCallState(object sender, CallStateEventArgs e)
{
if (InvokeRequired == true)
{
this.BeginInvoke(new EventHandler<CallStateEventArgs>(this.OnCallState), new object[] { sender, e });
return;
}
label1.Text = "Outgoing Call...";
}
But what I actually want to do is to get the number of an incoming call, but OnCallInfo does not get fired.
OnCallInfo
private void OnCallInfo(object sender, CallInfoChangeEventArgs e)
{
if (InvokeRequired == true)
{
this.BeginInvoke(new EventHandler<CallInfoChangeEventArgs>(this.OnCallInfo), new object[] { sender, e });
return;
}
label1.Text = "Incoming Call...";
}
It says somehwere, that it only works with x86, so I changed the target but still no success.
PS: I have a call manager (ProCall) installed on the same machine, that tells me when someone calls, so I should be able to get the info in c# as well?
Here is the whole code if someone is interested: http://pastebin.com/Q5W5iGun
Depending on TSP, you may get call info messages, but TAPI does not force the driver to do this. So some TSP make you get the info yourself. In the Win32 API this is done via lineGetCallInfo.
After a quick look in this atapi wrapper, this happens in the GatherCallInfo method of the TapiCall class. However I can see no way to trigger this manually in this wrapper. You would need to modify the atapi source to make this a public method.
You can use example from TAPI which do the same. The only difference is new line.Monitor() method
foreach (TapiLine line in tapiManager.Lines)
{
try
{
line.NewCall += OnNewCall;
line.CallStateChanged += OnCallStateChanged;
line.CallInfoChanged += OnCallInfoChanged;
line.Monitor();
}
catch (TapiException ex)
{
LogError(ex.Message);
}
}
For further reading read this https://atapi.codeplex.com/SourceControl/latest#Atapi/trunk/source/test/TcMon/TapiMonitorForm.cs
I am working in Visual Studio 2010 .NET 4.0 in C# using WinForms.
The form has a single DataGridView that is data bound to a DataSet.
The DataSet is being populated from a Thread that is processing data being read from a ConcurrentQueue.
The code is also using a semaphore to serialize access to the DataSet as it can be accessed from the "worker" Thread and the UI main thread ( when updating the DataGridView ).
The UI/Form has a System.Windows.Forms.Timer that fires every 1/4 second to call a function that causes the DataGridView to be updated with the current contents of the DataSet.
The code runs correctly until the point at which the DataGridView data scrolls and the scroll bar becomes visible. At this point the entire form becomes unresponsive - title caption says "Program Not Responding".
The interesting thing is that this DOESN'T happen while running under the debugger. Only when the program is deployed. And no exceptions are ever raised.
I've tried using both Invoke and BeginInvoke with no change in behavior.
Questions:
1- What might be wrong with my code below?
2- Since I can't observe this behavior while running under the debugger, how do I find out what is causing the problem?
The wall of code is provided below. I know it's a lot of code, but I cut out as much as I could.
// deleted using statements for brevity
namespace namcom
{
public partial class wndXMLtrans : Form
{
private DataSet dsRecords = new DataSet();
private Thread threadConsumeXML = null;
private static Semaphore ResourceLock;
public wndXMLtrans(String ip)
{
InitializeComponent();
ResourceLock = new Semaphore(1, 1);
curSize = this.Size;
m_ip = ip;
}
private Boolean AddRowToDataSet(String[] columns, String xml)
{
Boolean retCode = true;
String value = String.Empty;
Int64 id = -1;
DataRow row;
ResourceLock.WaitOne();
row = dsRecords.Tables[0].NewRow();
// prepare row code omitted - brevity
// add new data row to DataSet
dsRecords.Tables[0].Rows.Add(row);
ResourceLock.Release();
// SQL inserts into DB removed - brevity
return (retCode);
}
private Boolean HandleSingleXMLMessage(String[] columns, String xml, out String exceptionMessage)
{
Boolean boolRet = true;
exceptionMessage = String.Empty;
// store data in dataset and database
if ( closeRequested == false )
{
AddRowToDataSet(columns, xml);
}
return (boolRet);
}
private Boolean StoreG2SMessages(String message)
{
// code removed - brevity
// removed code just parses out string and in a loop calls HandleSingleXMLMessage
// until all XML in message have been processed
HandleSingleXMLMessage(columns,xml, out exceptionMessage) // call in loop
return (ret);
}
// pull XML out of mainwnd.msgQueue and update database
private void G2SParseThread()
{
String Data;
String exceptionMsg = String.Empty;
while ( /* thread is to be active - code removed for brevity */)
{
Data = String.Empty;
if (mainwnd.msgQueue.TryDequeue(out Data) == true)
{
this.StoreG2SMessages(Data);
}
Thread.Sleep(20);
}
// thread ended cleanup code removed - brevity
}
private void StartThreads()
{
// start XML packet processing thread
threadConsumeXML = new Thread(G2SParseThread);
threadConsumeXML.SetApartmentState(ApartmentState.STA);
threadConsumeXML.Start();
threadMonitor = new Thread(() => threadHandleStatusMsg(ref mainwnd.statusQueue));
threadMonitor.Start();
}
private void wndXMLtrans_Shown(object sender, EventArgs e)
{
// remove SQL code - brevity
// fill dsRecords ( DataSet )
try
{
var adapter = new SQLiteDataAdapter(selectSQL, dbConnection);
adapter.Fill(dsRecords);
dataGrid.DataSource = dsRecords.Tables[0].DefaultView;
adapter.Dispose();
}
catch { } // catch code removed - brevity
StartThreads();
}
private void gridUpdateTimer_Tick(object sender, EventArgs e)
{
ResourceLock.WaitOne();
try
{
if (dataGrid != null && numRows < dsRecords.Tables[0].Rows.Count)
{
if (dataGrid.RowCount > 0)
{
dataGrid.FirstDisplayedScrollingRowIndex = dataGrid.RowCount - 1;
}
dataGrid.Refresh();
numRows = dsRecords.Tables[0].Rows.Count;
}
}
catch { }
ResourceLock.Release();
}
}
}
EDIT: Hans provided the answer which is that you can't update a bound DataSet from a worker thread. So I was able to fix the problem by adding the following:
These are called by delegate functions using Invoke. Unbind called before DataSet update and Bind after DataSet is updated. This works, but causes the DataGridView to appear to flicker or redraw multiple times. Any way to remedy this?
public void UnbindDataGridView(DataGridView grid)
{
grid.DataSource = null;
}
public void BindDataGridView(DataGridView grid)
{
grid.DataSource = dsRecords.Tables[0].DefaultView;
}