I decompiled a .NET Application to write my own Injection Software. A Thread Error don't want to get removed. I tried everything but it keeps telling me that something is wrong...
Code (Screenshot)
{
using System;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
internal class NamedPipes
{
public static string luapipename = "IDUNNOPIPE123";
public static void LuaPipe(string script)
{
if (NamedPipeExist(luapipename))
{
new Thread (delegate {
try
{
using (NamedPipeClientStream stream = new NamedPipeClientStream(".", luapipename, PipeDirection.Out))
{
stream.Connect();
using (StreamWriter writer = new StreamWriter(stream, Encoding.Default, 0xf423f))
{
writer.Write(script);
writer.Dispose();
}
stream.Dispose();
}
}
catch (IOException)
{
MessageBox.Show("Error occured connecting to the pipe.", "Connection Failed!", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
catch (Exception exception1)
{
MessageBox.Show(exception1.Message.ToString());
}
}).Start();
}
else
{
MessageBox.Show("Please Inject " + Functions.exploitdllname + " before Using this!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
public static bool NamedPipeExist(string pipeName)
{
bool flag;
try
{
int timeout = 0;
if (!WaitNamedPipe(Path.GetFullPath($"\\\\.\\pipe\\{pipeName}"), timeout))
{
bool flag4;
int num2 = Marshal.GetLastWin32Error();
if (num2 != 0)
{
if (num2 != 2)
{
goto TR_0005;
}
else
{
flag4 = false;
}
}
else
{
flag4 = false;
}
return flag4;
}
TR_0005:
flag = true;
}
catch (Exception)
{
flag = false;
}
return flag;
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool WaitNamedPipe(string name, int timeout);
}
}
I think you are getting the The call is ambiguous between the following methods or properties: 'Thread.Thread(ThreadStart)' and 'Thread.Thread(ParameterizedThreadStart)' error with this.
As you correctly noted, the compiler can't distinguish what type the Thread constructor parameter is. ParameterizedThreadStart is used if you pass a parameter to your delegate when calling the Thread.Start method. Your code does not do this. Thus, you need a ThreadStart delegate here. Its syntax is
public delegate void ThreadStart();
I.e. it does not accept any parameter and does not return a value. So, you can just pass a lambda function that does the same.
new Thread(() => {
// ...
}).Start();
Related
I had an HTML button with id=submit so using Selenium IDE I selected the command "Click" with target as id=submit but selenium IDE pass this line as if it is executed but without the button really clicked so the form is not submitted !! what is wrong ?
Here is the C# code I exported. Problem is in driver.FindElement(By.Name("submit")).Click();
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
namespace SeleniumTests
{
[TestFixture]
public class Web
{
private IWebDriver driver;
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
driver = new FirefoxDriver();
baseURL = "I cannot share url";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
[Test]
public void TheWebTest()
{
driver.Navigate().GoToUrl("I cannot share url");
driver.FindElement(By.Id("submit")).Click();
for (int second = 0;; second++) {
if (second >= 60) Assert.Fail("timeout");
try
{
if ("All Campaigns" == driver.FindElement(By.CssSelector("#CampaignsTable > div.box > div.title")).Text) break;
}
catch (Exception)
{}
Thread.Sleep(1000);
}
for (int second = 0;; second++) {
if (second >= 60) Assert.Fail("timeout");
try
{
if ("Last" == driver.FindElement(By.Id("cloaker_last")).Text) break;
}
catch (Exception)
{}
Thread.Sleep(1000);
}
driver.FindElement(By.XPath("(//img[#alt='Click to edit the rotator settings'])[1]")).Click();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).Clear();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).SendKeys("dghfghf");
driver.FindElement(By.Id("addNewUrl_0_geo_0_mobile_url")).Click();
driver.FindElement(By.Id("submit")).Click();
driver.FindElement(By.Id("submit")).Click();
for (int second = 0;; second++) {
if (second >= 60) Assert.Fail("timeout");
try
{
if ("Success" == driver.FindElement(By.CssSelector("div.message.green > span > b")).Text) break;
}
catch (Exception)
{}
Thread.Sleep(1000);
}
driver.FindElement(By.XPath("(//img[#alt='Click to edit the rotator settings'])[2]")).Click();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).Clear();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).SendKeys("tyujghghj");
driver.FindElement(By.Name("submit")).Click();
driver.FindElement(By.XPath("(//img[#alt='Click to edit the rotator settings'])[3]")).Click();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).Clear();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).SendKeys("gfhjghjgh");
driver.FindElement(By.Name("submit")).Click();
driver.FindElement(By.XPath("(//img[#alt='Click to edit the rotator settings'])[4]")).Click();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).Clear();
driver.FindElement(By.Id("addNewUrl_0_geo_0_url_to_cloak")).SendKeys("ghghkghk");
driver.FindElement(By.Id("addNewUrl_0_geo_0_mobile_url")).Click();
driver.FindElement(By.Name("submit")).Click();
}
private bool IsElementPresent(By by)
{
try
{
driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
private bool IsAlertPresent()
{
try
{
driver.SwitchTo().Alert();
return true;
}
catch (NoAlertPresentException)
{
return false;
}
}
private string CloseAlertAndGetItsText() {
try {
IAlert alert = driver.SwitchTo().Alert();
string alertText = alert.Text;
if (acceptNextAlert) {
alert.Accept();
} else {
alert.Dismiss();
}
return alertText;
} finally {
acceptNextAlert = true;
}
}
}
}
Perhaps if you shared existing code, the URL you are trying to reference, etc. we could be more helpful.
It's very hard to answer a question without enough information.
You haven't shared the code so It is hard to analyze your problem. This might help you -
just change your command click to clickAt and then try.
I have a Arduino communicating the state of a button through serial port. On the PC I have a Windows Service application that is polling the serial port to get the state of the button. Whenever I receive a message in the serial port about the button state, I write the received message to a file, and also want to log the current mouse position. Following this I would like to raise a left click event at the current mouse position.
For some reason, when I read the current mouse position using GetCursorPos, I always get 0, 0 as the mouse position. I am using user32.dll to do this.
The following is my C# code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespace MouseInterface
{
public class MyMouseClass
{
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(ref Point pt);
public static void LeftDown(int x, int y)
{
SetCursorPos(x, y);
mouse_event((int)(MouseEventFlags.LEFTDOWN), x, y, 0, 0);
}
public static void LeftUp(int x, int y)
{
SetCursorPos(x, y);
mouse_event((int)(MouseEventFlags.LEFTUP), x, y, 0, 0);
}
}
public partial class Service1 : ServiceBase
{
private HCIDevice hcidev;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
hcidev = new HCIDevice();
hcidev.init();
}
protected override void OnStop()
{
hcidev.terminate();
}
private void onElapsedTimer(object source, ElapsedEventArgs e)
{
}
}
public class HCIDevice
{
private SerialPort _sPort;
private Thread _reader;
private bool _connected;
private bool _stop;
private System.Timers.Timer _contimer = new System.Timers.Timer();
public HCIDevice()
{
// create a new serial port
_sPort = new SerialPort();
// update settings
updateSerialPortSettings();
// set reader and writer threads.
_reader = new Thread(this._readerthread);
// reset flags.
_connected = false;
_stop = false;
// start the thread.
//init();
}
public void connect()
{
if (this._sPort.IsOpen == false)
{
try
{
this._sPort.Open();
_connected = true;
AddToFile("Device connected!\n");
}
catch
{
}
}
}
public void disconnect()
{
if (this._sPort.IsOpen)
{
this._sPort.Close();
}
_connected = false;
AddToFile("Device disconnected!\n");
}
public void init()
{
try
{
this._stop = false;
// start thread.
this._reader.Start();
}
catch
{
//AddToFile("Service could not be started!\n");
}
}
public void terminate()
{
// first stop.
this._stop = true;
this._reader.Join();
//AddToFile("Device stopped!");
}
public bool isRunning()
{
return (this._stop == false);
}
public bool isConnected()
{
return this._sPort.IsOpen;
}
private void updateSerialPortSettings()
{
// Allow the user to set the appropriate properties.
this._sPort.PortName = System.Configuration.ConfigurationManager.AppSettings["devCOM"];
this._sPort.BaudRate = int.Parse(System.Configuration.ConfigurationManager.AppSettings["devbaudrate"]);
this._sPort.Parity = Parity.None;
this._sPort.DataBits = 8;
this._sPort.StopBits = StopBits.One;
this._sPort.Handshake = Handshake.None;
// Set the read/write timeouts
this._sPort.ReadTimeout = 500;
this._sPort.WriteTimeout = 500;
}
private void _readerthread()
{
byte[] _rxdata = new byte[1024];
//int n;
double nanosecPerTick = 1.0 / Stopwatch.Frequency;
Stopwatch stp_watch = new Stopwatch();
stp_watch.Start();
AddToFile("Service started!\n");
while (_stop == false)
{
// make sure the device is still connected.
if (isConnected())
{
// Do nothing if in pause
// Not paused read data and parse
try
{
handle_message();
}
catch (System.TimeoutException) { }
}
else
{
// Just got disconnected?
if (_connected)
{
disconnect();
// Reset timer.
stp_watch.Reset();
stp_watch.Start();
}
else
{
// try to connect every one second.
if (stp_watch.ElapsedMilliseconds >= 1000)
{
connect();
// Reset timer.
stp_watch.Reset();
if (_connected == false)
{
stp_watch.Start();
}
}
}
}
}
disconnect();
AddToFile("Service stopped!\n");
}
private void AddToFile(string line)
{
using (FileStream fs = File.Open(System.Configuration.ConfigurationManager.AppSettings["logloc"], FileMode.Append, FileAccess.Write, FileShare.None))
{
Byte[] info = new UTF8Encoding(true).GetBytes(string.Format("{0} {1}", DateTime.Now.ToString("dd-MMM-yyyy HH:mm:ss"), line));
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
}
private void handle_message()
{
// read line.
string msg = _sPort.ReadLine();
AddToFile(msg);
Point pt = new Point();
MyMouseClass.GetCursorPos(ref pt);
AddToFile(string.Format("{0}, {1}", pt.X, pt.Y));
}
}
}
What am I doing wrong? I googled for a couple of hours to find the solution, but could not find anything about this.
I am developing a multithreaded application in C#, and I have now come to point where I have realised that my threads sometimes throw errors when I stop them via the .Abort(); .Join(); methods.
My current code for starting and stopping the thread is as follows:
public void StartLogging()
{
if (poller != null && poller.IsAlive)
{
poller.Abort();
poller.Join();
}
poller = new Thread(new ThreadStart(PollUSBDevice));
poller.IsBackground = true;
poller.Name = reference.VendorId.ToString() + ":" + reference.ProductId.ToString();
poller.Start();
IsLogging = true;
}
public void StopLogging()
{
if (poller != null && poller.IsAlive)
{
poller.Abort();
poller.Join();
IsLogging = false;
}
}
private void PollUSBDevice()
{
...Removed code - executes within milliseconds and I am not worried about stopping here.
ErrorCode ec = ErrorCode.None;
### THIS LOOPS FOR EVER OR UNTIL I CALL .Abort() ###
while (ec == ErrorCode.None && MyUsbDevice.IsOpen)
{
if (poller.ThreadState == System.Threading.ThreadState.AbortRequested)
{
reader.Abort();
reader.Dispose();
break;
}
else
{
byte[] readBuffer = new byte[8];
int bytesRead;
ec = reader.Read(readBuffer, 100, out bytesRead);
Application.Current.Dispatcher.BeginInvoke(
new OneArgDelegate(HandleData),
new object[] { readBuffer });
}
}
}
catch (Exception ex)
{
Do stuff....
}
finally
{
Close devices that are running in above while statement
}
}
I have tried other methods post here on Stackoverflow, however I just can't get my head around them (I'm newish to multithreading). Preferably, there would just be a bool switch on my parent object reference that I could check. IE:
public class Reference
{
public static bool gracefulStopRequested = false;
}
public void PollUSBDevice
{
while (ec == ErrorCode.None && !reference.gracefulStopRequested)
{
....
}
}
Can anyone point me to a good resource or give me a hint as to what search terms I should be searching for, or if you are in a really giving mood, possibly do a mockup of how you would handle this problem?
I would go for something like:
class Program
{
static void Main(string[] args)
{
Thread poller = new Thread(new ThreadStart(PollUSBDevice));
poller.Start();
Console.ReadLine();
StopPoller();
Console.WriteLine("Stopped");
Console.ReadLine();
}
public static void StopPoller()
{
_PollerStopRequested = true;
}
private static bool _PollerStopRequested = false;
private static void PollUSBDevice()
{
while (true && !_PollerStopRequested)
{
Console.WriteLine("running");
Thread.Sleep(500);
}
}
}
However this is just simulating a build in feature of C# BackgroundWorker, so you could also have a look at: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
My question is all about URL Protocols.
I have registered a URL Protocol called mcm, but I noticed that everytime I run it from any web browser, t creates a new instance of the application. Is there any way to handle the protocol request in an already running instance?
For example, when uTorrent is using the torrent protocol It handles the request immediately without running the app again. I couldn't really find anything interesting about it, so I am asking here...
Here is the code I use to register the protocol:
private static void RegisterUrlProtocol()
{
UnregisterUrlProtocol();
RegistryKey rKey = Registry.ClassesRoot.OpenSubKey(UrlProtocol, true);
if (rKey == null)
{
rKey = Registry.ClassesRoot.CreateSubKey(UrlProtocol);
rKey.SetValue("", "URL: MazCraft Protocol");
rKey.SetValue("URL Protocol", "");
rKey = rKey.CreateSubKey(#"shell\open\command");
rKey.SetValue("", "\"" + Application.ExecutablePath + "\" %1");
}
if (rKey != null)
{
rKey.Close();
}
}
And the code to read the arguments:
private static bool CheckForProtocolMessage()
{
string[] arguments = Environment.GetCommandLineArgs();
if (arguments.Length > 1)
{
string[] args = arguments[1].Split(':');
args[1] = args[1].Replace("//", "");
if (args[0].Trim().ToUpper() == "MCM" && args.Length > 1)
{
string[] actionDetail = args[1].Split('=');
if (actionDetail[0].Trim().ToUpper() == "INSTALL" && actionDetail.Length > 1)
{
string id = actionDetail[1].Trim().Replace("/", "");
Funcs.ID = id;
return true;
}
}
}
return false;
}
Any help would be greatly appreciated :)
Greetings.
You could use a Mutex to detect an instance of the application that is already running and send the data over to the existing instance via Named Pipes.
Hope the following example helps.
you can swap out the named pipes object (in this case string) for whatever serializable object you like.
NamedPipe.cs
namespace SingleInstanceNP
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.IO;
public class NamedPipe<T> : IDisposable
{
#region Attribute and Properties
private string _pipeName;
private NamedPipeServerStream _pipeServer;
private bool _disposed;
private Thread _thread;
private bool _started;
#endregion
#region Constructors
public NamedPipe(NameTypes pipeType)
{
_disposed = false;
_started = false;
_pipeName = pipeType.ToString();
_thread = new Thread(Main);
_thread.SetApartmentState(ApartmentState.STA);
_thread.Name = "NamePipe: " + pipeType.ToString() + " Thread";
_thread.IsBackground = true;
}
~NamedPipe()
{
Dispose();
}
#endregion
#region Events
public delegate void Request(T t);
public event Request OnRequest;
#endregion
#region Public Methods
public static void Send(NameTypes pipeType, T t)
{
using (var npc = new NamedPipeClientStream(".", pipeType.ToString(), PipeDirection.Out))
{
var bf = new BinaryFormatter();
npc.Connect();
bf.Serialize(npc, t);
}
}
public static T Recieve(NameTypes pipeType)
{
using (var nps = new NamedPipeServerStream(pipeType.ToString(), PipeDirection.In))
{
return Recieve(nps);
}
}
public void Start()
{
if (!_disposed && !_started)
{
_started = true;
_thread.Start();
}
}
public void Stop()
{
_started = false;
if (_pipeServer != null)
{
_pipeServer.Close();
// disposing will occur on thread
}
}
public void Dispose()
{
_disposed = true;
Stop();
if (OnRequest != null)
OnRequest = null;
}
#endregion
private void Main()
{
while (_started && !_disposed)
{
try
{
using (_pipeServer = new NamedPipeServerStream(_pipeName))
{
T t = Recieve(_pipeServer);
if (OnRequest != null && _started)
OnRequest(t);
}
}
catch (ThreadAbortException)
{ }
catch (System.IO.IOException iox)
{
Console.WriteLine("ERROR: {0}", iox.Message);
Thread.Sleep(TimeSpan.FromSeconds(30));
}
catch (Exception ex)
{
Console.WriteLine("ERROR: {0}", ex.Message);
return;
}
}
}
private static T Recieve(NamedPipeServerStream nps)
{
var bf = new BinaryFormatter();
try
{
nps.WaitForConnection();
var obj = bf.Deserialize(nps);
if (obj is T)
return (T)obj;
}
// Catch the IOException that is raised if the pipe is
// broken or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
return default(T);
}
#region Enums
public enum NameTypes
{
PipeType1
}
#endregion
}
}
Program.cs
Please give credit for the APP GUID to What is a good pattern for using a Global Mutex in C#?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
namespace SingleInstanceNP
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
using (var mutex = new Mutex(false, mutexId))
{
try
{
if (!mutex.WaitOne(0, false))
{
//signal existing app via named pipes
NamedPipe<string>.Send(NamedPipe<string>.NameTypes.PipeType1, "test");
Environment.Exit(0);
}
else
{
// handle protocol with this instance
Application.Run(new Form1());
}
}
finally
{
mutex.ReleaseMutex();
}
}
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SingleInstanceNP
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// start listening for named pipe connections
var namedPipeString = new NamedPipe<string>(NamedPipe<string>.NameTypes.PipeType1);
namedPipeString.OnRequest += new NamedPipe<string>.Request(namedPipeString_OnRequest);
namedPipeString.Start();
}
void namedPipeString_OnRequest(string t)
{
MessageBox.Show(t);
}
}
}
I created this simple sample Form with the close button.
Everything is working as expected when NOT using the Interop.WMPLib.dll
I've seen other applications using this without problems but why isn't the Form process closed when I just add the line:
SoundPlayer myPlayer = new SoundPlayer();
and of course dispose it:
if (myPlayer != null)
{
myPlayer.Dispose();
myPlayer = null;
}
The Form closes but the debugger VS2008 is still active. The Form project and the dll are still active.
If you send me an email to xdasleepsense#gmail.com, I can send you the zipped project.
Below is the class for the dll:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using WMPLib;
namespace WindowsMobile.Utilities
{
public delegate void SoundPlayerStateChanged(SoundPlayer sender, SoundPlayerState newState);
public enum SoundPlayerState
{
Stopped,
Playing,
Paused,
}
public class SoundPlayer : IDisposable
{
[DllImport("coredll")]
public extern static int waveOutSetVolume(int hwo, uint dwVolume);
[DllImport("coredll")]
public extern static int waveOutGetVolume(int hwo, out uint dwVolume);
WindowsMediaPlayer myPlayer = new WindowsMediaPlayer();
public SoundPlayer()
{
myPlayer.uiMode = "invisible";
myPlayer.settings.volume = 100;
}
string mySoundLocation = string.Empty;
public string SoundLocation
{
get { return mySoundLocation; }
set { mySoundLocation = value; }
}
public void Pause()
{
myPlayer.controls.pause();
}
public void PlayLooping()
{
Stop();
myPlayer.URL = mySoundLocation;
myPlayer.settings.setMode("loop", true);
}
public int Volume
{
get { return myPlayer.settings.volume; }
set { myPlayer.settings.volume = value; }
}
public void Play()
{
Stop();
myPlayer.URL = mySoundLocation;
myPlayer.controls.play();
}
public void Stop()
{
myPlayer.controls.stop();
myPlayer.close();
}
#region IDisposable Members
public void Dispose()
{
try
{
Stop();
}
catch (Exception)
{
}
// need this otherwise the process won't exit?!
try
{
int ret = Marshal.FinalReleaseComObject(myPlayer);
}
catch (Exception)
{
}
myPlayer = null;
GC.Collect();
}
#endregion
}
}
A MessageBox or Below solved it. Thx.
public void Dispose()
{
try
{
Stop();
}
catch (Exception)
{
}
// need this otherwise the process won't exit?!
try
{
int ret = Marshal.FinalReleaseComObject(myPlayer);
}
catch (Exception)
{
}
myPlayer = null;
GC.Collect();
//If you don't do this, it will not quit
//http://www.eggheadcafe.com/software/aspnet/31363254/media-player-freezing-app.aspx
for (int s = 0; s < 100; s++)
{
Application.DoEvents();
Thread.Sleep(1);
}
GC.WaitForPendingFinalizers();
//MessageBox.Show("Application Exiting");
}
I just found from this link: http://software.itags.org/pocketpc-developer/163455/
a hint...
So I added a messagebox:
public void Dispose()
{
try
{
Stop();
}
catch (Exception)
{
}
// need this otherwise the process won't exit?!
try
{
int ret = Marshal.FinalReleaseComObject(myPlayer);
}
catch (Exception)
{
}
myPlayer = null;
GC.Collect();
**MessageBox.Show("Application Exiting");**
}
once I click OK, the debugger also thinks it's finished. Of course I can't have the user click OK.
So what's happening here?