How does Windows know which application to use to open a file when you double click the file in file Explorer?

Hi @ITGeek82-8422

Thank you for your question and reaching out.

I can understand File and Folders gets opened up without double click. This issue happens, if there is mouse/hardware issue or it can be settings issue. I would suggest you to follow the troubleshooting steps as mentioned below:

Method 1: Restore the defaults.

Press Windows and E key together on your Keyboard.
Go to View tab on the top.
Click on Options > Click on Change folder and Search options.
Click on Restore Defaults. Click on Apply and Ok.

Method 2: Change the Mouse settings.

Click on the Settings icon from the start menu.
Click on Devices > Click on Mouse from the left panel of the window.
Click on Additional Mouse Options from the right pane.
This will open the Mouse Properties window, go to Buttons tab.
Under the Double click speed, drag the slider to the left to slow down the double click speed.
Next, go to Pointer options tab, uncheck the "Enhance Pointer Precision" to disable pointer precision to slow down the mouse.

Also , please Download below tool diagnostic and repair too from Microsoft which will fix Folders and some customization related settings.

https://support.microsoft.com/en-us/windows/automatically-diagnose-and-repair-windows-file-and-folder-problems-8ca2261a-eb4b-bda1-232c-d88b972438cd

Hope this answers your question :)
Thank you.
Prakash

--If the reply is helpful, please Upvote and Accept as answer--

  • Download source code - 34.8 KB

Introduction

When you double click a related file in Explorer, your app opens - but what if you want to open another file in the same instance of the app? If you double click another file in explorer, Windows will open a second instance ...

So you need to talk to the first instance and tell it the new file and let it handle it.

That's not too complex - but does involve Sockets - so I created the DynamicArgument class to do all the heavy lifting.

Background

Just an aside: If you want to open a specific file extension with your app in Windows 10 and the app doesn't self register to open them, then you can do it from within Explorer. I find this handy during dev, since I don't install my apps (when the registration of file extensions is normally done) on my dev machine as I want to run the debug version most of the time.

To do it:

Open Windows Explorer, and navigate to an example of your input file: (blahblah.myfiletype for example).

Right click the file, and select Properties:

How does Windows know which application to use to open a file when you double click the file in file Explorer?

Click "Change" and "More apps":

How does Windows know which application to use to open a file when you double click the file in file Explorer?

Scroll to the bottom of the list, make sure "Always use this app to open ..." is checked, and select "Look for another app on this PC":

How does Windows know which application to use to open a file when you double click the file in file Explorer?

Browse to the EXE file for your app, and click "OK".

Now when you double click, Windows will open the right app for you.

Using the Code

When you double click a file, Windows opens the associated app, and passes the full file path as a command line argument. This works fine, and has done for many, many versions of Windows - right back to V1.0, I believe.

And it's easy to get that argument - there are two ways.

For a Console app, they are passed as a string array to the Main method.

For a GUI app, they are available as an array of strings via the Environment.GetCommandLineArgs method.

The problem is ... once your app is running, that collection can't be added to, or changed in any way - Windows can't add "new arguments" to the array, and has no way to "alert you" that a new one is available.

So ... what you have to do is:

  1. Check if an instance of your app is running already.
  2. If it isn't, then handle the argument yourself, and prepare yourself in case the user tries to open another.
  3. If it is, pass the argument to the other instance, and close yourself down.

And that means Sockets and TCP stuff. Yeuch.

So, I created a DynamicArgument class that handles it all for you, and provides an event to tell you there is an argument available. It processes the arguments and raises an event for each in turn. If it's the first instance, it creates a thread which creates a Listener, and waits for information from other instances. When it gets a new argument, it raises an event for it.

To use it is pretty simple.

Add to your main form Load event:

private void FrmMain_Load(object sender, FormClosingEventArgs e)
    {
    DynamicArgument da = DynamicArgument.Create();
    da.ArgumentReceived += Application_ArgumentReceived;
    bool result = da.Start();
    if (!result) Close();
    }

DynamicArgument is a Singleton class, because it would get difficult and messy if there were two Listeners trying to process arguments - so you have to Create it rather than use the new constructor.

Add your event handler, and Start the system. If this was the only instance running it returns true - if not, then probably you want to close the form, which will close the app - I'd do it for you, but this way, you get the option to clean up after yourself if you need to, which Application.Exit doesn't let you do.

Then just handle your event and do what you need to with the arguments:

private void Application_ArgumentReceived(object sender, DynamicArgument.DynamicArgmentEventArgs e)
    {
    tbData.Text += "\r\n" + e.Argument;
    }

How It All Works

There are three files in the system, two of them are "support files": ByteArrayBuilder.cs and IByteArrayBuildable.cs.

These two are part of the "packaging the data up" system and are described in a different Tip: ByteArrayBuilder - a StringBuilder for Bytes - yes, I could have used JSON or XML, but I prefer JSON and forcing you to add a reference and a large nuget package to your app didn't really appeal. Free free to change it if JSON or XML is a better fit for you.

The other file is the class that does the work: DynamicArguments.cs

When you construct the one-and-only instance of the class, it reads the arguments and stores them in a queue.

You then call Start when you are ready to process them, which does one of two things.

  1. If this is the only instance, it starts a Listener thread, and generates the Events to tell your app there is data.
  2. Otherwise, it opens a Client and sends the other instance all its arguments.

Listener Thread

OK, there are fine details going on here ... so either skip this section and assume it "works by magic" or read on.

A Listener Socket is a blocking object: it stops your thread doing anything else until a client makes contact. That's fine, and exactly what you would expect, except it's also a pain since you're on the UI thread and that stops your whole system doing anything.

So you have to put the Listener onto a different thread, which brings up three problems:

  1. You have to be sure to Invoke back onto the UI thread to raise our "argument available" Event or the user has to do that - which means most won't bother and will complain when they get a cross-thread exception.
  2. It's a blocking object - so the new thread will be blocked, which means it can't be stopped "nicely" and has to be terminated properly by killing it or the app won't end and the domain won't get unloaded.
  3. If the main app crashes, or the coder is not paying attention, the DynamicArgument object won't get closed properly, and that means the Listener thread will keep running.

The way to avoid these is to use a BackgroundWorker - it automatically does all the Invoke work for you when it raises the ProgressChanged event, and it automatically creates the new thread as a Background thread, rather than Foreground - and that's an important difference.

Background threads are "don't care" threads: the system will kill them automatically when the app closes, unlike Foreground threads which run until they themselves exit. Yes, we could use a Thread or Task object directly, and set it as a Background thread and also handle the Invoke - but there is a class that does both for me, so I'll simplify my code and use that!

So ... you call Start on the DynamicArgument object and it creates a background thread and then passes the arguments as Events:

BackgroundWorker listener = new BackgroundWorker();
listener.DoWork += Listener_DoWork;
listener.WorkerReportsProgress = true;
listener.ProgressChanged += Listener_NewArguments;
listener.RunWorkerAsync();
PassUpAllArguments();
return true;

The BackgroundWorker sets up the Socket and waits for instructions:

IPAddress ipAddress = GetMyIP();
while (true)
    {
    TcpListener listener = new TcpListener(ipAddress, ListenPort);
    listener.Start();
    while (true)
        {
        try
            {
            using (Socket s = listener.AcceptSocket())
                {
                ...
                }
            }
        catch (Exception ex)
            {
            Debug.WriteLine(ex.ToString());
            }
        }
    }
}

When it gets a connection, it reads it, converts it to separate arguments and enqueues them before using ReportProgress to pass them to the external app:

int length = s.Available;
if (length > 0)
    {
    byte[] buffer = new byte[length];
    s.Receive(buffer);
    
    
    
    
    ByteArrayBuilder bab = new ByteArrayBuilder(buffer);
    byte cc = bab.GetByte();
    if (cc == CC_Argument)
        {
        int argCount = bab.GetInt();
        for (int i = 0; i < argCount; i++)
            {
            string arg = bab.GetString();
            Arguments.Enqueue(arg);
            }
        work.ReportProgress(0, 0);
        }
    }

Since the ReportProgress handled is already Invoked back to the UI thread, the arguments can just be enqueued, and passed up via a series of Events:

private void Listener_NewArguments(object sender, ProgressChangedEventArgs e)
    {
    if (e.UserState is string arg)
        {
        Arguments.Enqueue(arg);
        }
    PassUpAllArguments();
    }
public event EventHandler<DynamicArgmentEventArgs> ArgumentReceived;
protected virtual void OnArgumentReceived(DynamicArgmentEventArgs e)
    {
    ArgumentReceived?.Invoke(this, e);
    }
private void PassUpAllArguments()
    {
    while (Arguments.Count > 0)
        {
        OnArgumentReceived(new DynamicArgmentEventArgs() { Argument = Arguments.Dequeue() });
        }
    }

Not that complicated really, it just seems like it when you are trying to debug this stuff...

Send and Forget

When this isn't the only instance, we need to do the reverse: build up a block of data, and open a Client to send it. That's a lot simpler; everything stays in the Start method:

SendTime();
String strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
IPAddress ipAddress = ipEntry.AddressList[0];
using (TcpClient tc = new TcpClient(strHostName, ListenPort))
    {
    NetworkStream stream = tc.GetStream();
    ByteArrayBuilder bab = new ByteArrayBuilder();
    bab.Append(CC_Argument);
    int argCount = Arguments.Count;
    bab.Append(argCount);
    while (argCount-- > 0)
        {
        bab.Append(Arguments.Dequeue());
        }
    byte[] data = bab.ToArray();
    stream.Write(data, 0, data.Length);
    stream.Flush();
    }
return false;

SendTime is annoying: for some reason, the first message sent from any TcpClient I build is thrown away. I'm guessing it's a timing issue or Windows hates me - if you know why, let me know...

private void SendTime()
    {
    String strHostName = Dns.GetHostName();
    using (TcpClient tc = new TcpClient(strHostName, ListenPort))
        {
        NetworkStream stream = tc.GetStream();
        ByteArrayBuilder bab = new ByteArrayBuilder();
        bab.Append(CC_TimeStamp);
        bab.Append(1);
        bab.Append(DateTime.Now.ToString("hh:mm:ss"));
        byte[] data = bab.ToArray();
        stream.Write(data, 0, data.Length);
        }
    }

Once we have a connection, we just bundle the data up (here, you could use JSON or XML if you wanted) and send it to the Listener app.

And that's it: all done!

History

  • 2019-10-02: Download changed to remove "magic numbers": The port number in use was fixed at "51191" instead of using the ListenPort property. The code samples in the text have been revised to match. Sorry about that... :blush:
  • 2019-10-02: Typos fixed
  • 2019-10-01: First version

Born at an early age, he grew older. At the same time, his hair grew longer, and was tied up behind his head.
Has problems spelling the word "the".
Invented the portable cat-flap.
Currently, has not died yet. Or has he?

How do I make a file open with a specific program?

On the Start menu, select Settings > Apps > Default apps..
Select which default you want to set, and then choose the app. You can also get new apps in Microsoft Store. ... .
You may want your . pdf files, or email, or music to automatically open using an app other than the one provided by Microsoft..

How can you eliminate the possibility that an application error is caused by another application or service running in the background?

How can you eliminate the possibility that an application error is caused by another application or service running in the background? Run the application after starting the system in Safe Mode.

What is the name of the program in Windows that will allow you to manage files and folders?

You can view and organize files and folders using a built-in application known as File Explorer (called Windows Explorer in Windows 7 and earlier versions). To open File Explorer, click the File Explorer icon on the taskbar, or double-click any folder on your desktop.

How do I make a program open another program?

From the desktop, right-click the desired file, select Open with, and click Choose another app from the menu that appears. Select the desired application.