Poor performance when running Unity as embedded window in application - c#

I am trying to run a Unity application via the following code:
IntPtr parentHandle = Process.GetCurrentProcess().MainWindowHandle;
ProcessStartInfo procInfo = new ProcessStartInfo(_pathToUnityExe);
Logging.Log_Critical.Send("Starting process at " + _pathToUnityExe);
if (_useEmbeddedWindow)
{
procInfo.Arguments = "-parentHWND " + parentHandle + " " + Environment.CommandLine;
procInfo.UseShellExecute = true;
procInfo.CreateNoWindow = true;
}
_externalAppProcess = Process.Start(procInfo);
I've been noticing somewhat poor performance on the parent WPF app side, namely sluggish responsiveness and hitching with animations and media. This seems to occur whether the Unity app is loading a complex scene or a completely blank one.
Since the Unity app is being started through a process, I don't imagine there is threading/blocking problem, but I'm not sure what else it could be unless this is an issue in Unity.
Does anyone have thoughts or experience with this?
I'm open to other approaches as well if needed, I mainly just need to be able to control the Unity window's size and position. I'm mostly suspicious of the -parentHWND argument causing weird behavior on Unity's side.
However, this code doesn't seem to remove the window title bar on its own, is there a way to do that without using the -parentHWND argument?
procInfo.CreateNoWindow = true;

My solution ended up being to simply not use the embedded window flag when starting the Unity executable as a process. I then didn't need any arguments to be set in the StartProcessInfo object.
On the Unity side of things, I have a script that is in the initial scene which ensures the Unity application doesn't have the Windows title bar decorations. In that script's start override method I have:
Screen.fullScreen = true;

Related

Trouble stopping particle emission in unity

I am trying to only allow the particle system to emit particles when something is visible. The particle system knows when to start if a Boolean named avail is true. The code I thought would work for this is the following:
if (avail)
{
GetComponent<MeshRenderer>().enabled = true;
GetComponent<ParticleSystem>().enableEmission = true;
print("Mesh enabled");
}
However, this failed. I also tried:
if (avail)
{
GetComponent<MeshRenderer>().enabled = true;
GetComponent<ParticleSystem>().emission.enabled = true;
print("Mesh enabled");
}
However, this too failed. On every site I have searched in, these two "solutions" came up but they don't work for me. the first example said "this method of doing this is obsolete" and the second example says I can't set "emission.enabled" to a variable because it is a getter not a setter. Any help with figuring this out is extremely appreciated.
I don't have unity opened right now, but I think that
GetComponent<ParticleSystem>().Stop();
is what you need. You can restart the system using
GetComponent<ParticleSystem>().Play();
Also, if you do this often, you should consider keeping your particle system in a class variable.

how to detect, and react when an External sub Process, invokes an error dialog

I am aware that this may on first glance look like a question you have seen before:
Knowing when an external process' window shows
But this is slightly different.
I have an C# asp.net web application, for helping people create an installer for their programs. (The developers here are mostly mechanical engineers scripting equations in some calculation tools, they are not software people, so we don't want them spending time learning wix, debugging the installers, maintaing GUID's between releases, and so on..)
The serverside will be running the console application "heat.exe" (a tool that is shipped with the wix tools), to harvest information on how to register dll's etc., if and only if they have a dll in their repository..
I do it like this:
public int runHeat(string filePath, string outputFile, ref string response)
{
response += "run heat.exe to harvest file data" + '\r' + '\n';
string args = "file " + '"' + filePath + '"' + " -srd -out" + '"' + outputFile + '"';
string command = Path.Combine(WixBinariesPath, "heat.exe");
string workPath = Path.GetDirectoryName(filePath);
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.WorkingDirectory = workPath;
processStartInfo.Arguments = args;
processStartInfo.FileName = command;
processStartInfo.ErrorDialog = false;
//create the process handler
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.AppendLine(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
// use the output
response += outputBuilder.ToString();
if (process.ExitCode != 0)
response += '\r' + '\n' + "heat.exe exited with code: " + process.ExitCode;
process.CancelOutputRead();
return process.ExitCode;
}
I thought this worked..
It passed tests, it's been running for a while without problems, and then suddenly, a developer called, that the webtool I made, no longer produces wix xml for him..
When I logged into the server, I found this dialog:
and then clicked [OK] - the web application then continued, and produced the xml, and stuff worked..
I have now found the dll that, makes heat throw this error. It doesn't really need registering (typical right?). So I could probably just write a timeout thing, to kill heat.exe if it takes to long, and thus unlock the waiting script, (and basicly fix the issue untill it happens again with a dll that actually needs registering) But that is not really detecting the error, that is just detecting that stuff takes time...
On this error, I would like to continue the script, but present a warning to the user, that heat.exe failed to run on that particular file. But to do this I need my asp.net application to know that this error was invoked, and dispose it, so that the script can continue..
how the *? do I get information that this runtime error occurred, so I can handle it from the server script?
Have you tried using the -sreg command line option to heat?
I now have, and as a result, heat.exe no longer chrashes, but this is not a solution, as heat also avoids harvesting the registry information that I need for autoregistering the dll's shipped with the code in question.
Working with external "uncooperative" executables often requires some trickery. I'd try the following:
Start the program on the command line, and check if there is any output when the error occurs. Probably it writes to standard error, and you could use RedirectStandardError, read the stream and hopefully get a clue when the error occurs.
Check if there is any logging-possibility in heat.exe that you could enable, and use this to detect the error-case. Maybe a verbose setting, or a log-file...
If none of the above worked, I'd use process monitor (e.g. https://technet.microsoft.com/de-at/sysinternals/bb896645.aspx). Start process monitor and then your application and bring it to the point of error. Filter the enormous output in process monitor to just your application (which is still quite a lot) and search at the end, whether there is any access where the programm might log the error. Maybe some log-files, or a logging-service. You could check this file after your timeout.
But something the would work in any case is the hack you already suggested in your question. To detect whether the dialog opened. There are also possibilities to browse through the content of the dialog box, so you could also read the text and check which kind of error it is. I used this once in production code to get the progress of an external program, which was written in a text field inside the form. I used spy++ (bundled with Visual Studio) to get the name/id of the text field, and accessed it using the (native) windows API. An ugly hack, but worked fine unless the external program's UI is changed.
In your case it is a standard Error Dialog, so it should stay quite consistent.

FindWindowByCaption function finding window handle that doesn't exist yet

I am writing a C# application that requires moving gnuplot graphs to specific positions on the user's screen. To do this I am using DllImport to bring in several functions into my program. Specifically FindWindowByCaption and MoveWindow and a few others. This has been working fine for me thus far, but suddenly the graphs have stopped moving.
I figured out that the graphs are taking longer to generate and it tries to execute the MoveWindow function before the window is created so the window is not actually moved. I am not sure why this is a problem now because it was fine in earlier versions of the code.
For some reason the FindWindowByCaption function finds the window handle before the window actually exists.
I have the find window function in a loop that is supposed to try to execute until it finds the proper handle. The name changes for each graph.
IntPtr windowId = IntPtr.Zero;
while (windowId == IntPtr.Zero)//keeps trying to get the id until it has it
windowId = FindWindowByCaption(IntPtr.Zero, "p " + polyValue + " s " + (dataLocation + 1));
For some reason it finds the handle for the gnuplot graph before it actually is created and then it tries to run the MoveWindow function too soon, so that when the graph is actually generated it does not go to the right place.
Any suggestions would be helpful
Thanks,
-Jake

How can I update the frame of a Windows Media Player COM?

I have a windows media player COM in my Windows Form project that plays and opens videos admirably. However, I would like to be able to grab the first frame of the loaded video so my program users can preview the video (and, ideally, recognize one video from another).
How can I update the frame displayed by the windows media player object?
I have tried using the following code at the end of my openFileDialog event response:
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
Text = openFileDialog1.SafeFileName + " - MPlayer 2.0";
//mediaPlayer1.openPlayer(openFileDialog1.FileName);
mediaPlayer1.URL = openFileDialog1.FileName;
//hopefully, this will load the first frame.
mediaPlayer1.Ctlcontrols.play();
mediaPlayer1.Ctlcontrols.pause();
}
However, when I run this, the pause command gets ignored (Auto-play for video loading is turned off, so the video won't start playing without calling .play(), above). If I had to guess, I'd say that this is because of some threading operation that calls play, moves on to call pause, calls pause, and then, finally, the play resolves, and the video starts - but because the .pause resolved before the .play, the net effect is the .pause is ultimately unheeded.
Firstly, is there a way other than .play(); .pause(); to snag a preview image of the video for the AxWindowsMediaPlayer object? If not, how can I make sure that my .pause() doesn't get ignored?
(I know that .play(); .pause(); works in the general case, because I tested with a separate button that invoked those two methods after the video finished loading, and it worked as expected)
You can't do a lot of things with this COM. However Follow this link and you will find a Class that will help you extract an image from a Video file. You could simply get extract the image and just put it in top of the video, or next to it. This is a simple workaround for your requirement. If not happy with it, I would strongly recommend not using this COM at all and use some other open source video player/plugins. There a lot of real good ones, but I could recommend the VLC Plugin, or try finding another.
Good luck in your quest.
Hanlet
While the Windows Media Player Com might not officially support a feature like this, its quite easy to 'fake' this. If you use a CtlControls2, you have access to the built-in "step(1)" function, which proceeds exactly one frame.
I've discovered that if you call step(1) after calling pause(), searching on the trackbar will also update the video.
It's not pretty, but it works!
This is a little trick to solve the common step(-1) not working issue.
IWMPControls2 Ctlcontrols2 = (IWMPControls2)WindowsMediaPlayer.Ctlcontrols;
double frameRate = WindowsMediaPlayer.network.encodedFrameRate;
Console.WriteLine("FRAMERATE: " + frameRate); //Debug
double step = 1.0 / frameRate;
Console.WriteLine("STEP: " + step); //Debug
WindowsMediaPlayer.Ctlcontrols.currentPosition -= step; //Go backwards
WindowsMediaPlayer.Ctlcontrols.pause();
Ctlcontrols2.step(1);

Better way to "dock" a third party running application inside a windows.forms panel?

I am currently doing this as follows:
// _Container is the panel that the program is to be displayed in.
System.Diagnostics.Process procTest = new System.Diagnostics.Process();
procTest.StartInfo.FileName = "TEST.EXE";
procTest.StartInfo.CreateNoWindow = false;
procTest.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
procTest.Start();
procTest.WaitForInputIdle();
SetParent(procTest.MainWindowHandle, _Container.Handle);
MoveWindow(procTest.MainWindowHandle,
0, 0, _Container.Width, _Container.Height, true);
The problem I am having with this code is that some parts of the application UI no longer function properly once I change the MainWindowHandle (ie: buttons missing text).
Is there a way to do this without causing issues with the docked application? (Either through .net or user32)?
First of all, instead of simply waiting 1.5 seconds, try calling procTest.WaitForInputIdle to wait until its message loop is free. You already are.
In general, I don't think it's possible to do this without modifying the program that you're hosting.
EDIT: You could try to keep the other program above your hosting area by hiding in from the taskbar, removing its title bar, moving it as your program moves, etc. However, this still wouldn't work perfectly; I recommend that you try to find some alternative.
Try contacting the original developers of the third-party application and asking for their advice.

Categories

Resources