Why MonoTouch runtime crash with UIAlertView? - c#

I have a legacy C# class that extends UIAlertView for prompting the user to enter an IP address. It also will persist the IP address and load it as the default entry value.
The original code hacked the iOS4.x UIAlertView to add a text field and it had several layout problems. I converted the code to use the iOS5 UIAlertView's AlertViewStyle setting and everything worked fine.
I was asked to enable the Done key on the popup keyboard so that it could be a shortcut to the Connect button. I added code as follows to the class's constructor:
public AddressAlertView()
{
AddButton("Cancel");
AddButton("Connect");
AlertViewStyle = UIAlertViewStyle.PlainTextInput;
// NEW - get input text field, enable Done on the keyboard and
// allow Done to be the same as the Connect button
UITextField fld = GetTextField(0);
fld.ReturnKeyType = UIReturnKeyType.Done;
fld.ShouldReturn = (textField) =>
{
DismissWithClickedButtonIndex(1, true);
return textField.ResignFirstResponder();
};
// *** end of new code ***
// restore saved IP address
string savedAddress = NSUserDefault.StandardUserDefaults.StringForKey(AddressKey);
if (savedAddress != null)
fld.Text = savedAddress;
}
The class gets used like so:
....
AddressAlertView addrPrompt = new AddressAlertView();
addrPrompt.Title = "IP Address";
addrPrompt.Message = "Enter address";
addrPrompt.Dismissed += (sender, e) =>
{
if (e.ButtonIndex == 1)
{
// use IP address...
....
}
};
addrPrompt.Show();
....
Now the problem. When running this code, the AddressAlertView shows the popup dialog correctly and everything works as before. However, if I tap Done on the keyboard, the app bombs out in UIAlertView.
I tried moving the new code to the public override void LayoutSubviews() method, but it still crashes.
Any ideas?

You need to declare the AddressAlertView as a class member:
AddressAlertView addrPrompt;
public void DoSomething()
{
addrPrompt = new AddressAlertView();
//etc
}
The problem in this type of situations (I had many similar with UIAlertViews) is that the AddressAlertView object is GC'd and the event handler causes the crash. To make sure this is the issue, do not change anything, just remove/comment the addrPrompt.Dismissed += ... line and test it again. If it does not crash, than this is the solution.

According to Apple's documentation, you should not be subclassing UIAlertView:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html

Figured it out. In looking at the trace dump output from MonoTouch, there was a mention of an invalid object. Instead of getting the text field as a local variable, I save a reference to it as an instance variable:
private UITextField _addressTextField = null;
I changed the code in the constructor to:
_addressTextField = GetTextField(0);
Voila! That solved the crash. The local UITextField variable was undoubtedly getting GC'd, causing the crash when the ShouldReturn lambda expression was getting called.

Related

NAudio hanging and crashing when default device is changed

I've been stuck on this all day, so I'm going to post everything I've been able to find today that might be useful to helping me, it will be a long post. I'm having 2 issues that I believe are related to the same problem. First, let me explain what I am doing. I have 3 Winforms combo boxes that are bound to lists of all of the devices found by MMDeviceEnumerator. Two output device boxes, one input device. I am using the MMDeviceEnumerator to register a callback for whenever the devices are changed, removed, or a default device is set. The callback fires an event that then invokes a delegate to the form thread to re-enumerate the devices into combo boxes. It looks like this:
public void OnDefaultDeviceChanged(DataFlow dataFlow, Role deviceRole, string defaultDeviceId)
{
Devices.OnDevicesUpdated();
}
//The handler called by this event:
private void UpdateDeviceSelectors(object? sender = null, EventArgs? e = null)
{
Invoke(delegate ()
{
int primaryIndex = Devices.PrimaryOutput + 1, secondaryIndex = Devices.SecondaryOutput + 2, microphoneIndex = Devices.Microphone + 1;
Devices.Refresh();
try
{
SecondaryOutputComboBox.SelectedIndex = secondaryIndex;
PrimaryOutputComboBox.SelectedIndex = primaryIndex;
MicrophoneSelectComboBox.SelectedIndex = microphoneIndex;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
}
Now for the two issues I have. The first one involves a semi-random crash that leads back to NAudio.Wasapi.dll, where a System.ExecutionEngineException is thrown. It is kind of easy to reproduce. All I do is change the values of the combo boxes, switch the default devices around, and it will randomly crash.
The second issue occurs when another part of my code is involved. I have a microphone injector, which redirects a WaveInEvent that records a selected input device to a WaveOutEvent, like a loopback. Here is the relevant code for this:
public void Start()
{
if (Soundboard.Devices.SecondaryOutput == -2) return;
micStream = new WaveInEvent();
micStream.BufferMilliseconds = 50;
micStream.WaveFormat = new WaveFormat(44100, WaveIn.GetCapabilities(Soundboard.Devices.Microphone).Channels);
micStream.DeviceNumber = Soundboard.Devices.Microphone;
WaveInProvider waveIn = new(micStream);
var volumeSampleProvider = new VolumeSampleProvider(waveIn.ToSampleProvider());
volumeSampleProvider.Volume = 1 + Settings.Default.MicrophoneGain;
virtualCable = new WaveOutEvent();
virtualCable.DesiredLatency = 150;
virtualCable.DeviceNumber = Soundboard.Devices.SecondaryOutput;
virtualCable.Init(volumeSampleProvider);
micStream.StartRecording();
virtualCable.Play();
}
public void Stop()
{
try
{
if (micStream != null && virtualCable != null)
{
micStream.Dispose();
micStream = null;
virtualCable.Dispose();
virtualCable = null;
}
}
catch
{
micStream = null;
virtualCable = null;
}
}
In the delegate mentioned earlier, I am calling the Stop method of the Mic Injector and then the Start method to refresh the WaveIn and WaveOut devices to use the current device numbers so users do not see a device selected when a different device is being used. When this happens, the program, rather than crashing instantly and inconsistently, always hangs and has to be killed from the task manager. I am certain these 2 problems are related to the same root cause, but I have no idea what that root cause may be. Switching to Wasapi, DirectSound, or ASIO won't work because they lack certain functionalities I need, so I would really like to get this working still using Wave streams.
I've tried to find different ways to detect the device changes, assuming it is an issue deep inside NAudio, but I just can't find anything else. For the second problem specifically, I have moved the calls to the Mic Injector around thinking it may be a threading issue or something, but it didn't work or change the behavior.

The application called an interface that was Marshalled for different thread error when using MangementObjectSearcher

I have used MouseKeyboardActivityMonitor to set some limitations for user activities for example disable mouse.
www.codeproject.com/Articles/7294/Processing-Global-Mouse-and-Keyboard-Hooks-in-C
I have these code in my form
public partial class MyForm:Form
{
KeyboardHookListener kl;
MouseHookListener ml;
MyForm:Form()
{
ml = new MouseHookListener( new GlobalHooker());
ml.Enabled=true;
}
private void MyForm_Load
{
ml.MouseDownExt += ml_MouseDownExt;
// And same thing for Click or ...
}
private void ml_MouseDownExt( object sender,MouseEventExtArgs e)
{
e.Handled= true;
// I have got hard disk serial number here
string sn = HardDisk.Serial;
}
}
And code of HardDisk.Serial
ManagementObjectSearcher s= new ManagementObjectSearcher(" SELECT *...");
foreach( var wmi in s.Get())
{
}
I get error when I click on MyForm .
When I built my solution and run it manually
I get this error
The application called an interface that was Marshalled for different thread
Stack:
at system.management.MangementException.ThrowWithExtendedInfo( Exception e)
at system.management.MangementObjectSearcher.Get()
at HardDisk.Get_serial()
at ml_MouseDownExt( object sender,MouseEventExtArgs e)
at MouseKeyboardActivityMonitor.MouseHookListener.InvokeMouseEventHandlerExt(EventHandler'1 handler,MouseEventExtArgs e)
But when I run my solution with visual studio an exception will throw at HardDisk.serial
at s.Get line , I get this
Error:
Managed debugging assistant ' DisconnectedContext'
Has detected a problem in 'my app Name.exe'
Transition into com context 0xa4206 for this runtime callable wrapper failed with following error :
an outgoing call can't be made since the application is dispatching an input asynchronous call
It obvious that two error is from MangementObjectSearcher class.I get serial number in another place in MyForm .errors just occurres when get serial in ml_MouseDownExt method or other method that has been added to events of GlobalHooker .I have seen msdn. In inheritance hierarchy of MangementObjectSearcher I see System.MarshalByRefObject
https://msdn.microsoft.com/en-us/library/system.management.managementobjectsearcher(v=vs.110).aspx
I don't know that it is related to these errors or not
How should I avoid these errors?
Your hook callback isn't being raised on the right thread. And that's just the first problem. Wrap it in a BeginInvoke and all will be well:
private void ml_MouseDownExt( object sender,MouseEventExtArgs e)
{
e.Handled= true;
var wrongThread = new Action(()=>
{
// I have got hard disk serial number here
string sn = HardDisk.Serial;
//put anything else you were planning on doing with sn here
}
BeginInvoke(wrongThread, null);
}
The second problem is that you're trying to interact with a COM object in the handler for a global hook. BeginInvoke should get around that nicely by delaying it for a few microseconds.
Don't forget to make sure Dispose gets called on that global hook. Closing the app isn't enough unless you like rebooting often.

wpf combobox function operating on a delay

private void messageTypeMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string option = messageTypeMenu.Text;
if (option.Equals("Errors"))
{
LogParser.information = false;
LogParser.messages = false;
LogParser.errors = true;
}
if (option.Equals("Information"))
{
LogParser.information = true;
LogParser.messages = false;
LogParser.errors = false;
}
if (option.Equals("Messages"))
{
LogParser.information = false;
LogParser.messages = true;
LogParser.errors = false;
}
}
is a function that is supposed to update a static class depending on the selected value. however, its operating on a delay and im not sure why.
for some context, information, messages, and errors are 3 different message type for logs stored in a file that i am parsing. when someone selected information and hits view or save, they should only get the logs of that type. however, it appears to be operating on a delay.
for example, if i load in a file of logs and select information, it will show me the whoel file. then if i switch to messages, i will see only the information. then if i switch back to information, it will only show messages and so on. it seems like the combobox is looking at the value that was already present when they go to change this, but im not sure why.
In order to stay synchronized with the window, you should consider using the observable pattern and implement INotifyPropertyChanged combined with bindings. You will need to redesign your application since you can't implement interfaces on static classes but that may be worth it.
Another way would be using Dependency Properties.

Problems when starting application with files from Explorer

I just added the feature where I can make my program the default-program for multiple music filetypes by following this question on Stackoverflow
It seemed to work fine, since all of the mp3 files etc has my icon on them in Explorer.
But when I tried starting one (I have handled the arguments just fine) the process itself starts (it shows up in Task Manager) but nothing happens. I even tried adding a Messagebox in the beginning of the "Window.Initialized"-event, but no messagebox came up.
What might be the cause of this problem? I have literally no idea what's wrong.
If you need code or anything, just ask for it since I don't know what to include in this question.
Thank you.
Here's the code for Window_Initialied
private void Window_Initialized(object sender, EventArgs e)
{
MessageBox.Show("asd");
HandleInstances(); // Checks if multiple instances of the program is running. Exits it if there's more than one instance (tried commenting this out, didn't work)
if (Properties.Settings.Default.HasRegisteredFiletypes == false) // Checks if theres a need to add the filetypes
AddExec();
StartWithMusic(Environment.GetCommandLineArgs().ToList()); // Here I call for the arguments. Checks if there are valid files in the arguments (.mp3, .flac etc)
SettingsLoadBGs.IsChecked = Properties.Settings.Default.LoadBGs;
// set accentcolor box
List<AccentColor> ac = new List<AccentColor>();
string userAccent = Mplayer.Properties.Settings.Default.Accent.ToLower();
foreach (Accent c in ThemeManager.DefaultAccents)
{
AccentColor acEnt = new AccentColor();
acEnt.Name = c.Name;
ac.Add(acEnt);
if (c.Name.ToLower() == userAccent)
ThemeManager.ChangeTheme(this, c, Theme.Dark);
}
ThemeManager.IsThemeChanged += new EventHandler<OnThemeChangedEventArgs>(ThemeChanged);
accentChooserBox.ItemsSource = ac;
}

C# preserving variables across classes

I'm working on a plugin application. I have a frmDatasheet.cs (backend) and it's DatasheetPlugin.cs (frontend). I am working on a broadcast event between this datasheet and a model plugin, so that if someone goes back to the datasheet, makes some changes, and then goes back to modeling, modeling will be know of the new state and update itself.
The problem I'm having is the call to Broadcast is in the frmDatasheet, which goes to the datasheetPlugin to Raise the Broadcast Request, and I'm getting nulls because I'm leaving the plugin then coming back to it and everything is lost in that back and forth. Here's my code:
//in the frmDatasheet.cs, click GoToModeling, this is last few lines
IDictionary<string, object> packedState = new Dictionary<string, object>();
packedState = PackState(); <----packs up state to send
frmState.Broadcast(packedState); <----- had to instantiate new Plugin to get at .Broadcast
at the top of frmDataSheet.cs
private DatasheetPlugIn frmState = new DatasheetPlugIn();
Is this possibly the problem?? getting new DatasheetPlugin..does that clear it out, but how else can I get at the .Broadcast?
Here's my code in the DatasheetPlugin.cs
public void Broadcast(IDictionary<string,object> packedState)
{
signaller.RaiseBroadcastRequest(packedState);
}
I don't get an error, but the signaller shows the events (BroadcastState, ProjectOpened, ProjectSaved), but they have all null values. Then it goes to the signaller, checks to see if BroadcastState has any subscribers, fails because signaller is null.
How do I ensure that when I get back to the Plugin from the frmDatasheet, everything is still intact? If I put the call to .Broadcast locally in the plugin on some click event, the signaller is not null. So, I'm pretty sure its due to the back and forth and not preserving information.
Update: I should probably include code related to the signaller. Each plugin has:
private VBTools.Signaller signaller; //declared at top of plugin
//This function imports the signaller from the VBProjectManager
//Happens when app loads for each plugin.
[System.ComponentModel.Composition.Import("Signalling.GetSignaller", AllowDefault = true)]
public Func<VBTools.Signaller> GetSignaller
{
get;
set;
}
public void OnImportsSatisfied()
{
//If we've successfully imported a Signaller, then connect its events to our handlers.
signaller = GetSignaller();
signaller.BroadcastState += new VBTools.Signaller.BroadCastEventHandler<VBTools.BroadCastEventArgs>(BroadcastStateListener);
signaller.ProjectSaved += new VBTools.Signaller.SerializationEventHandler<VBTools.SerializationEventArgs>(ProjectSavedListener);
signaller.ProjectOpened += new VBTools.Signaller.SerializationEventHandler<VBTools.SerializationEventArgs>(ProjectOpenedListener);
this.MessageSent += new MessageHandler<VBTools.MessageArgs>(signaller.HandleMessage);
}
Thanks for any insight!!
When you instantiate a new DatasheetPlugin class that has this line:
private VBTools.Signaller signaller; //declared at top of plugin
The "signaller" object will be null until you instantiate it, which appears you do so when calling OnImportsSatisfied(). So when you say that you are calling...
private DatasheetPlugIn frmState = new DatasheetPlugIn();
...at the top of the file, if you never call "OnImportsSatisfied()" the signaller will be null.
It may be that you are getting confused between class member variables and static variables. http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.80).aspx
To solve this problem, I put the GoToModeling click event in the DSPlugin (was in frmDSheet).
void btnGoToModeling_Click(object sender, EventArgs e)
{
IDictionary<string, object> packedState = new Dictionary<string, object>();
packedState = _frmDatasheet.PackState();
Broadcast(packedState);
}
Then the packedState is populated with the current state of the datasheet to be sent to the Broadcast() which is in the same DSPlugin, so I don't lose anything going back and forth between the 2 classes. I took what I needed from the GoToModeling click method in the frmDSheet and moved it up to the PackState so that I can grab that too before broadcasting.

Categories