At work we make our own tablets. Some of the tablets have fingerprint biometrics, some don't. Sometimes a tech forgets to plug it in. I have yet to find a way to check if that device (or any for that matter) is present.
My first approach was to use the GUID for a biometric which is {53D29EF7-377C-4D14-864B-EB3A85769359}. I would search in the registry at hklm\system\currontcontrolset\control\class and check to see if that key is present.
That doesn't work because it seems that Windows 7 has that key present even if you've never had a biometric installed. It worked in XP, but I just tried again on a unit that used to have a biometric but I took it out and that key is still present.
The hardest part about this problem is that I have to work with Windows 7, 7 embedded, xp, and xp embedded.
Next idea was to use WMI, but I couldn't find the correct class to call to check if it is present.
I then found a biometric.dll but that only works in Windows 7.
Sometimes finding a common solution to a problem is not always easy. I'm doing this project in C# but iI'm willing to convert it to any language.
Any ideas on were I should start looking?
With the help of Joshua Drake who gave me an awesome link on how to solve my problem, those are my results:
The code that I am fixing to post is kind of specialized in that it looks for a specific GUID and only looks for the first one. I adapted it from the article about how to disable a device, although this code does not disable anything it merely checks for presence.
public static bool IsDevicePresent(string guid)
{
var info = IntPtr.Zero;
var NullGuid = new Guid(guid);
try
{
info = SetupDiGetClassDevsW(ref NullGuid,null,IntPtr.Zero,DIGCF_PRESENT);
CheckError("SetupDiGetClassDevs");
var devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
SetupDiEnumDeviceInfo(info,0,out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
}
catch
{
return false;
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
return true;
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(String.Format("Error disabling hardware device (Code {0}): {1}",code, message));
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsW([In] ref Guid ClassGuid,[MarshalAs(UnmanagedType.LPWStr)]string Enumerator,IntPtr parent,UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,UInt32 memberIndex,[Out] out SP_DEVINFO_DATA deviceInfoData);
//used to find device info from device manager
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved;
}
private const uint DIGCF_PRESENT = 2;
private const uint ERROR_INVALID_DATA = 13;
private const uint ERROR_NO_MORE_ITEMS = 259;
private const uint ERROR_ELEMENT_NOT_FOUND = 1168;
And here is a simple unit test to prove it works for first device
[Test]
public void TestDevicePresent()
{
var bluetoothClassGuid = "e0cbf06c-cd8b-4647-bb8a-263b43f0f974";
var biometricClassGuid = "53D29EF7-377C-4D14-864B-EB3A85769359";
var cdromdrivClassGiud = "4d36e965-e325-11ce-bfc1-08002be10318";
Assert.False(Native.IsDevicePresent(bluetoothClassGuid));
Assert.False(Native.IsDevicePresent(biometricClassGuid));
Assert.True(Native.IsDevicePresent(cdromdrivClassGiud));
}
Related
I would like to allow the user to connect to paired audio devices directly from the app instead of navigating to the bluetooth settings manually.
I am successfully listing all bluetooth devices using WinRT Apis:
var result = await DeviceInformation.FindAllAsync(BluetoothDevice.GetDeviceSelector());
// allow user to select a device
DeviceInfo d = await SelectDevice(result);
BluetoothDevice bluetoothDevice = await BluetoothDevice.FromIdAsync(d.Id);
As the WinRT-Apis do not expose any "Connect" interface (Remember, I want to connect the device as Windows would not communicate with it myself), I'm exploring using P/Invoke, so I use the following after reading this answer on superuser.com which suggests using BluetoothSetServiceState:
// Definitions
private const string bluetoothDll = "bthprops.cpl";
[DllImport(bluetoothDll, ExactSpelling = true, SetLastError = true)]
private static extern uint BluetoothSetServiceState(IntPtr hRadio, ref BLUETOOTH_DEVICE_INFO pbtdi, ref Guid pGuidService, uint dwServiceFlags);
[DllImport(bluetoothDll, SetLastError = true)]
private static extern IntPtr BluetoothFindFirstRadio(ref Bluetooth_Find_Radio_Params pbtfrp, out IntPtr phRadio);
[DllImport(bluetoothDll, SetLastError = true)]
private static extern bool BluetoothFindRadioClose(IntPtr findHandle);
[DllImport(bluetoothDll, SetLastError = true)]
private static extern uint BluetoothGetDeviceInfo(IntPtr hRadio, ref BLUETOOTH_DEVICE_INFO pbtdi);
private const uint BLUETOOTH_SERVICE_DISABLE = 0;
private const uint BLUETOOTH_SERVICE_ENABLE = 0x00000001;
// Code (using the bluetoothDevice obtained from the WinRT Api)
using(var pointer = GetRadioPointer())
{
BLUETOOTH_DEVICE_INFO deviceInfo = new BLUETOOTH_DEVICE_INFO
{
Address = bluetoothDevice.BluetoothAddress,
dwSize = (uint)Marshal.SizeOf<BLUETOOTH_DEVICE_INFO>()
};
uint result = BluetoothGetDeviceInfo(pointer.Handle, ref deviceInfo);
Guid serviceRef = InTheHand.Net.Bluetooth.BluetoothService.Handsfree;
result = BluetoothSetServiceState(pointer.Handle, ref deviceInfo, ref serviceRef, 1);
}
// I get the radio like this:
private RadioHandle GetRadioPointer()
{
Bluetooth_Find_Radio_Params pbtfrp = new Bluetooth_Find_Radio_Params();
pbtfrp.Initialize();
IntPtr findHandle = IntPtr.Zero;
try
{
findHandle = BluetoothFindFirstRadio(ref pbtfrp, out IntPtr phRadio);
return new RadioHandle(phRadio);
}
finally
{
if (findHandle != IntPtr.Zero)
{
BluetoothFindRadioClose(findHandle);
}
}
}
However, I cannot get it to work. BluetoothSetServiceState always returns 87, which is ERROR_INVALID_PARAMETER and nothing happens. Any idea on how to solve this? Using the command line tools referenced in the superuser-post, it works...
Thanks for your help.
I found it out by chance myself and it works now. Depending whether the service is already in the state (even if the device is disconnected), you will need to turn it off before. So turning it off and on again works:
BluetoothSetServiceState(pointer.Handle, ref deviceInfo, ref serviceRef, 0);
BluetoothSetServiceState(pointer.Handle, ref deviceInfo, ref serviceRef, 1);
So as it turns out, you can connect a device if you enumerate the services, turn all off and on again. Disconnecting works by turning all off. When the last one is off, Windows disconnects the device.
Why would you ever expect that the following line would work:
private const string bluetoothDll = "bthprops.cpl";
when MSDN's page states:
DLL: Bthprops.dll
?
We are writing a code to do on-demand scan of a file from C# using Windows Defender APIs.
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int WDStatus(out bool pfEnabled);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpManagerOpen(uint dwReserved, out IntPtr phMpHandle);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpScanStart(IntPtr hMpHandle, uint ScanType, uint dwScanOptions, IntPtr pScanResources, IntPtr pCallbackInfo, out IntPtr phScanHandle);
[DllImport(#"C:\Program Files\Windows Defender\MpClient.dll")]
public static extern int MpHandleClose(IntPtr hMpHandle);
private void DoDefenderScan_Click(object sender, EventArgs e)
{
try
{
bool pfEnabled;
int result = WDStatus(out pfEnabled); //Returns the defender status - It's working properly.
ErrorHandler.ThrowOnFailure(result, VSConstants.S_OK);
IntPtr phMpHandle;
uint dwReserved = 0;
IntPtr phScanHandle;
MpManagerOpen(dwReserved, out phMpHandle); //Opens Defender and returns the handle in phMpHandle.
tagMPRESOURCE_INFO mpResourceInfo = new tagMPRESOURCE_INFO();
mpResourceInfo.Path = "eicar.com";
mpResourceInfo.Scheme = "file";
mpResourceInfo.Class = IntPtr.Zero;
tagMPRESOURCE_INFO[] pResourceList = new tagMPRESOURCE_INFO[1];
pResourceList.SetValue(mpResourceInfo, 0);
tagMPSCAN_RESOURCES scanResource = new tagMPSCAN_RESOURCES();
scanResource.dwResourceCount = 1;
scanResource.pResourceList = pResourceList;
IntPtr resourcePointer = StructToPtr(scanResource);
result = MpScanStart(phMpHandle, 3, 0, resourcePointer, IntPtr.Zero, out phScanHandle); **//Getting Access violation exception here**.
MpHandleClose(phMpHandle);
MpHandleClose(phScanHandle);
Marshal.FreeHGlobal(resourcePointer);
}
catch (Exception)
{ }
}
And the structure is defined here.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct tagMPSCAN_RESOURCES
{
public uint dwResourceCount;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
public tagMPRESOURCE_INFO[] pResourceList;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct tagMPRESOURCE_INFO
{
[MarshalAs(UnmanagedType.LPWStr)]
public String Scheme;
[MarshalAs(UnmanagedType.LPWStr)]
public String Path;
public IntPtr Class;
}
public class MPRESOURCE_CLASS
{
public uint Value;
}
private static IntPtr StructToPtr(object obj)
{
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Marshal.StructureToPtr(obj, ptr, false);
return ptr;
}
The code is written based on the documentation available at
https://msdn.microsoft.com/en-us/library/vs/alm/dn920144(v=vs.85).aspx
We are getting this exception
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at
result = MpScanStart(phMpHandle, 3, 0, resourcePointer, IntPtr.Zero, out phScanHandle); **//Getting Access violation exception here**.
What could be the problem? Is the format of struct is correct?
P.S - No information about MPRESOURCE_CLASS is available in msdn.
I'm not sure, whether this line of code is correct.
mpResourceInfo.Class = IntPtr.Zero;
Update:
Quick scan is working fine with this code:
result = MpScanStart(phMpHandle, 1, 0, IntPtr.Zero, IntPtr.Zero, out phScanHandle);
Defender logs in the event viewer [ Applications and Services Logs-Microsoft-Windows-Windows Defender/Operational ] as
Windows Defender scan has started.
Scan ID:{CDC2AC0D-7648-4313-851C-4D8B7B5EB5CD}
Scan Type:AntiSpyware
Scan Parameters:Quick Scan
I couldn't identify the problem here. So I ended up with Antimalware Scan Interface (AMSI) available starting from Windows 10.
I have written a sample C# code here.
One thing I found is AMSI requires Windows defender/any antivirus to be turned on to verify the file passed to API. But triggering a scan through MpClient.dllwill trigger a defender scan even if defender is turned off.
Also ensure your project targets x64 platform.
public enum AMSI_RESULT
{
AMSI_RESULT_CLEAN = 0,
AMSI_RESULT_NOT_DETECTED = 1,
AMSI_RESULT_DETECTED = 32768
}
[DllImport("Amsi.dll", EntryPoint = "AmsiInitialize", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)]string appName, out IntPtr amsiContext);
[DllImport("Amsi.dll", EntryPoint = "AmsiUninitialize", CallingConvention = CallingConvention.StdCall)]
public static extern void AmsiUninitialize(IntPtr amsiContext);
[DllImport("Amsi.dll", EntryPoint = "AmsiOpenSession", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiOpenSession(IntPtr amsiContext, out IntPtr session);
[DllImport("Amsi.dll", EntryPoint = "AmsiCloseSession", CallingConvention = CallingConvention.StdCall)]
public static extern void AmsiCloseSession(IntPtr amsiContext, IntPtr session);
[DllImport("Amsi.dll", EntryPoint = "AmsiScanString", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiScanString(IntPtr amsiContext, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string #string, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string contentName, IntPtr session, out AMSI_RESULT result);
[DllImport("Amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)]
public static extern int AmsiScanBuffer(IntPtr amsiContext, [In] [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint length, [In()] [MarshalAs(UnmanagedType.LPWStr)] string contentName, IntPtr session, out AMSI_RESULT result);
//This method apparently exists on MSDN but not in AMSI.dll (version 4.9.10586.0)
[DllImport("Amsi.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern bool AmsiResultIsMalware(AMSI_RESULT result);
private void CallAntimalwareScanInterface()
{
IntPtr amsiContext;
IntPtr session;
AMSI_RESULT result = 0;
int returnValue;
returnValue = AmsiInitialize("VirusScanAPI", out amsiContext); //appName is the name of the application consuming the Amsi.dll. Here my project name is VirusScanAPI.
returnValue = AmsiOpenSession(amsiContext, out session);
returnValue = AmsiScanString(amsiContext, #"X5O!P%#AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*", "EICAR", session, out result); //I've used EICAR test string.
AmsiCloseSession(amsiContext, session);
AmsiUninitialize(amsiContext);
}
I've been searching about problem and I've read this as one of the possible causes:
"You often see differences between debug and release builds because
debug builds contain extra metadata to assist in debugging."
here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4f48c152-68cd-45ec-a11e-baa7de7f79c3/attempted-to-read-or-write-protected-memory?forum=csharpgeneral
Also you should check this answer to "Is it possible to catch an access violation exception in .NET?" and the further details that are explained in the article Handling Corrupted State Exceptions in MSDN magazine
...
So, according to that answers and articles I'd try:
1st Double check signatures and COM interop thunks for all unmanaged code to verify that they're correct.
2nd Set Visual Studio Debugger to bypass this exception:
Tools menu ->Options -> Debugging -> General -> Uncheck this option "Suppress JIT optimization on module load"
3rd Try-Catch the exception
(note: if you are using .Net 4 then in App.config, within the tag modify runtime to include legacyCorruptedStateExceptionsPolicy enabled="true"like:
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
</runtime>
)
In addition, here, I've found that some .net framework versions (latest comment point to 4.6.1 in one of the answer's comments) has a bug related with this exception and the solution, in the past, has been upgrading the framework.
Also, in the one of that answers I've read:
Hi There are two possible reasons.
1.We have un-managed code and we are calling it from managed code. that is preventing to run this code. try running these commands and
restart your pc
cmd: netsh winsock reset
open cmd.exe and run command "netsh winsock reset catalog"
2.Anti-virus is considering un-managed code as harmful and restricting to run this code disable anti-virus and then check
I'd like to know if some of these approaches helps you to solve your issue.
I really hope this helps.
KR,
Juan
You may use Antimalware Scan Interface to check file for malware.
The Antimalware Scan Interface (AMSI) is a generic interface standard that allows applications and services to integrate with any antimalware product present on a machine. It provides enhanced malware protection for users and their data, applications, and workloads.
It's available starting from Windows 10.
Windows Defender comes with CLI tool 'MpCmdRun' - it's not a full-sized antivirus app, but an API interface to the actual Windows Defender that's always (?) running in background.
Saving to a temporary file via Path.GetTempFileName() and then running a scan like this
MpCmdRun.exe -Scan -ScanType 3 -File "c:\path\to\temp\file" -DisableRemediation
works fine even in an ASP.NET (Core) app, that runs under app-pool identity
I've actually written a small (40 lines of code) C# helper that does everything for you (saves temp file, runs a scan, cleans up)
https://github.com/jitbit/WinDefender/blob/main/WinDefender.cs
I want to get user-friendly names of sound inputs with this code, but it can give me only first 32 chars of name, but I want it whole.
[DllImport("winmm.dll", SetLastError = true)]
static extern uint waveInGetNumDevs();
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveInGetDevCaps(uint hwo, ref WAVEOUTCAPS pwoc, uint cbwoc);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS
{
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szPname;
public uint dwFormats;
public ushort wChannels;
public ushort wReserved1;
public uint dwSupport;
}
public static string[] GetSoundDevices()
{
uint devices = waveInGetNumDevs();
string[] result = new string[devices];
WAVEOUTCAPS caps = new WAVEOUTCAPS();
using (StreamWriter sw = new StreamWriter("appdata/audio/name"))
{
for (uint i = 0; i < devices; i++)
{
waveInGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps));
result[i] = caps.szPname;
sw.WriteLine(caps.szPname);
}
return result;
}
}
I need this names of sound inputs:
but this code give me only this:
Thank you guys!
You may have to use the management interface:
ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(
"SELECT * FROM Win32_SoundDevice");
ManagementObjectCollection objCollection = objSearcher.Get();
foreach (ManagementObject obj in objCollection)
{
foreach (PropertyData property in obj.Properties)
{
Console.Out.WriteLine(String.Format("{0}:{1}", property.Name, property.Value));
}
}
Source: How to enumerate audio out devices in c#
You can use WASAPI to get full name of sound devices. It's only limit is that it is not available in Windows XP or older OS.
You need to use IMMDeviceEnumerator::EnumAudioEndpoints method to achieve the goal, but since the library is COM, you need to wrap it to be able to use it in C#.
A sample project is available on CodeProject.
I also couldn't find a way to get full name using WinMM library and finally end up writing a C++ wrapper around WASAPI and used that wrapper in C# through pinvoke!
Why screenshots in foreign and totally not understandable language?
There you go. English screenshot 1
You can copy it, that is what the second one means.
The number stands for the usb bus that is used.
When changing the plug keep that in mind.
I'm using .NET 3.5.
I'm experiencing a memory leak when creating a reference to a X509Certificate. I'm using ANTS profiler to analyse the results and the private bytes are increasing while the bytes in the heap remain static (indicating it's a memory leak due to unmanaged code).
I'm using the CRYPT32 dll to manage the certificates. I open a reference to cert store to get a store handle which is an internal pointer (intptr). I then use this store handler to find the cert in store (saved locally). Once I have the cert I close the cert store and return the cert to the calling program. I'm using the flag 0 in the certclosestore which keeps open resources open after the close store is called. I believe this is what is causing the memory leak as it's mentioned here:
http://msdn.microsoft.com/en-us/library/ms937035.aspx
However, when I change the close flag to:
CertCloseStore(storeHandle, 2)
This is supposed to free up the allocated resources. However it just causes the service to bomb out.
The application works on the sense that it is verify the certs etc. The only problem is that the memory use is slowly creeping up and the service needs to be restarted every week or so. Any ideas or thoughts would be greatly appreciated.
public static X509Certificate CreateFromRegistry(string certificateIdent)
{
X509Certificate certificate = null;
IntPtr storeHandle = CertificateStore.CertOpenStore(CERT_STORE_PROV_SYSTEM,
0, 0,CERT_SYSTEM_STORE_LOCAL_MACHINE, "MY");;
certificate = new X509Certificate(CertificateStore.
FindCertInStore(certificateIdent, storeHandle));
CertificateStore.CertCloseStore(storeHandle, 0);
return certificate;
}
public class CertificateStore
{
const int CERT_STORE_PROV_SYSTEM = 10;
private static int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);
const uint PKCS_7_ASN_ENCODING = 0x00010000;
const uint X509_ASN_ENCODING = 0x00000001;
const uint CERT_FIND_SUBJECT_STR = 0x00080007;
const uint CERT_FIND_ISSUER_STR = 0x00080004;
static uint MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
[DllImport("CRYPT32", EntryPoint = "CertOpenStore",
CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CertOpenStore(
int storeProvider, int encodingType,
int hcryptProv, int flags, string pvPara);
[DllImport("CRYPT32", EntryPoint = "CertCloseStore",
CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CertCloseStore(
IntPtr storeProvider,
int flags);
}
public static X509Certificate FindCertInStore
(string trustedRootIssuerName, IntPtr storeHandle)
{
IntPtr hCertCntxt;
X509Certificate theActualCertificate = null;
if (storeHandle != IntPtr.Zero)
{
hCertCntxt = CertFindCertificateInStore(
storeHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_ISSUER_STR,
trustedRootIssuerName,
IntPtr.Zero);
if (hCertCntxt != IntPtr.Zero)
{
theActualCertificate = new X509Certificate(hCertCntxt);
}
}
return theActualCertificate;
}
Well, you are leaking CRYPT32 resources of course. One immediate candidate I see in your snippet is the return value of CertFindCertificateInStore(). It must be released by an explicit call to CertFreeCertificateContext(), I don't see one.
The X509Certification(IntPtr) constructor is not documented well, it doesn't describe how long the context needs to be valid. I see it calling an internal method named X509Utils._DuplicateCertContext() so very high odds that you immediately can call the release function after creating the object.
Do check the rest of your code with a fine-toothed comb and triple check that all handles and pointers you get from CRYPT32 are released.
I'd like to invoke the user's screen saver if such is defined, in a Windows environment.
I know it can be done using pure C++ code (and then the wrapping in C# is pretty simple), as suggested here.
Still, for curiosity, I'd like to know if such task can be accomplished by purely managed code using the dot net framework (version 2.0 and above), without p/invoke and without visiting the C++ side (which, in turn, can use windows API pretty easily).
I've an idea, I'm not sure how consistently this would work, so you'd need to research a bit I think, but hopefully it's enough to get you started.
A screen saver is just an executable, and the registry stores the location of this executable in HKCU\Control Panel\Desktop\SCRNSAVE.EXE
On my copy of Vista, this worked for me:
RegistryKey screenSaverKey = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop");
if (screenSaverKey != null)
{
string screenSaverFilePath = screenSaverKey.GetValue("SCRNSAVE.EXE", string.Empty).ToString();
if (!string.IsNullOrEmpty(screenSaverFilePath) && File.Exists(screenSaverFilePath))
{
Process screenSaverProcess = Process.Start(new ProcessStartInfo(screenSaverFilePath, "/s")); // "/s" for full-screen mode
screenSaverProcess.WaitForExit(); // Wait for the screensaver to be dismissed by the user
}
}
I think having a .Net library function that does this is highly unlikely - I'm not aware of any. A quick search returned this Code Project tutorial which contains an example of a managed wrapper which you mentioned in your question.
P/invoke exists so that you're able to access OS-specific features, of which screen savers are an example.
I'm not sure you can use completely managed code to do this.
This uses Windows API but is still very simple: Launch System Screensaver from C# Windows Form
Working on any version of windows...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace HQ.Util.Unmanaged
{
public class ScreenSaverHelper
{
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
// Signatures for unmanaged calls
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
// Constants
private const int SPI_GETSCREENSAVERACTIVE = 16;
private const int SPI_SETSCREENSAVERACTIVE = 17;
private const int SPI_GETSCREENSAVERTIMEOUT = 14;
private const int SPI_SETSCREENSAVERTIMEOUT = 15;
private const int SPI_GETSCREENSAVERRUNNING = 114;
private const int SPIF_SENDWININICHANGE = 2;
private const uint DESKTOP_WRITEOBJECTS = 0x0080;
private const uint DESKTOP_READOBJECTS = 0x0001;
private const int WM_CLOSE = 16;
public const uint WM_SYSCOMMAND = 0x112;
public const uint SC_SCREENSAVE = 0xF140;
public enum SpecialHandles
{
HWND_DESKTOP = 0x0,
HWND_BROADCAST = 0xFFFF
}
public static void TurnScreenSaver(bool turnOn = true)
{
// Does not work on Windows 7
// int nullVar = 0;
// SystemParametersInfo(SPI_SETSCREENSAVERACTIVE, 1, ref nullVar, SPIF_SENDWININICHANGE);
// Does not work on Windows 7, can't broadcast. Also not needed.
// SendMessage(new IntPtr((int) SpecialHandles.HWND_BROADCAST), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, (IntPtr)SC_SCREENSAVE, (IntPtr)0);
}
}
}