|
Well, this is awkward. For testing, I commented the ShowWithoutActivation -override. As expected, it did activate then. Then I removed the comments and it still activates. I don't know what's happening there.
However, I have another solution for you:
1) Go to this article and download the code: All Three Features of Single-Instance Applications at One Shot, .NET[^]
2) Copy the files IRemoteFileLoader.cs , Server.cs and SingleInstanceManager.cs to your project folder.
3) Include them into your project (Project > Add existing item)
4) Add a reference to the System.Runtime.Remoting -assembly to your project (Project > Add reference).
5) Replace the code of your Program.cs by this:
using System;
using System.Windows.Forms;
using SA.Universal.SingleInstance;
namespace FileArranger
{
internal sealed class Program
{
[STAThread]
static void Main(string[] commandLine)
{
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
} 6) Add a using-Directive to your MainForm.cs :
using SA.Universal.SingleInstance; 7) Add this to the constructor of MainForm :
SingleInstanceManager.FilesLoading += (sender, eventArgs) => {
Invoke(new System.Action(() => ReadArguments(eventArgs.CommandLine)));
};
That should do it.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
modified 22-Oct-17 9:04am.
|
|
|
|
|
Finally, the form starts without focus. But it doesn't get the second round of parameters. As I said, this app should get the parameters twice: once when it's started, and once when it's started again after the user selects the target file and pass that as a parameter. Basically the second instance should pass the parameters to the first one.
I suspect the issue is here:
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
The second instance should pass the command line arguments to the first instance. But how to do that?
modified 22-Oct-17 9:31am.
|
|
|
|
|
It does for me. Do you know how to use the debugger? Place a breakpoint on the opening brace of ReadArguments(..) (F9). Start your app in debugging mode (F5). Then do that XnView thing. The debugger should stop the execution of your app at the beginning of ReadArguments(..) . Then step through the execution line by line (F10 step over, F11 step into) and inspect the values of the variables and what your code is doing - and how that differs from your expectation of what it should be doing.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
alin1 wrote: I suspect the issue is here:
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
The second instance should pass the command line arguments to the first instance. But how to do that?
Nope, that's being taken care of by the code in the files you included into your project. That's all fine. See my other reply.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Now the issue was in the ReadArguments function: previously args[0] was the app's path (got through Environment.GetCommandLineArgs()), but now it's not. I modified ReadArguments a little and the app finally works as intended. Thanks a lot for help!
|
|
|
|
|
You're welcome
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Be sure to leave an upvote on the article where you downloaded the code from, too
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
|
Member 12536513 wrote: Any ideas how to make it work? It shouldn't work.
If you are launching an application that does not put up a UI (ie, needs no immediate user-interaction), then you do not need to display a form. Display something in the System-Tray, and show a form only once you're ready to interact with the user.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
This is a particular case where I want to have a form in background. The app has just a listbox with the files selected by the user and s/he could see the list just in case. Also this app works in 2 stages: when it's started it waits in background and when it's started again it does the processing and it closes automatically. So, it's important to have that form starting in background.
|
|
|
|
|
That doesn't make any sense.
What do you mean by "has just a listbox with the files selected by the user"? How is this functionality supposed to work? Where does this list of files come from and how does the listbox get populated? How is this app supposed to know how and when "files are selected by the user"?
How does the "when it's started again it does the processing" supposed to work?
This is not intuitive and goes against probably half of the UI and app design guidelines.
|
|
|
|
|
"Where does this list of files come from and how does the listbox get populated?"
The list is populated with the file names selected by the user in another app (XnView). These file names (complete paths) are passed as arguments.
"How is this app supposed to know how and when "files are selected by the user"?"
When it's launched it read the parameters. If there are parameters it means those are the files selected by the user.
"What do you mean by "has just a listbox with the files selected by the user"?"
The purpose of this app is renaming files in 2 stages: first the source (which files to rename) then the destination (after which file). Because it does its job in background in 2 stages, the user needs a visual prompt to know that he selected the files (that he finished the stage 1). Also if he forgets which files he selected in the first place, he could read the list. That's why I didn't hide the MainForm.
|
|
|
|
|
|
I suggest you edit your original post to include all the details you have now revealed with your replies, here, to the other posts.
If your WinForm app depends on getting a list of file-paths from an external app that launches it, then, why not pass the paths as parameters when the external app starts the WinForm app?
Check out the 'SetForegroundWindow and 'SetActiveWindow API's for restoring focus to the external app after you launch the WinForm app: [^].
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it. A few hundred years later another traveler despairing as myself, may mourn the disappearance of what I may have seen, but failed to see.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|
|
"If your WinForm app depends on getting a list of file-paths from an external app that launches it, then, why not pass the paths as parameters when the external app starts the WinForm app?"
This is what I actually did. The problem is that I need to pass 2 rounds of parameters: 1. the app is started, it gets some parameters and it waits; 2. the app is started again, it gets other parameters, do its job and it quits. Because it doesn't quit after the first stage, I had to use SingleInstanceController to avoid multiple instances. That messed up the ShowWithoutActivation.
|
|
|
|
|
Quote: The problem is that I need to pass 2 rounds of parameters
OK, so how was this determination made? What is the process or problem that dictated it?
I seriously doubt this is good solution to the problem.
|
|
|
|
|
That is suitable to my app's workflow. I tested it and it's well intergated with XnView, except that focus issue.
The app's workflow is like this:
- the user wants to arrange some pictures in XnView using the Browser view (rows of thumbnails);
- the user selects the pictures he wants to be arranged (source) and the starts this app;
- the app starts minimized in background, so the user knows that the app got the selected pictures as parameters; he could read the list too in case he forgets which pictures he selected;
- the user selects the target picture (destination) and starts this app again;
- the app gets the target picture as a paramater and does the renaming automatically; after that, the app is closed automatically.
- repeat the process until the pictures are in the expected order.
It feels like a lot of work but it's very fast: just 2 selections and 2 key combos to start the app. Here's a video with this app in action (notice how the files are automatically renamed after the user is doing the selections and starting the app): FileArranger - Streamable[^]
I never seen an app like this before, so I just made one. Instead doing manual renaming to arrange the files, I just let the app to do the heavylifiting.
|
|
|
|
|
Yeah...
Here's the problem with what you're doing. What if the user selects enough images to overwhelm the length of the command line? The limit is 8191 characters.
How does the list of filepaths get assembled into a command line in this XnView software? Is this a plugin to the software or what?
How does your app code make the determination that it's receiving the paths to the first set of images or the rename pattern?
How does the user add more images to the list after the first set of images?
Your "process", as you've described it, doesn't appear to be well thought out.
|
|
|
|
|
"Here's the problem with what you're doing. What if the user selects enough images to overwhelm the length of the command line? The limit is 8191 characters."
There's a hidden setting OpenWithMax that should be added to the [Start] section of xnview.ini. But there's another version of XnView called XnView MP that doesn't have these limitations.
|
|
|
|
|
The limitation is no in XnView, but with the maximum length of the command line you're using to start your external app, 8191 characters.
No, I'm not testing your app.
|
|
|
|
|
I have a problem writing lazy init in a (humanly lazy) fashion.
1st, I am not wondering about and using Lazy<T> for that discussion. This is not the topic at hand...
The topic here is which of the following 2 hand crafted lazy initialisation is the most... "satisfying" (a completely subjective topic, I should warn you)
method 1
public object Property
{
get
{
if (field == null)
field = Create();
return field;
}
}
object field;
object Create() { return new object(); }
method 2
public object Property
{
get
{
field = field ?? Create();
return field;
}
}
object field;
object Create() { return new object(); }
the difference is that in method1 I use an if statement, which is more cumbersome to write, but in method2 I use the (C# 6) null coalesce operator, this is more succinct, but it looks like I might do pointless assignment.
[EDIT] a coworker came up with my favourite answer
public object Property
{
get
{
return field ?? (field = Create());
}
}
object field;
object Create() { return new object(); }
modified 19-Oct-17 20:44pm.
|
|
|
|
|
|
None. Your methods are not thread-safe.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
I wondered for at least 2 minutes whether I should bother reply to that stupid comment.
I decided to.
Yes, thread safety in that minimalist sample would distract the viewer from my problem. So the lack of it is a feature, not a bug.
modified 20-Oct-17 3:59am.
|
|
|
|
|
Super Lloyd wrote: it is a feature, not a bug.
Ah! It's in the manual!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|