g_main_loop_run() freeze the window - c#

I am trying to create a video player in C# using the functions imported from the GStreamer library by Pinvoke. It's looking nice so far, but if I add g_main_loop_run(loop); my C# application freezes and I just can't click any button or move my window.
I think that the problem may involve gmaincontext, but I don't know exactly what the problem is or how to resolve it.
Here is my C++ code in my library:
GstElement *pipeline;
void seek_to_pos(double position) {
gint64 len;
gst_element_query_duration(pipeline, GST_FORMAT_TIME, &len);
gst_element_seek_simple(pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, len*position);
}
void play_file(char* path_to_file, void* hwnd_ptr_of_window){
gst_init(NULL, NULL);
HWND hwnd = (HWND)hwnd_ptr_of_window;
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
pipeline = gst_element_factory_make("playbin", "player");
g_object_set (G_OBJECT (pipeline), "uri", path_to_file, NULL);
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(pipeline), (guintptr)hwnd);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_main_loop_run(loop);//problem
}
Without the g_main_loop_run(loop); string, all works fine, but of course I need it for some another things.
Also, I already know that I need to run GMainLoop in a
different thread to not block my C# application's event loop but I don't know exactly how I can do it.
So I need a code sample or link which describes how I can do it right. Thanks!

Related

Error on copy some text to clipboard in C# WPF [duplicate]

Why does the following code sometimes causes an Exception with the contents "CLIPBRD_E_CANT_OPEN":
Clipboard.SetText(str);
This usually occurs the first time the Clipboard is used in the application and not after that.
This is caused by a bug/feature in Terminal Services clipboard (and possible other things) and the .NET implementation of the clipboard. A delay in opening the clipboard causes the error, which usually passes within a few milliseconds.
The solution is to try multiple times within a loop and sleep in between.
for (int i = 0; i < 10; i++)
{
try
{
Clipboard.SetText(str);
return;
}
catch { }
System.Threading.Thread.Sleep(10);
}
Actually, I think this is the fault of the Win32 API.
To set data in the clipboard, you have to open it first. Only one process can have the clipboard open at a time. So, when you check, if another process has the clipboard open for any reason, your attempt to open it will fail.
It just so happens that Terminal Services keeps track of the clipboard, and on older versions of Windows (pre-Vista), you have to open the clipboard to see what's inside... which ends up blocking you. The only solution is to wait until Terminal Services closes the clipboard and try again.
It's important to realize that this is not specific to Terminal Services, though: it can happen with anything. Working with the clipboard in Win32 is a giant race condition. But, since by design you're only supposed to muck around with the clipboard in response to user input, this usually doesn't present a problem.
I know this question is old, but the problem still exists. As mentioned before, this exception occurs when the system clipboard is blocked by another process. Unfortunately, there are many snipping tools, programs for screenshots and file copy tools which can block the Windows clipboard. So you will get the exception every time you try to use Clipboard.SetText(str) when such a tool is installed on your PC.
Solution:
never use
Clipboard.SetText(str);
use instead
Clipboard.SetDataObject(str);
I solved this issue for my own app using native Win32 functions: OpenClipboard(), CloseClipboard() and SetClipboardData().
Below the wrapper class I made. Could anyone please review it and tell if it is correct or not. Especially when the managed code is running as x64 app (I use Any CPU in the project options). What happens when I link to x86 libraries from x64 app?
Thank you!
Here's the code:
public static class ClipboardNative
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
[DllImport("user32.dll")]
private static extern bool SetClipboardData(uint uFormat, IntPtr data);
private const uint CF_UNICODETEXT = 13;
public static bool CopyTextToClipboard(string text)
{
if (!OpenClipboard(IntPtr.Zero)){
return false;
}
var global = Marshal.StringToHGlobalUni(text);
SetClipboardData(CF_UNICODETEXT, global);
CloseClipboard();
//-------------------------------------------
// Not sure, but it looks like we do not need
// to free HGLOBAL because Clipboard is now
// responsible for the copied data. (?)
//
// Otherwise the second call will crash
// the app with a Win32 exception
// inside OpenClipboard() function
//-------------------------------------------
// Marshal.FreeHGlobal(global);
return true;
}
}
Actually there could be another issue at hand. The framework call (both the WPF and winform flavors) to something like this (code is from reflector):
private static void SetDataInternal(string format, object data)
{
bool flag;
if (IsDataFormatAutoConvert(format))
{
flag = true;
}
else
{
flag = false;
}
IDataObject obj2 = new DataObject();
obj2.SetData(format, data, flag);
SetDataObject(obj2, true);
}
Note that SetDataObject is always called with true in this case.
Internally that triggers two calls to the win32 api, one to set the data and one to flush it from your app so it's available after the app closes.
I've seen several apps (some chrome plugin, and a download manager) that listen to the clipboard event. As soon as the first call hits, the app will open the clipboard to look into the data, and the second call to flush will fail.
Haven't found a good solution except to write my own clipboard class that uses direct win32 API or to call setDataObject directly with false for keeping data after the app closes.
Use the WinForms version (yes, there is no harm using WinForms in WPF applications), it handles everything you need:
System.Windows.Forms.Clipboard.SetDataObject(yourText, true, 10, 100);
This will attempt to copy yourText to the clipboard, it remains after your app exists, will attempt up to 10 times, and will wait 100ms between each attempt.
Ref. https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setdataobject?view=netframework-4.7.2#System_Windows_Forms_Clipboard_SetDataObject_System_Object_System_Boolean_System_Int32_System_Int32_
This happen to me in my WPF application. I got OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).
i use
ApplicationCommands.Copy.Execute(null, myDataGrid);
solution is to clear the clipboard first
Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
The difference between Cliboard.SetText and Cliboard.SetDataObject in WPF is that the text is not copied to the clipboard, only the pointer. I checked the source code. If we call SetDataObject(data, true) Clipoard.Flush() will also be called. Thanks to this, text or data is available even after closing the application. I think Windows applications only call Flush() when they are shutting down. Thanks to this, it saves memory and at the same time gives access to data without an active application.
Copy to clipboard:
IDataObject CopyStringToClipboard(string s)
{
var dataObject = new DataObject(s);
Clipboard.SetDataObject(dataObject, false);
return dataObject;
}
Code when app or window is closed:
try
{
if ((clipboardData != null) && Clipboard.IsCurrent(clipboardData))
Clipboard.Flush();
}
catch (COMException ex) {}
clipboardData is a window class field or static variable.
That's not a solution, just some additional information on how to reproduce it when all solutions work on your PC and fail somewhere else. As mentioned in the accepted answer - clipboard can be busy by some other app. You just need to handle this failure properly, to explain user somehow why it does not work.
So, just create a new console app with few lines below and run it. And while it is running - test your primary app on how it is handles busy clipboard:
using System;
using System.Runtime.InteropServices;
namespace Clipboard
{
class Program
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
static void Main(string[] args)
{
bool res = OpenClipboard(IntPtr.Zero);
Console.Write(res);
Console.Read();
CloseClipboard();
}
}
}

GStreamer# equivalent for video overlay functions

I have a C#/Mono application for rendering video streams and, until recently, I've been using my own in-house developed InterOp bindings.
I'm now moving from that method to use those of GStreamer#, since that's likely to be much less maintenance effort, at least for me :-)
Since I need to bind independent video streams to a specific DrawableArea widgets, my method captured the GStreamer messages then checked them with the function gst_is_video_overlay_prepare_window_handle_message(msg)(a).
If that returned true, I then responded with a call to gst_video_overlay_set_window_handle(xid), where xid was the handle for the widget obtained earlier with gdk_x11_window_get_xid().
My problem is this: searching through the GStreamer# code, I cannot find an equivalent function for doing the binding so I was wondering how this is meant to be done.
Anyone have any advice or information to offer?
The source code for those two functions are in gst-plugins-base-1.4.4/gst-libs/gst/video/videooverlay.c so, in a pinch, I could dummy up my own functions to do the job (or just stick with our bindings for that one little bit) but it seems to me this would have been included in GStreamer# since rendering to specific widgets seems like a very handy facility.
(a) Those GStreamer wags. They must replace their keyboards quite a bit with all that unnecessary typing :-)
Turns out those functions are available, but in a separate library and sub-namespace to the baseline GStreamer stuff.
The getting of the Xid is still done with a self-made InterOp binding, to wit:
[DllImport("libgdk-3", EntryPoint = "gdk_x11_window_get_xid")]
private extern static IntPtr GdkX11WindowGetXid(IntPtr window);
You also have instance-level variables for the playbin, bus and X11 window ID:
private IntPtr windowXid;
private Gst.Element playBin;
private Gst.Bus bus;
Then, when instantiating the class, you capture the Realized signal and ensure that all messages on the GStreamer bus go to your callback function:
this.Realized += OnRealized;
playBin = Gst.ElementFactory.Make('playbin', 'playbin');
bus = playBin.Bus;
bus.AddSignalWatch();
bus.Message += MessageCallback;
In that realisation function, you save the Xid for later use (in an instance variable):
void OnRealized(object o, EventArgs args) {
windowXid = GdkX11WindowGetXid(this.Window.Handle);
}
and, when asked by GStreamer, provide this handle in response to the request:
private void MessageCallback(object o, MessageArgs args) {
Gst.Message msg = args.Message;
if (! Gst.Video.Global.IsVideoOverlayPrepareWindowHandleMessage(msg))
return;
Gst.Element src = msg.Src as Gst.Element;
if (src == null)
return;
Gst.Element overlay = null;
if (src is Gst.Bin)
overlay = ((Gst.Bin)src).GetByInterface
Gst.Video.VideoOverlayAdapter = new Gst.Video.VideoOverlayAdapter(overlay.Handle);
adapter.WindowHandle = windowXid;
}
Note that I've fully qualified all the GStreamer objects so it's absolutely clear where they can be found. The code would probably be a lot cleaner without them (and with using var for the variables) but I wanted to ensure all information was available.

C++ hybrid (native managed) +sdl + opengl & c# winforms

I'm working on a interactive rendering software using opengl in sdl written in c++.
The project evolving, I wanted to have a HIM/GUI to manipulate my rendering engine.
So I started to search some easy/fast HIM coding ways to do it.
Finnaly I decided to use winforms and c# to create HIM, because its offer a way to design and code easily a HIM.
At first i started to create an hybrid dll with native and managed c++. OK.
After I try to use this dll inside a c# application. OK.
Now my goal is: insert the opengl rendering inside the winforms application.
My questions are:
Can I bind my SDLcontext/SDLwindows(C++) to a winforms object?
Can I bind a c# bitmap to an array of byte from my dll ? (aim to update pixels of it by the dll)
if ok: do I have to call a function to refresh my GUI(winforms) on pixel change ?
Do you think it will be interesting to drop SDL and use only winforms for this kind of work ?
Any suggestion ?
EDIT: add information about my investigation
Thanks to Lawrence Kok I pursue my research.
So I tried to bind my SDL windows to a Panel form
private void LaunchEngine(string str)
{
unsafe
{
byte[] bytes = Encoding.ASCII.GetBytes(str);
sbyte[] sbyt = (sbyte[])(Array)bytes;
fixed (sbyte* p = sbyt)
{
// Engine is a managed class that bridge my c++ to c#
// all it's function are static
Engine.LOAD_CONTENT_FROM_FILE(p);
Engine.PRINTCONFIGURATION();
if (Engine.LOAD_ENGINE_DATA() && Engine.INITIALISE_ENGINE_DATA())
{
// Bind attempt here
_SdlWindowHandle = Engine.GETHANDLE();
SetWindowPos(_SdlWindowHandle, this.Handle, 0, 0, 0, 0, (SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_SHOWWINDOW));
// Make the SDL Window the child of our Panel
SetParent(_SdlWindowHandle, m_SdlPanel.Handle);
ShowWindow(_SdlWindowHandle, ShowWindowCommand.SW_SHOWNORMAL);
// In futur i will put this loop in another thread
// but for now I'm trying to validate my prototype
for (; ; )
{
Engine.UPDATE_ENGINE_DATA();
Engine.DRAW_ENGINE_DATA();
}
}
}
}
Actually, the change the parent of my sdl windows, close it, and my engine is running, but my panel is completely blank.
I think I'm missing something here but i can't figured what.
here is how i get the SDL window handle (from SDL2)
// coming from c++ native library
// and represent by Engine.GETHANDLE();
// from managed c++ lib
HWND SDLWindowManager::GetHandle()
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
/*if (SDL_GetWMInfo(&info) < 0)
return 0;*/
SDL_GetWindowWMInfo(_mainWindow, &info);
return info.info.win.window;
}
EDIT: problem solved
I forget to add the panel to winforms control:
public Form1()
{
InitializeComponent();
m_SdlPanel = new Panel();
m_SdlPanel.Size = new Size(512, 512);
m_SdlPanel.Location = new Point(0, 0);
Controls.Add(m_SdlPanel);
}
Given that your target platform is Windows, there is a very easy way to insert your opengl rendering into your windows form application. You can simply make a window with your favorite package of choice, sdl, sfml, plain winapi code. When you obtain the handle of the window in question, just change the style of the window so that it becomes a child window, and put it as a child window of your window of choice. As for the parent window, what I like to do is subclass special panel-type control for this purpose.
After that you can just use the composition of the controls as normal. Only the airspace problems might influence the presentation (https://msdn.microsoft.com/en-us/library/aa970688%28v=vs.110%29.aspx).

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

C# - Popup my application whenever print happened to test driver

I am working on the assignment to write a Virtual Printer with C# and NTDDK.
I have created a test printer driver and I am able to print .ps documents(redirected to C:\test\test.ps always) using it. But now I have to write a small application that will popup a messagebox saying "Print is done" using C# but I am not able to figure out how to do it?
Can anyone help me in this?
Thanks in advance!
Use the endprint event...that is something like, in the designer code put:
///////////////
something.EndPrint += new PrintEventHandler(endingclass);
//////////////
in the form constructor, or anywhere in the program/form where you can call the method put:
private void endingclass(object sender, PrintEventArgs e)
{
MessageBox.Show("wazaaaaaaaaaaaaaaaaa");
}
////////
Obviously, you need to make some...arrangements in the code above - but I believe they are self evident. In case you have problems, googl the endprint event, it will 100% has some examples associated with it.
////
On a side note...if you're really bored you can think of asynchronous process which checks the process every....1/10 of a second :). It will work, but it's a bizarre way to accomplish such a thing. Since you're a c++ developer, c# should be easy to you, you'll like it, it's more powerfull than c++ anyway.

Categories

Resources