I'm looking to show window (WPF) that is defined in separate class library project as new separate process. Is there any way to do this?
I need to start the second project instance in new process, because there stay occupied memory when I start it by this way:
secondProject.WPFWindow win = new secondProject.WPFWindow();
win.Show();
I have ONE solution with multiple projects.
StartUp project is WPF app., output type: Windows application (exe file).
All other projects are WFP app., output type: Class library (dll file).
Now I am running "applications" (other projects in this one solution built as dll) by this code:
secondProject.WPFWindow win = new secondProject.WPFWindow();
win.Show();
I want is runnig that apps in new process... Normally I would use Process.Start(), but I can't is this case because it needs exe file as agrument and I have (and want) DLLs.
You could pass command line arguments to the main .exe to tell it which of the 'sub-apps' to run. Then the main .exe can just launch itself in a new process and tell the new instance which sub-app to run. For example in the main .exe app, put logic like this in your Application class:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
//See if exactly one command line argument was passed
if (e.Args.Length == 1)
{
//See if the argument is one of our 'known' values
switch (e.Args[0])
{
case "launchApp2":
secondProject.WPFWindow win2 = new secondProject.WPFWindow();
win2.Show();
break;
case "launchApp3":
thirdProject.OtherWPFWindow win3 = new thirdProject.OtherWPFWindow();
win3.Show();
break;
}
}
//Probably want to call the base class always?
base.OnStartup(e);
}
}
Then anytime you want to launch one of the sub-app in a new process, you can do so like this:
public void LaunchApp2()
{
//Start a new process that is ourself and pass a command line parameter to tell
// that new instance to launch App2
Process.Start(Assembly.GetEntryAssembly().Location, "launchApp2");
}
Let's assume you have a solution with 2 projects - one project compiles to an application (EXE), while the second compiles to a class library (DLL). I'll assume that the DLL has some type defined (say, a window) which you want to start from the EXE.
The simplest way to do this, is simply add a reference to the DLL. Right-click on the EXE project in the Solution Explorer, and choose Add Reference.... Wait a minute while the dialog opens. From the Projects tab, select the DLL project. Click OK.
Now, in your EXE project, WPFWindow will available as an imported type. You need to add a
using secondProject;
to the top of each code file that uses WPFWindow. I normally do this automatically using the keyboard shortcut CTRL + Period.
The method I've described is the standard method of using DLLs in C#. You can load them manually, but that is a little more complex, and likely isn't what you want to do.
Edit:
Alexei is correct. I think what we have here is an XY Problem. What you're trying to do is probably very easy, but the approach (instantiating a window defined in a DLL) is not.
Be aware that any code you run (in your case, your WPFWindow) has to originate from an application, even though the code itself is defined in a DLL. A DLL by itself normally provides no information to the operating system about how to run any of the code contained within.
Consider adding another EXE project which runs your WPFWindow which you call using Process. This suggestion might be wrong, though, as we still don't know what your end goal is. You're asking "How do I flap my wings like a bird?" when the correct question could be "How do I buy a plane ticket?"
Related
I have built a WinForms application which consists of just a RichTextBox and ive built it into an EXE.
The purpose of this EXE is to act as a logger so that when im running tests the log is output onto this EXE.
Important note is that this EXE is referenced in a separate Unit Test (to be run by Microsoft Test Manager) solution by adding the EXE as a reference in the project. This seems to expose the objects I need.
I've had some success using HTTP through the ChannelFactory interfaces, but i'd prefer to talk to the EXE directly.
These unit tests I have are essentially sending and receiving data from OpenVMS, and some of these tests can take some time to complete.
So I built a new Console project to test the public methods I've exposed in my logger.exe and so far heres my code:
TerminalLogger.Logger term = new TerminalLogger.Logger();
term.TerminalLog("Test");
When I run this the form opens, but nothing loads in. So im assuming thats because the form runs on the same thread? Do I need to have this form running on its own thread?
I notice that if I add "term" to watch in Visual Studio and inspect the richtextbox I can see that it has actually written the value "Test" to the object, but of course I cannot see this as the form hasnt fully rendered in.
I still need to be able to call methods like term.LogMessage("Example Message") and get it to display on the form.
If you need extra info please add a comment and i'll do my best to elaborate more on my question.
Well I asked one of the web developers at my organisation on the offchance they might be able to help, and apparently calling a redraw fixed my issue!.
I simply added:
LogConsole.TextChanged += (sender, e) =>
{
if (this.Visible)
{
this.Refresh();
}
};
Here is how I call the code from my unit test:
TerminalLogger.Logger term = new TerminalLogger.Logger();
term.Show();
term.TerminalLog("Test1");
term.TerminalLog("Test2");
And I can send messages to it without it locking up.
I'd like to create an updater that checks if the main .exe file is updated. Must i create an updater file that closes the main application and overwrites the .exe file or is there another way?
Must i create an updater file that closes the main application and overwrites the .exe file or is there another way?
If you want to overwrite the exe then yes you must exit the currently running exe. The way I currently handle this is the following (it is a Winforms app for the record)
1) update check is performed from internet
2) download of update starts to temporary folder (yourapp/temp_update)
3) once download completes, application uses Process.Start to launch updater.exe
4) updater.exe is passed arguments via the command line - namely the exe name and folder location so it can
know which Process to wait for exit (your main application)
where to copy the new exe (and any other files)
eg
var process = new Process();
process.StartInfo.FileName = "updater.exe"
process.StartInfo.Arguments = " yourApp.exe temp_update"
process.Start();
//now exit your app.
Environment.Exit(0); //if Winforms app then just call `.Close()` on your main form
4) main application exits
6) updater.exe calls Process.Start( ) to restart your main application; update.exe then
5) updater.exe detects exit - it can now copy the new exe over the old one exists
Rarely, updater.exe itself needs updating - i embed it inside inside the exe (This is typically ok because update.exe is tiny) - the new exe would then extract it and copy over the existing updater.exe.
I've seen some other possible alternatives using AppDomains whereby your exe is a very lightweight executable that checks for updates and then loads your 'main application' into a new AppDomain : (pseudo code)
public static void Main(string[] args)
{
//dont reference MyApp.dll from this exe!
if(CheckForUpdate())
{
//download new MyApp.dll
}
//load MyApp.dll in a new AppDomain
}
however the problem with these approaches it doesnt matter how lightweight you make that initial exe you may need to update it and then you are back at the original problem!
You must. And it is more complex. Normal users have no right to replace an executable for security reasons. It requires escalated rights as the programs folder is protected.
I would start looking into installer technologies and make a version check before. This is not that hard.
This question already has answers here:
How do I set a program to launch at startup
(13 answers)
Closed 9 years ago.
For some CAD software i have been making a cleanup program (WPF) that can clean certain types of temp and journal files that i wont need anymore, but that my cad software does not clean out of itself.
As i made it into a WPF application, i just made a button to clean up any filetypes that i wont need.
Now i have been looking for a function to make it execute after i have logged into windows. So i do not try to execute the full program after the startup, only one of the commands within the program. I know that there is a folder called startup in windows, but i preffer not to use that one.
Is it possible to do it in a different way? So i can just make a checkbox in my application where i can set yes or no to make it run at startup?
i have been looking around, but havent realy found anything about this.
In accordance to your edit;
Check out your startup options in this post; How do I set a program to launch at startup
Then with you addition of 'So i do not try to execute the full program after the startup, only one of the commands within the program'; make use of start-up parameters.
Supply your application with a command-line parameter and check it in your application's Program.cs.
When the right parameter is set, just clean your files and don't show the interface.
When the parameter isn't set (which it won't be when the user just double clicks the icon) run your program like normal.
To use command-line arguments just add a string[] parameter to your Main method like so;
public static void Main(string[] args)
{
Console.WriteLine("Number of command line parameters = {0}",
args.Length);
foreach(string s in args)
{
Console.WriteLine(s);
}
}
Your full (pseudo-)code would look like this:
public static void Main(string[] args)
{
if (args[0].Equals("/JUSTCLEANPLEASE"))
JustClean();
else
Application.Run(new Form1());
}
Why do you not want to use the auto start directory?
There are some alternatives to this. Please refer to this knowledgebase article:
http://support.microsoft.com/kb/137367/en-us
When you create an entry in one of those nodes specified in the link you can control whether you want to run something on every start up or only once (see RunOnce).
The simple approach would be creating a batch file in a temp directory or somewhere where it won't be deleted and add it to one of the auto run nodes, that is appropriate for you.
Samples on how to access registry with C# are plenty.
Edit: Or see the link, provided by Gerald Versluis
However keep in mind that there is a difference in Run and RunOnce of these different nodes
Not sure how your program is set up, but I would split the user interface (GUI) from the logic.
You could create a service for the logic and the WPF user interface would seperately call this service.
You could then write a little code to call the service without having to use the GUI.
Have a look in MSConfig.exe for better control over the startup and services etc.
I have an application that works weirdly: the setup process copies the files to a temp folder, then checks a few things, and if everything is ok, moves the files to the final folder.
The problem is that the installer creates the shortcuts before the files are moved.
The result is that on the start menu (the one with the tiles), the icon is the "default sortcut" one.
I have tried to force the refresh of the system using this link (broadcast a WM_SETTINGCHANGE message) but it doesn't seem to work for the Windows 8 start menu.
Even rebooting the OS doesn't seem to refresh the icon, the only thing that works is to reinstall the soft on top of itself.
How can I force the icons refresh for the Win8 start menu ?
Thanks
First off, you don't tell us why your install process needs to work the way that it does. I agree that's weird, but I assume you have a good reason for doing it that way. If not, I suggest starting there and fixing the installer rather than putting band-aids on individual problems. You're bound to run into other problems later, and the root fix is bound to be much simpler and easier to maintain than a bunch of band-aids.
Anyway, if you must go down this path… The reason that broadcasting a WM_SETTINGCHANGE message doesn't work is because this doesn't have anything to do with icons. Those are managed separately by Explorer and don't get rebuilt unless you delete its icon cache. That, naturally, prompts it to rebuild it. It's a common troubleshooting technique for end users, but it's not something you want to do programmatically. Talk about an ugly hack. And altering the global state to solve a local problem.
Besides, if rebooting the OS doesn't work, you know it's not as simple as you make it sound in your question: a property in need of a refresh. The reason that reinstalling on top of the existing installation works is because when the shortcut gets created in the beginning, its target executable already exists in the expected place (put there by the previous installation) with a valid icon.
What I recommend doing is writing some code to change the icon of the existing shortcut. Make sure that you execute it after you've copied the executable file to its final destination. The method that allows you to do that is IShellLink::SetIconLocation, which takes two parameters: a path to the icon (this would be the path to your executable file), and the index of the icon (this would probably be 0 assuming that the icon you want is the first and only one contained in the executable).
Of course, in order to call SetIconLocation, you're going to have to obtain an object representing your shortcut that implements IShellLink. This is a COM interface, which I don't believe is wrapped anywhere by the .NET Framework. General information on creating shortcuts from C# is available in the answers to this question. More specifically, there's a link to this wrapper class that you can use to do most of the dirty work. I don't think it contains a method for setting/changing the icon, but that can be trivially added.
Alternatively, you can get at these same properties using the Windows Scripting Host (WSH) wrapper, WshShortcut. It has an IconLocation property that works much the same way except that it takes only a single string argument, with the index delimited from the path by a comma: myApp.exe, 0. It's not particularly well documented (best I can find), but to get an existing shortcut, you just use the CreateShortcut method and specify the path to the existing shortcut. For example:
public void SetShortcutIcon(string shortcutPath, string iconPath, int iconIndex)
{
// Note: no error checking is performed for the parameters!
// This is not production-ready code!
// If a shortcut does not exist at the specified path, you should
// create a new one instead.
// If iconPath does not specify a valid executable file, you should
// set a default icon or perhaps abort.
IWshRuntimeLibrary.WshShell wsh = new IWshRuntimeLibrary.WshShell();
IWshRuntimeLibrary.IWshShortcut shortcut = wsh.CreateShortcut(shortcutPath);
shortcut.IconLocation = String.Format("{0}, {1}", iconPath, iconIndex);
shortcut.Save();
}
Note that in order for the above code to compile, you will also need to add a reference to the Windows Script Host Object Model to your application. To do this, right-click on the "References" folder in the Solution Explorer, click the "COM" tab, and find "Windows Script Host Object Model" in the list.
I just tested this and it works; the effect is instant.
How would you open a file (that has a known file/app association in the registry) into a "running instance" of the application it's supposed to open in? An example would be, I have Excel open and I click on an XLS file.....the file opens up in the current Excel instance. I want to do this for a custom application...how does the eventing/messaging work that "tells" the current instance that it needs to open a file? Is there a "file watcher" that looks for a request to do so etc? Thanks..
What you want to do is inherit a class from WindowsFormsApplicationBase, setting the protected IsSingleInstance property to true:
// This should all be refactored to make it less tightly-coupled, obviously.
class MyWindowsApplicationBase : WindowsFormsApplicationBase
{
internal MyWindowsApplicationBase() : base()
{
// This is a single instance application.
this.IsSingleInstance = true;
// Set to the instance of your form to run.
this.MainForm = new MyForm();
}
}
The Main method of your app then looks like this:
// This should all be refactored to make it less tightly-coupled, obviously.
public static void Main(string args[])
{
// Process the args.
<process args here>
// Create the application base.
MyWindowsApplicationBase appBase = new MyWindowsApplicationBase();
// <1> Set the StartupNextInstance event handler.
appBase.StartupNextInstance = <event handler code>;
// Show the main form of the app.
appBase.Run(args);
}
Note the section marked <1>. You set this up with an event handler for the StartupNextInstanceEvent. This event is fired when the next instance of your app is fired when you have a single instance application (which you specified in the constructor of MyWindowsApplicationBase). The event handler will pass an EventArgs-derived class which will have the command line arguments which you can then process in the running instance of your app.
Then, all you have to do is set the file associations normally for the file types you want your app to process, and you are set.
Looks like what you are looking for is creating a single instance application. This can be done in C# by using WindowsFormsApplicationBase located in Microsoft.VisualBasic.dll
For details, take a look at:
http://www.hanselman.com/blog/TheWeeklySourceCode31SingleInstanceWinFormsAndMicrosoftVisualBasicdll.aspx
or search for WindowsFormsApplicationBase
The way I'd do it is like this:
First thing in the main method, check the process list for an existing instance of the application.
If found, send the filename/path to the already running instance using your favorite interprocess communication method (sending windows messages, remoting, wcf, etc.)
Close the new process that windows tried to start (since the existing instance already handled the file open operation
Example using TCP-sockets:
http://pieterjan.pro/?a=Projecten_csharp_DrawIt.php
start TCPListener on the form
connect TCPClient in the main of the second instance
Send ActivationArguments through the TCP-connection to the form
Works for multiple files at once as well, and even for multiple files at the first time (when application not started yet)
The most important code blocks are:
The constructor of the MainForm (Hoofdscherm) where the server is started and the port number is written to a file. The first file is opened as well.
The Main-function (Program.cs) where the second, third, ... instance connects to the TcpListener in the first instance and sends the filename through the socket
The source code is available on the button "Broncode"
Windows uses DDE for this purpose.
Dynamic Data Exchange (DDE) is a technology for communication between multiple applications under Microsoft Windows or OS/2.
Registry associations for word or office files usually have DDE commands in addition to the usual file association (to be executed if the app already is running).
So you can host a DDE server in your C# app to implement this functionality.