I'm writing an application in c# that needs to display a property page. I have the code:
ISpecifyPropertyPages pProp = sourceObject as ISpecifyPropertyPages;
int hr = 0;
//Get the name of the filter from the FilterInfo struct
FilterInfo filterInfo;
hr = ((IBaseFilter) sourceObject).QueryFilterInfo(out filterInfo);
//DsError.ThrowExceptionForHR(hr);
if (hr == 0)
{
// Get the propertypages from the property bag
CAUUID caGUID;
hr = pProp.GetPages(out caGUID);
if (hr == 0)
{
// Create and display the OlePropertyFrame
hr = Win32.OleCreatePropertyFrame(parentWindowForPropertyPage, 0, 0,
filterInfo.achName, 1,ref sourceObject,
caGUID.cElems,
caGUID.pElems, 0, 0, IntPtr.Zero);
}
// Release COM objects
Marshal.ReleaseComObject(pProp);
Marshal.FreeCoTaskMem(caGUID.pElems);
}
... which works fine IF the source isn't running - if the source is running the property window flashes up and immediately exits. I'm guessing I need to use OleCreatePropertyFrameIndirect instead and call it with the existing object but I can't find any examples of doing that via c# - any ideas?
It is unlikely to be a problem with OleCreatePropertyFrameIndirect. Far more likely, the property page itself (and/or the source filter) has a problem and closes, throws an exception, generates access violation etc.
Related
Question: What is the best way to programmatically disconnect and reconnect displays programmatically?
The Goal: Kill the video output (black screen with no backlight) on a display and later turn it back on. Imagine unplugging the video cord from the monitor, then plugging it back in.
My Attempt:
// Get the monitor to disable
uint iDevNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.cb = Marshal.SizeOf(displayDevice);
EnumDisplayDevices(null, iDevNum, ref displayDevice, 0))
DEVMODE devMode = new DEVMODE();
EnumDisplaySettings(displayDevice.DeviceName, 0, ref devMode);
//
// Do something here to disable this display device!
//
// Save the display settings
ChangeDisplaySettingsEx(displayDevice.DeviceName, ref devMode,
IntPtr.Zero, ChangeDisplaySettingsFlags.CDS_NONE, IntPtr.Zero);
I can interact with each display, but I can't figure out how to disconnect one.
It is similar to "Disconnect this display" in the Screen Resolution properties in Windows 7:
Notes:
Turning off video output on all displays won't work because I need the other monitors to stay on.
The desktop area on the "dead" display does NOT need to be usable when it is off. Also, it is fine if windows move around.
References:
SO: Enabling a Second Monitor
How to Turn Off a Monitor
1) Get MultiMonitorHelper from here:
https://github.com/ChrisEelmaa/MultiMonitorHelper/tree/master
2) Extend Win7Display to disconnect the display:
using MultiMonitorHelper.DisplayModels.Win7.Enum;
using MultiMonitorHelper.DisplayModels.Win7.Struct;
/// <summary>
/// Disconnect a display.
/// </summary>
public void DisconnectDisplay(int displayNumber)
{
// Get the necessary display information
int numPathArrayElements = -1;
int numModeInfoArrayElements = -1;
StatusCode error = CCDWrapper.GetDisplayConfigBufferSizes(
QueryDisplayFlags.OnlyActivePaths,
out numPathArrayElements,
out numModeInfoArrayElements);
DisplayConfigPathInfo[] pathInfoArray = new DisplayConfigPathInfo[numPathArrayElements];
DisplayConfigModeInfo[] modeInfoArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
error = CCDWrapper.QueryDisplayConfig(
QueryDisplayFlags.OnlyActivePaths,
ref numPathArrayElements,
pathInfoArray,
ref numModeInfoArrayElements,
modeInfoArray,
IntPtr.Zero);
if (error != StatusCode.Success)
{
// QueryDisplayConfig failed
}
// Check the index
if (pathInfoArray[displayNumber].sourceInfo.modeInfoIdx < modeInfoArray.Length)
{
// Disable and reset the display configuration
pathInfoArray[displayNumber].flags = DisplayConfigFlags.Zero;
error = CCDWrapper.SetDisplayConfig(
pathInfoArray.Length,
pathInfoArray,
modeInfoArray.Length,
modeInfoArray,
(SdcFlags.Apply | SdcFlags.AllowChanges | SdcFlags.UseSuppliedDisplayConfig));
if (error != StatusCode.Success)
{
// SetDisplayConfig failed
}
}
}
3) Extend Win7Display to reconnect the display using an answer from this post:
using System.Diagnostics;
/// <summary>
/// Reconnect all displays.
/// </summary>
public void ReconnectDisplays()
{
DisplayChanger.Start();
}
private static Process DisplayChanger = new Process
{
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "DisplaySwitch.exe",
Arguments = "/extend"
}
};
4) Update the methods in IDisplay.
5) Implement the methods:
IDisplayModel displayModel = DisplayFactory.GetDisplayModel();
List<IDisplay> displayList = displayModel.GetActiveDisplays().ToList();
displayList[0].DisconnectDisplay(0);
displayList[0].ReconnectDisplays();
There's a github project that I STILL haven't got around, but it's a starting point. You need to use Win7 specific API in order to change settings. ChangeDisplaySettings won't work.
Have a look: https://github.com/ChrisEelmaa/MultiMonitorHelper
This is what you need to do:
update the IDisplay interface to support TurnOff() method,
and then call it:
var displayModel = DisplayFactory.GetDisplayModel();
var displayList = displayModel.GetActiveDisplays().ToList();
var firstDisplay = displayList[0].TurnOff();
How to implement TurnOff()? I WOULD imagine this is how(I might be wrong here now):
You need to break the connection between GPU & monitor through breaking the "paths". You can break path between source and target like this:
Call SetDisplayConfig() and pass inside specific paths and make sure you map out the DISPLAYCONFIG_PATH_ACTIVE from the DISPLAY_PATH_INFO structure flags integer.
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553945(v=vs.85).aspx
Sorry for not being more helpful, but this is pretty hardcore stuff, it took me quite a while to even understand the basics of that API. It's a starting point :-),
take a look at the example how to rotate specific monitor in Win7: How do I set the monitor orientation in Windows 7?
In all honesty, just wrap the DisplaySwitch.exe for Win7, and pass /internal or /external(depending if you want to disable/enable first/second monitor), this might or might not work for >2 monitors.
We are using Win32 calls to the GDI Printing API to print graphics.
I've noticed that on certain PCL print queues, we leak GDI memory when printing. As far as I can tell everything is being disposed of properly. Not being an admin at my company it's rather difficult for me to tell which drivers print queues are using. PCL6 drivers appear to be the main culprit. Other seemingly unrelated issues I've seen:
System.ComponentModel.Win32Exception: The data area passed to a system call is too small
System.ComponentModel.Win32Exception: The operation completed successfully
var hdc = GdiPrintWin32.CreateDC(IntPtr.Zero, printerName, IntPtr.Zero, IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw GetError("Device context failed for {0}", printerName);
try
{
if (GdiPrintWin32.StartDoc(hdc, ref doc) <= 0)
throw GetError("Failed to start print job");
This logic is currently being done in a WCF hosted inside a Windows service. When I set the instance mode to single and hammer it, I only get it the first couple calls; when I set the instance mode to per call, it's a lot easier to reproduce.
I've also seen errors where basically no calls can go through to the service for about 20 minutes and requires a restart to fix. I notice that after stopping the service there are Windows Event logs to the effect of:
2 user registry handles leaked from \Registry\User...: Process 3792 (\Device\HarddiskVolume1\Windows\System32\rundll32.exe) has opened key \REGISTRY\USER... Process 3792 (\Device\HarddiskVolume1\Windows\System32\rundll32.exe) has opened key ...\Software\Hewlett-Packard\HP SSNP
Basically it's been nothing but a nightmare for us. Has anyone had any experience with anything like this?
The guts of the code we're using to print are:
var doc = new Docinfo { DocName = printJobName };
// create a device-context
var hdc = GdiPrintWin32.CreateDC(IntPtr.Zero, printerName, IntPtr.Zero, IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw GetError("Device context failed for {0}", printerName);
try
{
if (GdiPrintWin32.StartDoc(hdc, ref doc) <= 0)
throw GetError("Failed to start print job");
foreach (PrintingMetafile metafile in pages)
{
var bytes = metafile.GetBytes();
// load the bytes to a memory location and get the pointer
var inMemRef = GdiPrintWin32.SetEnhMetaFileBits((uint)bytes.Length, bytes);
if (inMemRef == IntPtr.Zero)
throw GetError("Failed to create EMF in memory");
// Get the pixel coordinates of the paper
var x = 0; // GdiPrintWin32.GetDeviceCaps(hdc, HORZSIZE);
var y = 0; // GdiPrintWin32.GetDeviceCaps(hdc, VERTSIZE);
var hres = GdiPrintWin32.GetDeviceCaps(hdc, HORZRES);
var vres = GdiPrintWin32.GetDeviceCaps(hdc, VERTRES);
var rect = new Rect { Left = x, Top = y, Right = hres, Bottom = vres };
if (GdiPrintWin32.StartPage(hdc) <= 0)
throw GetError("StartPage failed");
if (GdiPrintWin32.PlayEnhMetaFile(hdc, inMemRef, ref rect) == 0)
throw GetError("PlayEnhMetaFile failed");
if (GdiPrintWin32.EndPage(hdc) <= 0)
throw GetError("EndPage failed");
if (inMemRef != IntPtr.Zero)
{
GdiPrintWin32.DeleteEnhMetaFile(inMemRef);
}
}
if (GdiPrintWin32.EndDoc(hdc) <= 0)
throw GetError("Failed to finish print job");
}
finally
{
GdiPrintWin32.DeleteDC(hdc);
}
This question is for educational purposes only.
I am aware of how a native program is working. The compiler takes each primitive and gives it an address, then uses that address in the program. For structures, it simply stacks the address together (with some padding) - but basically, a structure doesn't really "exist".
The native program doesn't tell me which fields and variables it has. It only accesses different addresses - and if I look at the assembly, I can name each address if I want to, but the program won't give me that information. So assuming I am looking for a specific variable, I cannot do it without either examining the executing of the program, or it's assembly.
The .NET environment does tell me which variables it has and which fields it has. Using the Assembly class and Reflection namespace, I can load up a file and see which fields and classes it has.
Then, using a program which searches memory (whether its native or not) I can find the physical location of the field (by using it value, filtering out etc) - like Cheat Engine does. It will give me the actual address of the field in the memory, which is accessed by the assembly made by the JIT.
I know that the MSIL does not contain information about the desired location of a specific field. I am also almost certain that the JIT will never optimize the code by removing any class.
I know that the .NET debugger is an actual class in the program which allows Visual Studio to interact with the internal information of an application. When the debugger is missing, Visual Studio cannot read or write to fields, nor can it inspect the application in any way.
Is there any way, without the use of Cheat Engine or similar tools to find the physical location of a field in a static (or of a specific instance) class in a running .NET process? Will the address be the same after each executing (such as in native program) ? May it differ only on different platforms or machines? How does the JIT decide where to place a field?
If I was unclear, I wish to do it without access to the code of the program, i.e externally by another process (like a debugger, but for programs compiled under release).
Next code inject Injector method in Paint.net and get MainForm fields.
NInject.exe
public static int Injector(string parameter)
{
try
{
var mainForm = Application.OpenForms.OfType<Form>().FirstOrDefault(form => form.GetType().FullName.EndsWith("MainForm"));
var builder = new StringBuilder();
builder.AppendFormat("process: {0}\r\n\r\n", Application.ExecutablePath);
builder.AppendFormat("type: {0}\r\n", mainForm.GetType().FullName);
foreach (var field in mainForm.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
builder.AppendFormat("field {0}: {1}\r\n", field.Name, field.GetValue(mainForm));
}
new Form()
{
Controls =
{
new TextBox
{
Text = builder.ToString(),
Multiline = true,
Dock = DockStyle.Fill
}
}
}
.ShowDialog();
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
return 0;
}
static void Main(string[] args)
{
var process = System.Diagnostics.Process.GetProcessesByName("PaintDotNet").FirstOrDefault();
var processHandle = OpenProcess(ProcessAccessFlags.All, false, process.Id);
var proxyPath = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "NInjector.dll");
var pathBytes = System.Text.Encoding.ASCII.GetBytes(proxyPath);
var remoteBuffer = VirtualAllocEx(processHandle, IntPtr.Zero, (uint)pathBytes.Length, AllocationType.Commit, MemoryProtection.ReadWrite);
WriteProcessMemory(process.Handle, remoteBuffer, pathBytes, (uint)pathBytes.Length, IntPtr.Zero);
var remoteThread = CreateRemoteThread(processHandle, IntPtr.Zero, 0, GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA") , remoteBuffer, 0, IntPtr.Zero);
WaitForSingleObject(remoteThread, unchecked((uint)-1));
CloseHandle(remoteThread);
}
NInjector.dll (native)
#include "MSCorEE.h"
#pragma comment (lib, "MSCorEE")
void StartTheDotNetRuntime()
{
MessageBox(0, L"Started", L"proxy", 0);
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hr = CorBindToRuntimeEx(
NULL, L"wks", 0, CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost, (PVOID*)&pClrHost);
hr = pClrHost->Start();
DWORD dwRet = 0;
hr = pClrHost->ExecuteInDefaultAppDomain(
L"bla-bla\\NInject.exe",
L"NInject.NInject_Program", L"Injector", L"MyParameter", &dwRet);
hr = pClrHost->Stop();
pClrHost->Release();
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
StartTheDotNetRuntime();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Output:
process: C:\Program Files\Paint.NET\PaintDotNet.exe
type: PaintDotNet.Dialogs.MainForm
field appWorkspace: PaintDotNet.Controls.AppWorkspace
field defaultButton: System.Windows.Forms.Button, Text:
field floaters: PaintDotNet.Dialogs.FloatingToolForm[]
field floaterOpacityTimer: [System.Windows.Forms.Timer], Interval: 25
field deferredInitializationTimer:
field components: System.ComponentModel.Container
field killAfterInit: False
field singleInstanceManager: PaintDotNet.SystemLayer.SingleInstanceManager
field queuedInstanceMessages: System.Collections.Generic.List`1[System.String]
field processingOpen: False
field scrollPosition: {X=0,Y=0}
I am trying to build a custom graph filter and I am having problems.
I am using the C# DirectShow.NET lib
I am reading a file with vc1 video and dts audio. i add the source filter to the graph, it works fine, i can also add the splitter filter (using lav splitter), but when i try to connect the file source filter to the lav splitter, it fails.
and it fails, because it doesn't find any input pin on the splitter ... i know that output pins can be dynamic, but the input pin should be there right ?
this is the code
_graphBuilder = (IGraphBuilder)new FilterGraph();
_dsRotEntry = new DsROTEntry((IFilterGraph)_graphBuilder);
LogInfo("Adding source filter...");
int hr = _graphBuilder.AddSourceFilter(_inputFilePath, _inputFilePath,
out _fileSource);
DsError.ThrowExceptionForHR(hr);
IPin pinSourceOut = DsFindPin.ByDirection(_fileSource, PinDirection.Output, 0);
if (pinSourceOut == null)
{
LogError("Unable to find source output pin");
};
IBaseFilter lavSplitter = CreateFilter(LAV_SPLITTER);
if (lavSplitter == null)
{
LogError("LAV Splitter not found");
};
hr = _graphBuilder.AddFilter(lavSplitter, "LAV Splitter");
DsError.ThrowExceptionForHR(hr);
bool result = TryConnectToAny(pinSourceOut, lavSplitter);
if (!result)
{
LogError("Unable to connect FileSource with LAV Splitter");
}
and
private bool TryConnectToAny(IPin sourcePin, IBaseFilter destinationFilter)
{
IEnumPins pinEnum;
int hr = destinationFilter.EnumPins(out pinEnum);
DsError.ThrowExceptionForHR(hr);
IPin[] pins = { null };
while (pinEnum.Next(pins.Length, pins, IntPtr.Zero) == 0)
{
int err = _graphBuilder.Connect(sourcePin, pins[0]);
if (err == 0)
return true;
Marshal.ReleaseComObject(pins[0]);
}
return false;
}
Most likely that input pin does exist, and what fails is the connection itself. err holds error code to possibly explain the problem. If it is unable to make the connection, TryConnectToAny returns false the same way as if there were no input pins on the filter at all.
I have been using the latest version of the WPFMediaKit. What I am trying to do is write a sample application that will use the Samplegrabber to capture the video frames of video files so I can have them as individual Bitmaps.
So far, I have had good luck with the following code when constructing and rendering my graph. However, when I use this code to play back a .wmv video file, when the samplegrabber is attached, it will play back jumpy or choppy. If I comment out the line where I add the samplegrabber filter, it works fine. Again, it works with the samplegrabber correctly with AVI/MPEG, etc.
protected virtual void OpenSource()
{
FrameCount = 0;
/* Make sure we clean up any remaining mess */
FreeResources();
if (m_sourceUri == null)
return;
string fileSource = m_sourceUri.OriginalString;
if (string.IsNullOrEmpty(fileSource))
return;
try
{
/* Creates the GraphBuilder COM object */
m_graph = new FilterGraphNoThread() as IGraphBuilder;
if (m_graph == null)
throw new Exception("Could not create a graph");
/* Add our prefered audio renderer */
InsertAudioRenderer(AudioRenderer);
var filterGraph = m_graph as IFilterGraph2;
if (filterGraph == null)
throw new Exception("Could not QueryInterface for the IFilterGraph2");
IBaseFilter renderer = CreateVideoMixingRenderer9(m_graph, 1);
IBaseFilter sourceFilter;
/* Have DirectShow find the correct source filter for the Uri */
var hr = filterGraph.AddSourceFilter(fileSource, fileSource, out sourceFilter);
DsError.ThrowExceptionForHR(hr);
/* We will want to enum all the pins on the source filter */
IEnumPins pinEnum;
hr = sourceFilter.EnumPins(out pinEnum);
DsError.ThrowExceptionForHR(hr);
IntPtr fetched = IntPtr.Zero;
IPin[] pins = { null };
/* Counter for how many pins successfully rendered */
int pinsRendered = 0;
m_sampleGrabber = (ISampleGrabber)new SampleGrabber();
SetupSampleGrabber(m_sampleGrabber);
hr = m_graph.AddFilter(m_sampleGrabber as IBaseFilter, "SampleGrabber");
DsError.ThrowExceptionForHR(hr);
/* Loop over each pin of the source filter */
while (pinEnum.Next(pins.Length, pins, fetched) == 0)
{
if (filterGraph.RenderEx(pins[0],
AMRenderExFlags.RenderToExistingRenderers,
IntPtr.Zero) >= 0)
pinsRendered++;
Marshal.ReleaseComObject(pins[0]);
}
Marshal.ReleaseComObject(pinEnum);
Marshal.ReleaseComObject(sourceFilter);
if (pinsRendered == 0)
throw new Exception("Could not render any streams from the source Uri");
/* Configure the graph in the base class */
SetupFilterGraph(m_graph);
HasVideo = true;
/* Sets the NaturalVideoWidth/Height */
//SetNativePixelSizes(renderer);
}
catch (Exception ex)
{
/* This exection will happen usually if the media does
* not exist or could not open due to not having the
* proper filters installed */
FreeResources();
/* Fire our failed event */
InvokeMediaFailed(new MediaFailedEventArgs(ex.Message, ex));
}
InvokeMediaOpened();
}
And:
private void SetupSampleGrabber(ISampleGrabber sampleGrabber)
{
FrameCount = 0;
var mediaType = new AMMediaType
{
majorType = MediaType.Video,
subType = MediaSubType.RGB24,
formatType = FormatType.VideoInfo
};
int hr = sampleGrabber.SetMediaType(mediaType);
DsUtils.FreeAMMediaType(mediaType);
DsError.ThrowExceptionForHR(hr);
hr = sampleGrabber.SetCallback(this, 0);
DsError.ThrowExceptionForHR(hr);
}
I have read a few things saying the the .wmv or .asf formats are asynchronous or something. I have attempted inserting a WMAsfReader to decode which works, but once it goes to the VMR9 it gives the same behavior. Also, I have gotten it to work correctly when I comment out the IBaseFilter renderer = CreateVideoMixingRenderer9(m_graph, 1); line and have filterGraph.Render(pins[0]); -- the only drawback is that now it renders in an Activemovie widow of its own instead of my control, however the samplegrabber functions correctly and without any skipping. So I am thinking the bug is in the VMR9 / samplegrabbing somewhere.
Any help? I am new to this.
Some decoders will use hardware acceleration using DXVA. This is implemented by negotiating a partly-decoded format, and passing this partly decoded data to the renderer to complete decoding and render. If you insert a sample grabber configured to RGB24 between the decoder and the renderer, you will disable hardware acceleration.
That, I'm sure, is the crux of the problem. The details are still a little vague, I'm afraid, such as why it works when you use the default VMR-7, but fails when you use VMR-9. I would guess that the decoder is trying to use dxva and failing, in the vmr-9 case, but has a reasonable software-only backup that works well in vmr-7.
I'm not familiar with the WPFMediaKit, but I would think the simplest solution is to replace the explicit vmr-9 creation with an explicit vmr-7 creation. That is, if the decoder works software-only with vmr-7, then use that and concentrate on fixing the window-reparenting issue.
It ended up the the code I have posted (which in itself was fairly shamelessly slightly modified by me from Jeremiah Morrill's WPFMediakit source code) was in fact adequate to render the .WMV files and be sample grabbed.
It seems the choppiness has something to do with running through the VS debugger, or VS2008 itself. After messing around with the XAML for a while in the visual editor, then running the app, I will have this choppy behavior introduced. Shutting down VS2008 seems to remedy it. :P
So not much of an answer, but at least a (annoying - restarting VS2008) fix when it crops up.