Calling Windows API function to set Auto Gain Control - c#

https://learn.microsoft.com/en-us/windows/desktop/api/devicetopology/nf-devicetopology-iaudioautogaincontrol-setenabled
I'm trying to do a C# wrapper/call to set this low level device setting value. It basically disables/enables AGC microphone setting. I've found this link but i'm not sure how to wire it up to look like this:
// http://msdn.microsoft.com/en-us/library/dd757304%28VS.85%29.aspx
[DllImport("winmm.dll", CharSet = CharSet.Ansi)]
public static extern Int32 mixerGetNumDevs();
Essentially, I want to disable (uncheck) this enhancement

This answer has discussed "How to use interface pointer exported by C++ DLL in C#". But, I think what you want more is as below.
You don't need to use the winapi. The auto gain control functionality is implemented in the AudioQualityEnhancer class, which is a mediahandler. It uses the AutoGainControl bool property to enable or disable the gain control feature.
Automatic Gain Control example in C#:
using System;
using Ozeki.Media;
namespace Automatic_Gain_Control
{
class Program
{
static Microphone microphone;
static Speaker speaker;
static MediaConnector connector;
static AudioQualityEnhancer audioProcessor;
static void Main(string[] args)
{
microphone = Microphone.GetDefaultDevice();
speaker = Speaker.GetDefaultDevice();
connector = new MediaConnector();
audioProcessor = new AudioQualityEnhancer();
audioProcessor.AutoGainControl = true;//enable
audioProcessor.GainSpeed = 12;
audioProcessor.MaxGain = 30;
connector.Connect(microphone, audioProcessor);
connector.Connect(audioProcessor, speaker);
microphone.Start();
speaker.Start();
Console.ReadLine();
}
}
}
UPDATE:
To disable the AGC with interface simply, You can encapsulate all interface procedures in your own DLL functions, like:
HRESULT EnableAGC()
{
CComPtr<IMMDeviceEnumerator> m_pIMMEnumerator;
CComPtr<IAudioVolumeLevel> m_pMicBoost;
CComPtr<IAudioAutoGainControl> m_pAGC;
HRESULT hr = S_OK;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, (void**)&m_pIMMEnumerator);
if (FAILED(hr)) return hr;
CComPtr<IMMDevice> pIMMDeivce = NULL;
std::wstring strEndPointID;//String of the Device ID
if (strEndPointID.empty())
{
hr = m_pIMMEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pIMMDeivce);
}
else
{
hr = m_pIMMEnumerator->GetDevice(strEndPointID.c_str(), &pIMMDeivce);
}
if (FAILED(hr)) return hr;
CComPtr<IDeviceTopology> pTopo = NULL;
hr = pIMMDeivce->Activate(IID_IDeviceTopology, CLSCTX_INPROC_SERVER, 0, (void**)&pTopo);
if (FAILED(hr)) return hr;
CComPtr<IConnector> pConn = NULL;
hr = pTopo->GetConnector(0, &pConn);
if (FAILED(hr)) return hr;
CComPtr<IConnector> pConnNext = NULL;
hr = pConn->GetConnectedTo(&pConnNext);
if (FAILED(hr)) return hr;
CComPtr<IPart> pPart = NULL;
hr = pConnNext->QueryInterface(IID_IPart, (void**)&pPart);
if (FAILED(hr)) return hr;
hr = pPart->Activate(CLSCTX_INPROC_SERVER, IID_IAudioAutoGainControl, (void**)&m_pAGC);
if (SUCCEEDED(hr) && m_pAGC)
{
//Hardware Supports Microphone AGC
BOOL bEnable = TRUE;
hr = m_pAGC->SetEnabled(bEnable, NULL);
}
else
{
//Hardware not Supports Microphone AGC
}
return hr;
}

Here is a pure C# console app sample, equivalent of #Drake's C/C++ code. I've written it using code from an open source project called DirectN that defines thousands of c# Windows interop types (DirectX, etc.), including Code Audio API, etc.
class Program
{
static void Main(string[] args)
{
// using DirectN
var enumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
// or call GetDevice(...) with an id
enumerator.GetDefaultAudioEndpoint(
__MIDL___MIDL_itf_mmdeviceapi_0000_0000_0001.eCapture,
__MIDL___MIDL_itf_mmdeviceapi_0000_0000_0002.eConsole,
out var device).ThrowOnError();
const int CLSCTX_ALL = 23;
device.Activate(typeof(IDeviceTopology).GUID, CLSCTX_ALL, null, out var iface).ThrowOnError();
var topology = (IDeviceTopology)iface;
topology.GetConnector(0, out var connector).ThrowOnError();
var part = (IPart)connector;
if (part.Activate(CLSCTX_ALL, typeof(IAudioAutoGainControl).GUID, out iface).IsError)
{
Console.WriteLine("AGC not supported.");
return;
}
var control = (IAudioAutoGainControl)iface;
control.SetEnabled(true, IntPtr.Zero);
}
[ComImport]
[Guid("bcde0395-e52f-467c-8e3d-c4579291692e")] // CLSID_MMDeviceEnumerator
class MMDeviceEnumerator
{
}
}
You can use either the DirectN's nuget package, or copy to your project only the needed .cs files (and their dependencies). Here, you would need the following:
HRESULT.cs
HRESULTS.cs
IAudioAutoGainControl.cs
IAudioVolumeLevel.cs
IConnector.cs
IControlChangeNotify.cs
IControlInterface.cs
IDeviceTopology.cs
IMMDevice.cs
IMMDeviceCollection.cs
IMMDeviceEnumerator.cs
IMMNotificationClient.cs
IPart.cs
IPartsList.cs
IPerChannelDbLevel.cs
ISubunit.cs
PROPERTYKEY.cs
PropertyType.cs
PropVariant.cs
_tagpropertykey.cs
__MIDL___MIDL_itf_devicetopology_0000_0000_0011.cs
__MIDL___MIDL_itf_devicetopology_0000_0000_0012.cs
__MIDL___MIDL_itf_devicetopology_0000_0000_0013.cs
__MIDL___MIDL_itf_mmdeviceapi_0000_0000_0001.cs
__MIDL___MIDL_itf_mmdeviceapi_0000_0000_0002.cs

Related

c# IDsObjectPickerCredentials fail

I have to set credentials to Active-Directory-Object-Picker (IDsObjectPicker) on c#.
But I can't force the IDsObjectPickerCredentials to work.
I've made same on c++ (msdn example) and it works good.
I used "headers" from here ComInterop.cs and StructsFlags.cs
Please tell me what I'm doing wrong. How to call IDsObjectPickerCredentials.SetCredentials
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Tulpep.ActiveDirectoryObjectPicker;
namespace cred
{
class Program
{
static void Main(string[] args)
{
string szTargetComputer = #"10.0.9.115";
string szUser = #"TST\test";
string szPassword = #"123qazWSX";
DSObjectPicker picker = new DSObjectPicker();
IDsObjectPicker ipicker = (IDsObjectPicker)picker;
int res = InitObjectPicker(ipicker, szTargetComputer);
if (res == (int)HRESULT.S_OK)
{
try
{
// !!! HERE !!!
IDsObjectPickerCredentials cred = (ipicker as IDsObjectPickerCredentials);
res = cred.SetCredentials(szUser, szPassword);
// c++ like variant
// res = InitCredentials(ipicker, szUser, szPassword);
if (res != (int)HRESULT.S_OK) Console.WriteLine("SetCredentials Fail : 0x{0}", res.ToString("X4")); // On Win32 I get 0x80070057 - looks like E_INVALIDARG
IntPtr hwndParent = Process.GetCurrentProcess().MainWindowHandle;
IDataObject dataObj = null;
int hresult = ipicker.InvokeDialog(hwndParent, out dataObj);
Console.WriteLine(hresult == (int)HRESULT.S_OK ? "OK" : "Cancel");
Console.ReadKey();
}
finally
{
Marshal.ReleaseComObject(ipicker);
}
}
}
[ComImport, Guid("E2D3EC9B-D041-445A-8F16-4748DE8FB1CF"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDsObjectPickerCredentials
{
[PreserveSig()]
int SetCredentials(
[In, MarshalAs(UnmanagedType.LPWStr)] string szUserName,
[In, MarshalAs(UnmanagedType.LPWStr)] string szPassword);
}
static int InitObjectPicker(IDsObjectPicker ipicker, string szTargetComputer)
{
int res = (int)HRESULT.S_FALSE;
DSOP_SCOPE_INIT_INFO[] aScopeInit = new DSOP_SCOPE_INIT_INFO[1];
DSOP_INIT_INFO InitInfo = new DSOP_INIT_INFO();
aScopeInit[0].cbSize = (uint)Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO));
aScopeInit[0].flType =
DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN |
DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN;
aScopeInit[0].FilterFlags.Uplevel.flBothModes =
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_COMPUTERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_USERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_BUILTIN_GROUPS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_INCLUDE_ADVANCED_VIEW;
aScopeInit[0].FilterFlags.flDownlevel =
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_COMPUTERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_USERS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_BUILTIN_GROUPS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS |
DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_INCLUDE_ADVANCED_VIEW;
IntPtr refScopeInitInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)) * aScopeInit.Length);
try
{
// Marshal structs to pointers
for (int index = 0; index < aScopeInit.Length; index++)
{
Marshal.StructureToPtr(aScopeInit[index], (IntPtr)((int)refScopeInitInfo + index * Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO))), false);
}
InitInfo.cbSize = (uint)Marshal.SizeOf(typeof(DSOP_INIT_INFO));
InitInfo.cDsScopeInfos = (uint)aScopeInit.Length; //sizeof(aScopeInit)/sizeof(DSOP_SCOPE_INIT_INFO);
InitInfo.aDsScopeInfos = refScopeInitInfo;
InitInfo.flOptions = DSOP_INIT_INFO_FLAGS.DSOP_FLAG_MULTISELECT;
InitInfo.pwzTargetComputer = szTargetComputer;
res = ipicker.Initialize(ref InitInfo);
}
finally
{
Marshal.FreeHGlobal(refScopeInitInfo);
}
return res;
}
static int InitCredentials(IDsObjectPicker ipicker, string User, string Password)
{
IntPtr ptr = IntPtr.Zero;
Guid IID_IDsObjectPickerCredentials = new Guid("E2D3EC9B-D041-445A-8F16-4748DE8FB1CF");
IntPtr pIUnk = Marshal.GetIUnknownForObject(ipicker);
int hr = Marshal.QueryInterface(pIUnk, ref IID_IDsObjectPickerCredentials, out ptr);
if (hr == HRESULT.S_OK)
{
try
{
IDsObjectPickerCredentials cred = (IDsObjectPickerCredentials)Marshal.GetObjectForIUnknown(ptr);
hr = cred.SetCredentials(User, Password);
if (hr != HRESULT.S_OK)
{
System.Diagnostics.Debugger.Break(); // Fail
return (int)HRESULT.S_FALSE;
}
}
catch (Exception ex)
{
return (int)HRESULT.S_FALSE;
}
finally
{
Marshal.Release(ptr);
}
}
return (int)HRESULT.S_OK;
}
}
}
The IDsObjectPickerCredentials interface derives from the IDsObjectPicker. Your sample actually calls the Initialize method with a username and a password instead of SetCredentials.
Here is a correct declaration:
[ComImport, Guid("e2d3ec9b-d041-445a-8f16-4748de8fb1cf"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IDsObjectPickerCredentials
{
[PreserveSig]
int Initialize(ref DSOP_INIT_INFO pInitInfo);
[PreserveSig]
int InvokeDialog(IntPtr HWND, out IDataObject lpDataObject);
[PreserveSig]
int SetCredentials(string userName, string password);
}
Please note that instead of deriving from the IDsObjectPicker, the code replicates its methods, in the same order. This is necessary for .NET COM Interop.
You don't need that manual QueryInterface/Release calls, var cred = (IDsObjectPickerCredentials)iobjectpicker; is enough.
Also I found that the SetCredentials should be called before the Initialize method.

VBA Code to communicate with a Human Interface Device (HID)

I have recently bought a RFID R/W device, and I would like to send and receive data from it to a VBA application that I have already developed. I have been looking for a solution, but I was not able to find any.
I would like to know if there are any VBA instructions for receiving and sending data to a HID. If not, which would be the simplest way to communicate with my device? The code would be very simple, just writing and reading hex code. I could manage an application in vb or C# too.
The driver has been correctly installed and their specs. are in the link: http://www.securakey.com/PRODUCTS/RFID_PRODUCTS/ET4AUS_D_AUM_7656.pdf
Thank you all.
It's been a long time since I've done VBA programming but you will be able to use the same Windows API calls that C# or VB.Net use. Here is some example code for enumerating Hid devices and connecting. You will need to translate to VBA. This is a link on Windows API in VBA https://riptutorial.com/vba/example/31727/windows-api---dedicated-module--1-of-2-.
Here is Windows API code for enumerating Hid devices:
https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Device.Net/Windows/WindowsDeviceFactoryBase.cs
var deviceDefinitions = new Collection<DeviceDefinition>();
var spDeviceInterfaceData = new SpDeviceInterfaceData();
var spDeviceInfoData = new SpDeviceInfoData();
var spDeviceInterfaceDetailData = new SpDeviceInterfaceDetailData();
spDeviceInterfaceData.CbSize = (uint)Marshal.SizeOf(spDeviceInterfaceData);
spDeviceInfoData.CbSize = (uint)Marshal.SizeOf(spDeviceInfoData);
var guidString = ClassGuid.ToString();
var copyOfClassGuid = new Guid(guidString);
var i = APICalls.SetupDiGetClassDevs(ref copyOfClassGuid, IntPtr.Zero, IntPtr.Zero, APICalls.DigcfDeviceinterface | APICalls.DigcfPresent);
if (IntPtr.Size == 8)
{
spDeviceInterfaceDetailData.CbSize = 8;
}
else
{
spDeviceInterfaceDetailData.CbSize = 4 + Marshal.SystemDefaultCharSize;
}
var x = -1;
while (true)
{
x++;
var isSuccess = APICalls.SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref copyOfClassGuid, (uint)x, ref spDeviceInterfaceData);
if (!isSuccess)
{
var errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS)
{
break;
}
throw new Exception($"Could not enumerate devices. Error code: {errorCode}");
}
isSuccess = APICalls.SetupDiGetDeviceInterfaceDetail(i, ref spDeviceInterfaceData, ref spDeviceInterfaceDetailData, 256, out _, ref spDeviceInfoData);
WindowsDeviceBase.HandleError(isSuccess, "Could not get device interface detail");
//Note this is a bit nasty but we can filter Vid and Pid this way I think...
var vendorHex = vendorId?.ToString("X").ToLower().PadLeft(4, '0');
var productIdHex = productId?.ToString("X").ToLower().PadLeft(4, '0');
if (vendorId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(vendorHex)) continue;
if (productId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(productIdHex)) continue;
var deviceDefinition = GetDeviceDefinition(spDeviceInterfaceDetailData.DevicePath);
deviceDefinitions.Add(deviceDefinition);
}
APICalls.SetupDiDestroyDeviceInfoList(i);
return deviceDefinitions;
Here is connection code:
public bool Initialize()
{
Dispose();
if (DeviceInformation == null)
{
throw new WindowsHidException($"{nameof(DeviceInformation)} must be specified before {nameof(Initialize)} can be called.");
}
var pointerToPreParsedData = new IntPtr();
_HidCollectionCapabilities = new HidCollectionCapabilities();
var pointerToBuffer = Marshal.AllocHGlobal(126);
_ReadSafeFileHandle = APICalls.CreateFile(DeviceInformation.DeviceId, APICalls.GenericRead | APICalls.GenericWrite, APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, 0, IntPtr.Zero);
_WriteSafeFileHandle = APICalls.CreateFile(DeviceInformation.DeviceId, APICalls.GenericRead | APICalls.GenericWrite, APICalls.FileShareRead | APICalls.FileShareWrite, IntPtr.Zero, APICalls.OpenExisting, 0, IntPtr.Zero);
if (!HidAPICalls.HidD_GetPreparsedData(_ReadSafeFileHandle, ref pointerToPreParsedData))
{
throw new Exception("Could not get pre parsed data");
}
var getCapsResult = HidAPICalls.HidP_GetCaps(pointerToPreParsedData, ref _HidCollectionCapabilities);
//TODO: Deal with issues here
Marshal.FreeHGlobal(pointerToBuffer);
var preparsedDataResult = HidAPICalls.HidD_FreePreparsedData(ref pointerToPreParsedData);
//TODO: Deal with issues here
if (_ReadSafeFileHandle.IsInvalid)
{
return false;
}
_ReadFileStream = new FileStream(_ReadSafeFileHandle, FileAccess.ReadWrite, _HidCollectionCapabilities.OutputReportByteLength, false);
_WriteFileStream = new FileStream(_WriteSafeFileHandle, FileAccess.ReadWrite, _HidCollectionCapabilities.InputReportByteLength, false);
IsInitialized = true;
RaiseConnected();
return true;
}

Silently print HTML from WPF WebBrowser

I have to silently print HTML document from WebBrowser in WPF. I have done it NOT silently by that code:
mshtml.IHTMLDocument2 doc = WebBrowser1.Document as mshtml.IHTMLDocument2;
doc.execCommand("Print", true, null);
Now I want to skip the print dialog.
Please help.
For silent printing with WPF WebBrowser, below code worked for me:
private void PrintCurrentPage()
{
// document must be loaded for this to work
IOleServiceProvider sp = WebBrowser1.Document as IOleServiceProvider;
if (sp != null)
{
Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
const int OLECMDID_PRINT = 6;
const int OLECMDEXECOPT_DONTPROMPTUSER = 2;
dynamic wb; // should be of IWebBrowser2 type
sp.QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, out wb);
if (wb != null)
{
// this will send to the default printer
wb.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, null, null);
}
}
}
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IOleServiceProvider
{
[PreserveSig]
int QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid guidService, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
}
Note: answer was inspired by this SO q & a

How do I use OpenFileDialog to select a folder?

I was going to use the following project: https://github.com/scottwis/OpenFileOrFolderDialog
However, there's a problem: it uses the GetOpenFileName function and OPENFILENAME structure. OPENFILENAME has the member named templateID, which is the identifier for dialog template. And the project contains the res1.rc file and the templated dialog init, too. But I couldn't figure out how to attach this file to my C# project.
Is there a better way to use an OpenFileDialog to select folders?
Basically you need the FolderBrowserDialog class:
Prompts the user to select a folder. This class cannot be inherited.
Example:
using(var fbd = new FolderBrowserDialog())
{
DialogResult result = fbd.ShowDialog();
if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
{
string[] files = Directory.GetFiles(fbd.SelectedPath);
System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
}
}
If you work in WPF you have to add the reference to System.Windows.Forms.
you also have to add using System.IO for Directory class
As a note for future users who would like to avoid using FolderBrowserDialog, Microsoft once released an API called the WindowsAPICodePack that had a helpful dialog called CommonOpenFileDialog, that could be set into a IsFolderPicker mode. The API is available from Microsoft as a NuGet package.
This is all I needed to install and use the CommonOpenFileDialog. (NuGet handled the dependencies)
Install-Package Microsoft.WindowsAPICodePack-Shell
For the include line:
using Microsoft.WindowsAPICodePack.Dialogs;
Usage:
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
MessageBox.Show("You selected: " + dialog.FileName);
}
There is a hackish solution using OpenFileDialog where ValidateNames and CheckFileExists are both set to false and FileName is given a mock value to indicate that a directory is selected.
I say hack because it is confusing to users about how to select a folder. They need to be in the desired folder and then just press Open while file name says "Folder Selection."
This is based on Select file or folder from the same dialog by Denis Stankovski.
OpenFileDialog folderBrowser = new OpenFileDialog();
// Set validate names and check file exists to false otherwise windows will
// not let you select "Folder Selection."
folderBrowser.ValidateNames = false;
folderBrowser.CheckFileExists = false;
folderBrowser.CheckPathExists = true;
// Always default to Folder Selection.
folderBrowser.FileName = "Folder Selection.";
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
string folderPath = Path.GetDirectoryName(folderBrowser.FileName);
// ...
}
Here is a pure C# version, nuget-free, that should work with all versions of .NET (including .NET Core, .NET 5, WPF, Winforms, etc.) and uses Windows Vista (and higher) IFileDialog interface with the FOS_PICKFOLDERS options so it has the nice folder picker Windows standard UI.
I have also added WPF's Window type support but this is optional, the WPF-marked lines can just be removed.
usage:
var dlg = new FolderPicker();
dlg.InputPath = #"c:\windows\system32";
if (dlg.ShowDialog() == true)
{
MessageBox.Show(dlg.ResultPath);
}
code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows; // for WPF support
using System.Windows.Interop; // for WPF support
public class FolderPicker
{
public virtual string ResultPath { get; protected set; }
public virtual string ResultName { get; protected set; }
public virtual string InputPath { get; set; }
public virtual bool ForceFileSystem { get; set; }
public virtual string Title { get; set; }
public virtual string OkButtonLabel { get; set; }
public virtual string FileNameLabel { get; set; }
protected virtual int SetOptions(int options)
{
if (ForceFileSystem)
{
options |= (int)FOS.FOS_FORCEFILESYSTEM;
}
return options;
}
// for WPF support
public bool? ShowDialog(Window owner = null, bool throwOnError = false)
{
owner ??= Application.Current.MainWindow;
return ShowDialog(owner != null ? new WindowInteropHelper(owner).Handle : IntPtr.Zero, throwOnError);
}
// for all .NET
public virtual bool? ShowDialog(IntPtr owner, bool throwOnError = false)
{
var dialog = (IFileOpenDialog)new FileOpenDialog();
if (!string.IsNullOrEmpty(InputPath))
{
if (CheckHr(SHCreateItemFromParsingName(InputPath, null, typeof(IShellItem).GUID, out var item), throwOnError) != 0)
return null;
dialog.SetFolder(item);
}
var options = FOS.FOS_PICKFOLDERS;
options = (FOS)SetOptions((int)options);
dialog.SetOptions(options);
if (Title != null)
{
dialog.SetTitle(Title);
}
if (OkButtonLabel != null)
{
dialog.SetOkButtonLabel(OkButtonLabel);
}
if (FileNameLabel != null)
{
dialog.SetFileName(FileNameLabel);
}
if (owner == IntPtr.Zero)
{
owner = Process.GetCurrentProcess().MainWindowHandle;
if (owner == IntPtr.Zero)
{
owner = GetDesktopWindow();
}
}
var hr = dialog.Show(owner);
if (hr == ERROR_CANCELLED)
return null;
if (CheckHr(hr, throwOnError) != 0)
return null;
if (CheckHr(dialog.GetResult(out var result), throwOnError) != 0)
return null;
if (CheckHr(result.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var path), throwOnError) != 0)
return null;
ResultPath = path;
if (CheckHr(result.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out path), false) == 0)
{
ResultName = path;
}
return true;
}
private static int CheckHr(int hr, bool throwOnError)
{
if (hr != 0)
{
if (throwOnError)
Marshal.ThrowExceptionForHR(hr);
}
return hr;
}
[DllImport("shell32")]
private static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);
[DllImport("user32")]
private static extern IntPtr GetDesktopWindow();
#pragma warning disable IDE1006 // Naming Styles
private const int ERROR_CANCELLED = unchecked((int)0x800704C7);
#pragma warning restore IDE1006 // Naming Styles
[ComImport, Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] // CLSID_FileOpenDialog
private class FileOpenDialog
{
}
[ComImport, Guid("42f85136-db7e-439c-85f1-e4075d135fc8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileOpenDialog
{
[PreserveSig] int Show(IntPtr parent); // IModalWindow
[PreserveSig] int SetFileTypes(); // not fully defined
[PreserveSig] int SetFileTypeIndex(int iFileType);
[PreserveSig] int GetFileTypeIndex(out int piFileType);
[PreserveSig] int Advise(); // not fully defined
[PreserveSig] int Unadvise();
[PreserveSig] int SetOptions(FOS fos);
[PreserveSig] int GetOptions(out FOS pfos);
[PreserveSig] int SetDefaultFolder(IShellItem psi);
[PreserveSig] int SetFolder(IShellItem psi);
[PreserveSig] int GetFolder(out IShellItem ppsi);
[PreserveSig] int GetCurrentSelection(out IShellItem ppsi);
[PreserveSig] int SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName);
[PreserveSig] int GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
[PreserveSig] int SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
[PreserveSig] int SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText);
[PreserveSig] int SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
[PreserveSig] int GetResult(out IShellItem ppsi);
[PreserveSig] int AddPlace(IShellItem psi, int alignment);
[PreserveSig] int SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
[PreserveSig] int Close(int hr);
[PreserveSig] int SetClientGuid(); // not fully defined
[PreserveSig] int ClearClientData();
[PreserveSig] int SetFilter([MarshalAs(UnmanagedType.IUnknown)] object pFilter);
[PreserveSig] int GetResults([MarshalAs(UnmanagedType.IUnknown)] out object ppenum);
[PreserveSig] int GetSelectedItems([MarshalAs(UnmanagedType.IUnknown)] out object ppsai);
}
[ComImport, Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellItem
{
[PreserveSig] int BindToHandler(); // not fully defined
[PreserveSig] int GetParent(); // not fully defined
[PreserveSig] int GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
[PreserveSig] int GetAttributes(); // not fully defined
[PreserveSig] int Compare(); // not fully defined
}
#pragma warning disable CA1712 // Do not prefix enum values with type name
private enum SIGDN : uint
{
SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
SIGDN_FILESYSPATH = 0x80058000,
SIGDN_NORMALDISPLAY = 0,
SIGDN_PARENTRELATIVE = 0x80080001,
SIGDN_PARENTRELATIVEEDITING = 0x80031001,
SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
SIGDN_PARENTRELATIVEPARSING = 0x80018001,
SIGDN_URL = 0x80068000
}
[Flags]
private enum FOS
{
FOS_OVERWRITEPROMPT = 0x2,
FOS_STRICTFILETYPES = 0x4,
FOS_NOCHANGEDIR = 0x8,
FOS_PICKFOLDERS = 0x20,
FOS_FORCEFILESYSTEM = 0x40,
FOS_ALLNONSTORAGEITEMS = 0x80,
FOS_NOVALIDATE = 0x100,
FOS_ALLOWMULTISELECT = 0x200,
FOS_PATHMUSTEXIST = 0x800,
FOS_FILEMUSTEXIST = 0x1000,
FOS_CREATEPROMPT = 0x2000,
FOS_SHAREAWARE = 0x4000,
FOS_NOREADONLYRETURN = 0x8000,
FOS_NOTESTFILECREATE = 0x10000,
FOS_HIDEMRUPLACES = 0x20000,
FOS_HIDEPINNEDPLACES = 0x40000,
FOS_NODEREFERENCELINKS = 0x100000,
FOS_OKBUTTONNEEDSINTERACTION = 0x200000,
FOS_DONTADDTORECENT = 0x2000000,
FOS_FORCESHOWHIDDEN = 0x10000000,
FOS_DEFAULTNOMINIMODE = 0x20000000,
FOS_FORCEPREVIEWPANEON = 0x40000000,
FOS_SUPPORTSTREAMABLEITEMS = unchecked((int)0x80000000)
}
#pragma warning restore CA1712 // Do not prefix enum values with type name
}
result:
Strange that so much answers/votes, but no one add the following code as an answer:
using (var opnDlg = new OpenFileDialog()) //ANY dialog
{
//opnDlg.Filter = "Png Files (*.png)|*.png";
//opnDlg.Filter = "Excel Files (*.xls, *.xlsx)|*.xls;*.xlsx|CSV Files (*.csv)|*.csv"
if (opnDlg.ShowDialog() == DialogResult.OK)
{
//opnDlg.SelectedPath -- your result
}
}
Sounds to me like you're just after the FolderBrowserDialog.
Here is another solution, that has all the source available in a single, simple ZIP file.
It presents the OpenFileDialog with additional windows flags that makes it work like the Windows 7+ Folder Selection dialog.
Per the website, it is public domain: "There’s no license as such as you are free to take and do with the code what you will."
Article: .NET Win 7-style folder select dialog (http://www.lyquidity.com/devblog/?p=136)
Source code: http://s3downloads.lyquidity.com/FolderSelectDialog/FolderSelectDialog.zip
Archive.org links:
Article: https://web.archive.org/web/20180823181552/http://www.lyquidity.com/devblog/?p=136
Source code: https://web.archive.org/web/20180823181632/http://s3downloads.lyquidity.com/FolderSelectDialog/FolderSelectDialog.zip
Take a look at the Ookii Dialogs libraries which has an implementation of a folder browser dialog for Windows Forms and WPF respectively.
Ookii.Dialogs.WinForms
https://github.com/augustoproiete/ookii-dialogs-winforms
Ookii.Dialogs.Wpf
https://github.com/augustoproiete/ookii-dialogs-wpf
Im new to C# and came across this thread just now.
This might be of interest to those that are new to the language as well.
Change design of FolderBrowserDialog
The answer given by Simon Mourier would be the best answer considering the OP question. It does not involve NuGET package so there will not be any dependency issue in the future for selecting folder method.
If you encountered error related to "...is not available in C# 7.3", just add <LangVersion>8.0</LangVersion> to your .csproj (testing with Visual Studio does not produce any error when build and run)
If you can't change the project language then just replace owner
??= Application.Current.MainWindow
with
owner = owner ?? Application.Current.MainWindow
This is how I use the folder browser dialog in Small Visual Basic. This code solves the non-selected initial folder issue and also selects the folder from the clipboard or the registry (if any), and if the folder is deleted, it goes up through parents until selecting an existing folder. This makes using the dialog very comfortable.
Note that I am sending 4 tabs because I show the Create new folder button, but if you hide it, use two tabs only.
Public Shared Function OpenFolderDialog(initialFolder As String) As String
Dim folder = GetInitialFolder(initialFolder)
If folder = "" Then folder = GetInitialFolder(System.Windows.Clipboard.GetText())
If folder = "" Then folder = GetInitialFolder(GetSetting("sVB", "OpenFolder", "LastFolder", ""))
Dim th As New Threading.Thread(
Sub()
Threading.Thread.Sleep(300)
System.Windows.Forms.SendKeys.SendWait("{TAB}{TAB}{TAB}{TAB}{RIGHT}")
End Sub)
th.Start()
Try
Dim dlg As New System.Windows.Forms.FolderBrowserDialog With {
.Description = "Select a folder:",
.SelectedPath = folder
}
If dlg.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
SaveSetting("sVB", "OpenFolder", "LastFolder", dlg.SelectedPath)
OpenFolderDialog = dlg.SelectedPath
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Function
Private Shared Function GetInitialFolder(folder As String) As String
Try
If folder <> "" Then
If IO.File.Exists(folder) Then
folder = Path.GetDirectoryName(folder)
Else
Do
If Directory.GetDirectoryRoot(folder) = folder OrElse Directory.Exists(folder) Then
Exit Do
End If
folder = Path.GetDirectoryName(folder)
Loop
End If
End If
Catch
folder = ""
End Try
Return folder
End Function
this should be the most obvious and straight forward way
using (var dialog = new System.Windows.Forms.FolderBrowserDialog())
{
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
if(result == System.Windows.Forms.DialogResult.OK)
{
selectedFolder = dialog.SelectedPath;
}
}

How to access an audio stream using DirectShow.NET C#

What I would like to do is to pass an arbitrary audio file to a DirectShow filtergraph and receive a (PCM audio) stream object in the end using .NET 3.5 C# and DirectShow.NET. I would like to reach the point that I can just say:
Stream OpenFile(string filename) {...}
and
stream.Read(...)
I have been reading up on DirectShow for a couple of days and think I have started to grasp the idea of filters and filtergraphs. I found examples (to file / to device) how to play audio or write it to a file, but cannot seem to find the solution for a Stream object. Is this even possible? Could you point me in the right direction in case I missed something, please?
Best,
Hauke
I would like to share my solution to my own problem with you (my focus was on the exotic bwf file format. hence the name.):
using System;
using System.Collections.Generic;
using System.Text;
using DirectShowLib;
using System.Runtime.InteropServices;
using System.IO;
namespace ConvertBWF2WAV
{
public class BWF2WavConverter : ISampleGrabberCB
{
IFilterGraph2 gb = null;
ICaptureGraphBuilder2 icgb = null;
IBaseFilter ibfSrcFile = null;
DsROTEntry m_rot = null;
IMediaControl m_mediaCtrl = null;
ISampleGrabber sg = null;
public BWF2WavConverter()
{
// Initialize
int hr;
icgb = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
gb = (IFilterGraph2) new FilterGraph();
sg = (ISampleGrabber)new SampleGrabber();
#if DEBUG
m_rot = new DsROTEntry(gb);
#endif
hr = icgb.SetFiltergraph(gb);
DsError.ThrowExceptionForHR(hr);
}
public void reset()
{
gb = null;
icgb = null;
ibfSrcFile = null;
m_rot = null;
m_mediaCtrl = null;
}
public void convert(object obj)
{
string[] pair = obj as string[];
string srcfile = pair[0];
string targetfile = pair[1];
int hr;
ibfSrcFile = (IBaseFilter)new AsyncReader();
hr = gb.AddFilter(ibfSrcFile, "Reader");
DsError.ThrowExceptionForHR(hr);
IFileSourceFilter ifileSource = (IFileSourceFilter)ibfSrcFile;
hr = ifileSource.Load(srcfile, null);
DsError.ThrowExceptionForHR(hr);
// the guid is the one from ffdshow
Type fftype = Type.GetTypeFromCLSID(new Guid("0F40E1E5-4F79-4988-B1A9-CC98794E6B55"));
object ffdshow = Activator.CreateInstance(fftype);
hr = gb.AddFilter((IBaseFilter)ffdshow, "ffdshow");
DsError.ThrowExceptionForHR(hr);
// the guid is the one from the WAV Dest sample in the SDK
Type type = Type.GetTypeFromCLSID(new Guid("3C78B8E2-6C4D-11d1-ADE2-0000F8754B99"));
object wavedest = Activator.CreateInstance(type);
hr = gb.AddFilter((IBaseFilter)wavedest, "WAV Dest");
DsError.ThrowExceptionForHR(hr);
// manually tell the graph builder to try to hook up the pin that is left
IPin pWaveDestOut = null;
hr = icgb.FindPin(wavedest, PinDirection.Output, null, null, true, 0, out pWaveDestOut);
DsError.ThrowExceptionForHR(hr);
// render step 1
hr = icgb.RenderStream(null, null, ibfSrcFile, (IBaseFilter)ffdshow, (IBaseFilter)wavedest);
DsError.ThrowExceptionForHR(hr);
// Configure the sample grabber
IBaseFilter baseGrabFlt = sg as IBaseFilter;
ConfigSampleGrabber(sg);
IPin pGrabberIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
IPin pGrabberOut = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
hr = gb.AddFilter((IBaseFilter)sg, "SampleGrabber");
DsError.ThrowExceptionForHR(hr);
AMMediaType mediatype = new AMMediaType();
sg.GetConnectedMediaType(mediatype);
hr = gb.Connect(pWaveDestOut, pGrabberIn);
DsError.ThrowExceptionForHR(hr);
// file writer
FileWriter file_writer = new FileWriter();
IFileSinkFilter fs = (IFileSinkFilter)file_writer;
fs.SetFileName(targetfile, null);
hr = gb.AddFilter((DirectShowLib.IBaseFilter)file_writer, "File Writer");
DsError.ThrowExceptionForHR(hr);
// render step 2
AMMediaType mediatype2 = new AMMediaType();
pWaveDestOut.ConnectionMediaType(mediatype2);
gb.Render(pGrabberOut);
// alternatively to the file writer use the NullRenderer() to just discard the rest
// assign control
m_mediaCtrl = gb as IMediaControl;
// run
hr = m_mediaCtrl.Run();
DsError.ThrowExceptionForHR(hr);
}
//
// configure the SampleGrabber filter of the graph
//
void ConfigSampleGrabber(ISampleGrabber sampGrabber)
{
AMMediaType media;
// set the media type. works with "stream" somehow...
media = new AMMediaType();
media.majorType = MediaType.Stream;
//media.subType = MediaSubType.WAVE;
//media.formatType = FormatType.WaveEx;
// that's the call to the ISampleGrabber interface
sg.SetMediaType(media);
DsUtils.FreeAMMediaType(media);
media = null;
// set BufferCB as the desired Callback function
sg.SetCallback(this, 1);
}
public int SampleCB(double a, IMediaSample b)
{
return 0;
}
/// <summary>
/// Called on each SampleGrabber hit.
/// </summary>
/// <param name="SampleTime">Starting time of the sample, in seconds.</param>
/// <param name="pBuffer">Pointer to a buffer that contains the sample data.</param>
/// <param name="BufferLen">Length of the buffer pointed to by pBuffer, in bytes.</param>
/// <returns></returns>
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
byte[] buffer = new byte[BufferLen];
Marshal.Copy(pBuffer, buffer, 0, BufferLen);
using (BinaryWriter binWriter = new BinaryWriter(File.Open(#"C:\directshowoutput.pcm", FileMode.Append)))
{
binWriter.Write(buffer);
}
return 0;
}
}
}
This (AVILibrary Wrapper) may lead you to a solution, it's not DirectSound based (which I get the feeling is very much biased to interfacing your code with the playback hardware) but could be the answer.
Another approach can be found here.
How about NAudio ? http://www.codeplex.com/naudio
It has a stream implementation.

Categories