File Association problems in C#

Schadenfroh

Elite Member
Mar 8, 2003
38,416
4
0
(I am using Visual Studio 2005 and I basically copy / pasted this from an email I sent one of my professors, so I might have references to his class in there)

I have decided to start learning C# (I am best at VB.NET, although I have only been programming for a little under a year now, so I am still very novice). The way that I learn best is by a pet project. When I wanted to learn VB.NET better, I started working on a "bolt on" application that provides a GUI and extends the features of the sourceforge project StreamRipper, the result of my VB.NET learning experience can bee seen here:
http://streamripper.sourceforg...2/viewtopic.php?t=2683

I am currently "porting" it to C# in order to learn the language. But, I was hoping that you could help me solve a problem that has lingered in my VB.NET program and thus in my new C# port.
The problem lies after I read-in SRGUI.NET associated PLS files that are opened after downloading them from shoutcast.com.

It currently "processes" the information in the .pls files by using a background worker that monitors a predetermined folder that users download these files to. The background worker detects the new file and once it is downloaded, SRGUI.NET processes it, then sends the info from it to the proper functions and then it deletes the file. The problem with this method is obvious in that one has an extra background worker and it adds extra effort to the user who has to download the file to a predetermined folder in order to have it analyzed. It would be much easier for the user to just "open with SRGUI.NET" when they download the file.

Associating the file with my program is not a problem, I have also discovered how to properly read in this file if the program is not running already. The problem arises when the program is already running. I rigged the VB.NET program's 2nd instance to copy the pls file over to the folder being monitored (basically producing the same result as the user downloading it to that folder), but that never did work correctly.

In my C# port, I am planning on having the program associated with the file and use a better method than stated above to analyze the new files that are opened with my program. In the C# port, I have already figured out how to determine if an instance is already running and act accordingly and how to analyze a file if the program is not running yet. Currently, I am reading in the file into a System.IO.StreamReader within the 2nd instance and then saving it to a string variable, but I do not know how to send this information between the two instances of the same program. I would prefer not to use the method described above in the VB.NET program for this C# program.

(By the way, the code that I am using currently in C# is based on this guy's post, but I added code to determine if the program is already running or not, see my attached code below):
http://www.csharpfriends.com/F...Post.aspx?PostID=32627

Cliff Notes:
  • Can you think of any way that I could go about sending a string variable between two different running instances of the same program and then triggering an event to send the newly arrived string to a function in the original program instance in C#?
Thanks
 

EagleKeeper

Discussion Club Moderator<br>Elite Member
Staff member
Oct 30, 2000
42,589
5
0
Options that have been used in different projects:

Allocate Global Memory Object and pass the handle to the other instance via a PostMessage

Memory Mapped File

COM object

 

imported_Dhaval00

Senior member
Jul 23, 2004
573
0
0
I am somewhat confused by what your problem exactly is. Maybe you can provide a "simpler" explanation?

Are you using the FileSystemWatcher (If not, you may want to look into it)? Moreover, if you always want "something to keep listening," I would say create a Windows Service that'll spawn a new background thread - instantiate a FileSystemWatcher in this background thread to monitor a user configurable folder, and you should be good to go. I am completely ignoring the GUI for the sake of this discussion. Now as far as marshalling values is concerned, you want a "middle-ware" that'll act as a negotiator between your 2 instances. Again, the easiest solution seems a Windows Service that is always listenting to something (we can discuss this further, if you confirm that I understand your problem correctly).

Again, the above, rudimentary solution is based on my understanding of your problem. If this doesn't seem to fit your needs, then my bad :)
 

Schadenfroh

Elite Member
Mar 8, 2003
38,416
4
0
Originally posted by: Dhaval00
I am somewhat confused by what your problem exactly is. Maybe you can provide a "simpler" explanation?

Think of the program that I am working on as a web browser (for sake of an analogy) called MyBrowser.exe.

Say I have two html files on my desktop and I double click one. I now have the browser up and running displaying the first HTML file.

I then double click the 2nd file on my desktop. In my program's current state, two separate instances of the web browser would be opened displaying the two different .HTML files. (MyBrowser.exe is listed twice in task manager and they are running as two separate entities)

My objective is to have the 2nd instance detect that another instance of the program is running (which I have already figured out) and then follow another set of instructions that sends the location of the 2nd html file to the original instance of the program as a string, then trigger an event that would cause the original instance of the program to send the incoming string to a function that would display a "tab" of the 2nd html file in the original instance of the program with only one MyBrowser.exe listed in the task manager.


(unrelated to the above, I used FileSystemWatcher originally in my VB.NET program for the monitoring of the folder, but it kept stepping on itself and would crash about 15% of the time, my current background worker never crashes)
 

imported_Dhaval00

Senior member
Jul 23, 2004
573
0
0
You can still use a FileSystemWatcher (there are many things that need to be considered when using the FSW - Google "FileSystemWatcher issues") from your BackgorundWorker. One of the prominent reasons why it might have failed is because the way you're using your StreamReader (ignore this tip if you are already doing this): open the file using the FileStream API and use the overloaded constructor that allows you to set FileShare permissions; then wrap your StreamReader around this FileStream object. This way your other instances will not fight for the same "handle." For your scenario, FileShare.Read should be enough.

Here is "hack" you could try to incorporate your current logic and make the application more dependable:
1) Create a List<String> object at the class level. For the file that is being opened, add its path to this list.
2) Instantiate a BW (BackgroundWorker or a background thread) in your Form_Load (note that it is OK to have multiple background threads or BWs for each instance). Pass the current file's path to the thread as a parameter.
3) You have the option of using the FSW or your own "polling logic" at this point - I think it is easier to use an ad-hoc background thread. Either way, in your ThreadStart delegate or BW's DoWork method, keep polling a single, common folder location that will contain the string (path) you're looking for in a text file. The thread's objective is to extract all lines (each line is a new path), and check if the current path is not already in the List. You will want to use FileShare.ReadWrite for this file (moreover, create a static class object to synchronize your threads). If the list already has this file, send the background thread to sleep, else update the list as well as the file (so that other instances' threads can know of the change when they wake up), create a new UI object to reflect the change (I am assuming you know how to do this since you're already playing around with the BW), and then send the background thread to sleep.

There are other ways to "negotiate," but the above logic should direct you in the right direction: you need something sitting at the center of all your instances that will marshal data across your application boundaries (you can relate this to the Singleton, Factory pattern). I didn't give it much thought, and am sure there other, better ways to do this.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Lots of good suggestions here already. I've used memory mapped files, and named pipes, for similar tasks. Here's perhaps a simpler method, that doesn't involve centralized services: have every instance that starts up with a command line file spec look to see if another instance is already running (use FindWindow or a process-oriented call from System.Diagnostics). If it is, write the file spec to the program folder as a text file (or write it into the registry), and then post a message (using PInvoke) to the window of the previous instance telling it to look for the file or key, and then exit.

The first page of this article shows how to call PostMessage from a .Net application, and on the second is a good example of overriding WndProc to trap custom messages.

http://www.devsource.com/article2/0,1895,1961578,00.asp
 

Nothinman

Elite Member
Sep 14, 2001
30,672
0
0
If it is, write the file spec to the program folder as a text file (or write it into the registry), and then post a message (using PInvoke) to the window of the previous instance telling it to look for the file or key, and then exit.

Writing to the registry might not be a bad idea because you can use HKCU but writing to the program folder is terrible and one of the reasons that so many Windows apps require admin priviledges to run even when they shouldn't.
 

Schadenfroh

Elite Member
Mar 8, 2003
38,416
4
0
Originally posted by: Nothinman
If it is, write the file spec to the program folder as a text file (or write it into the registry), and then post a message (using PInvoke) to the window of the previous instance telling it to look for the file or key, and then exit.

Writing to the registry might not be a bad idea because you can use HKCU but writing to the program folder is terrible and one of the reasons that so many Windows apps require admin priviledges to run even when they shouldn't.

Well, I had considered writing the string to my.settings.* and have a background worker monitor for that setting to become nonblank. But, I am not sure if multiple instances share the same my.settings.* file.

I am looking over named pipes at the moment, which seems like it might do the trick.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Originally posted by: Nothinman
If it is, write the file spec to the program folder as a text file (or write it into the registry), and then post a message (using PInvoke) to the window of the previous instance telling it to look for the file or key, and then exit.

Writing to the registry might not be a bad idea because you can use HKCU but writing to the program folder is terrible and one of the reasons that so many Windows apps require admin priviledges to run even when they shouldn't.

Yeah, you're probably right about that, but you can use the technique in the My Documents tree as well, or anywhere else that you want to.
 

UCJefe

Senior member
Jan 27, 2000
302
0
0
This is a perfect application for using remoting/named mutexs. For a good .NET slick implementation see the "Multi-Instance Detection" class in the Genghis framework: Text

Essentially a named mutex is used to determine if an instance is already running and if so, remoting is used to send over any parameters to the second instance. It works pretty well.
 

Schadenfroh

Elite Member
Mar 8, 2003
38,416
4
0
and my professor's response (in case anyone is following this thread):
Well, this is not an easy thing to do. The quick and dirty way is to use a "file" to communicate information from one app to another (even if that's two separate instances of the same app). One app could open and write to a file while the other monitors it for changes. Strings could be passed around this way. Another method is to use a mutex which is a construct we can use to force mutual exclusiveness. In this case, we wish that our app be mutually exclusive in that only one instance can be running at any single time. Creating a mutex signifies that the app is running. If another instance is executed and the mutex exists, it will abort. We can, however, let the user know that another instance is already running or even toggle the main window of the running instance.; though for this we have to use complicated API calls of the Windows system DLLs. You can read more here:

http://www.codeproject.com/csh...nstanceApplication.asp

If you can associate .pls files with your app (or at least if the user can manually do this), then when a .pls file is downloaded, your app can automatically be run. Fine. Now, if another instance of the app is running, there is no easy way (that I know of) to communicate this "string" of data to the other instance unless you wish to use tcp/ip (sockets). In this case, you could have a background worker in the app that listens on some port (like 31337) for an incoming network connection. Your second instance (launched when the user specified to open the .pls with your app) can detect a previous running instance, make a connection to port 31337 and send the string of data to the other instance. That previous running instance can then receive this data and alert the main program to begin processing it. You'll need to check out System.Net.Sockets for this.

Hope this gets you started.

--
NAME REMOVED TO PROTECT THE INNOCENT, Ph.D.
School of Computing
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
Paragraph before the link == good. Paragraph after the link == not so much. As the responses here show there are all sort of mechanisms for IPC in Windows that don't require you to gin up a TCP protocol.