Related
In xaml I have simple MediaElement mediaElement control with video.mp4 source.
Video duration is about 4,3 seconds.
When mediaElement.MediaOpened is fired, I am trying to get video duration.
private void mediaElement_MediaOpened(object sender, RoutedEventArgs e)
{
double TotalTime = mediaElement.NaturalDuration.TimeSpan.TotalMilliseconds;
Console.WriteLine("Total video time: {0}", TotalTime);
}
Console returns "Total video time: 4000".
How do I get exact video duration?
I was having this same problem in my project and was unable to find a WPF, built-in way to get an accurate millisecond count. So, we have to go to external libraries.
I chose to go with Javi.MediaInfo for a few reasons, some of which were that it was an MIT-licensed wrapper for MediaInfoLib (BSD 2-clause). Some of the other wrappers on Nuget look like they are MIT/similar licensed, but if you look at the source, they are using GPL code, as is the case with MP-MediaInfo. To use Javi.MediaInfo, I had to also install MediaInfo.Native v19.4.0 (native MediaInfo.dll) from NuGet.
I ran into an issue with Javi.MediaInfo when trying to use the Options functionality. Turns out that the MediaInfoWrapper class uses old function declarations that don't match the latest MediaInfo.dll. To fix this, I made some adjustments to MediaInfo.cs and MediaInfoWrapper.cs (see below). The latter I obtained using the 19.4.0 DLL code download. I also placed the entire Javi.MediaInfo source code in my source code to avoid issues with the former targeting .NET Standard 2 and my project not doing so (it gave me duplicate DLL reference warnings if I used the NuGet).
MediaInfoWrapper.cs
/* Copyright (c) MediaArea.net SARL. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the License.html file in the root of the source tree.
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Microsoft Visual C# wrapper for MediaInfo Library
// See MediaInfo.h for help
//
// To make it working, you must put MediaInfo.Dll
// in the executable folder
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// from https://mediaarea.net/en/MediaInfo/Download/Windows -- for version 19.4.0; modified slightly
using System;
using System.Runtime.InteropServices;
#pragma warning disable 1591 // Disable XML documentation warnings
namespace MediaInfoLib
{
public enum StreamKind
{
General,
Video,
Audio,
Text,
Other,
Image,
Menu,
}
public enum InfoKind
{
Name,
Text,
Measure,
Options,
NameText,
MeasureText,
Info,
HowTo
}
public enum InfoOptions
{
ShowInInform,
Support,
ShowInSupported,
TypeOfValue
}
public enum InfoFileOptions
{
FileOption_Nothing = 0x00,
FileOption_NoRecursive = 0x01,
FileOption_CloseAll = 0x02,
FileOption_Max = 0x04
};
public enum Status
{
None = 0x00,
Accepted = 0x01,
Filled = 0x02,
Updated = 0x04,
Finalized = 0x08,
}
public class MediaInfo : IDisposable
{
//Import of DLL functions. DO NOT USE until you know what you do (MediaInfo DLL do NOT use CoTaskMemAlloc to allocate memory)
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_New();
[DllImport("MediaInfo.dll")]
private static extern void MediaInfo_Delete(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open(IntPtr Handle, [MarshalAs(UnmanagedType.LPWStr)] string FileName);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open(IntPtr Handle, IntPtr FileName);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Init(IntPtr Handle, Int64 File_Size, Int64 File_Offset);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open(IntPtr Handle, Int64 File_Size, Int64 File_Offset);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Continue(IntPtr Handle, IntPtr Buffer, IntPtr Buffer_Size);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open_Buffer_Continue(IntPtr Handle, Int64 File_Size, byte[] Buffer, IntPtr Buffer_Size);
[DllImport("MediaInfo.dll")]
private static extern Int64 MediaInfo_Open_Buffer_Continue_GoTo_Get(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern Int64 MediaInfoA_Open_Buffer_Continue_GoTo_Get(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Open_Buffer_Finalize(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Open_Buffer_Finalize(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern void MediaInfo_Close(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Inform(IntPtr Handle, IntPtr Reserved);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Inform(IntPtr Handle, IntPtr Reserved);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_GetI(IntPtr Handle, IntPtr StreamKind, IntPtr StreamNumber, IntPtr Parameter, IntPtr KindOfInfo);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_GetI(IntPtr Handle, IntPtr StreamKind, IntPtr StreamNumber, IntPtr Parameter, IntPtr KindOfInfo);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Get(IntPtr Handle, IntPtr StreamKind, IntPtr StreamNumber, [MarshalAs(UnmanagedType.LPWStr)] string Parameter, IntPtr KindOfInfo, IntPtr KindOfSearch);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Get(IntPtr Handle, IntPtr StreamKind, IntPtr StreamNumber, IntPtr Parameter, IntPtr KindOfInfo, IntPtr KindOfSearch);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Option(IntPtr Handle, [MarshalAs(UnmanagedType.LPWStr)] string Option, [MarshalAs(UnmanagedType.LPWStr)] string Value);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoA_Option(IntPtr Handle, IntPtr Option, IntPtr Value);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_State_Get(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_Count_Get(IntPtr Handle, IntPtr StreamKind, IntPtr StreamNumber);
public string[] InfoParametersCSV { get; }
//MediaInfo class
public MediaInfo()
{
try
{
Handle = MediaInfo_New();
this.InfoParametersCSV = this.Option("Info_Parameters_CSV").Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
}
catch
{
Handle = (IntPtr)0;
}
if (Environment.OSVersion.ToString().IndexOf("Windows") == -1)
MustUseAnsi = true;
else
MustUseAnsi = false;
}
~MediaInfo() { if (Handle == (IntPtr)0) return; MediaInfo_Delete(Handle); }
public int Open(String FileName)
{
if (Handle == (IntPtr)0)
return 0;
if (MustUseAnsi)
{
IntPtr FileName_Ptr = Marshal.StringToHGlobalAnsi(FileName);
int ToReturn = (int)MediaInfoA_Open(Handle, FileName_Ptr);
Marshal.FreeHGlobal(FileName_Ptr);
return ToReturn;
}
else
return (int)MediaInfo_Open(Handle, FileName);
}
public int Open_Buffer_Init(Int64 File_Size, Int64 File_Offset)
{
if (Handle == (IntPtr)0) return 0; return (int)MediaInfo_Open_Buffer_Init(Handle, File_Size, File_Offset);
}
public int Open_Buffer_Continue(IntPtr Buffer, IntPtr Buffer_Size)
{
if (Handle == (IntPtr)0) return 0; return (int)MediaInfo_Open_Buffer_Continue(Handle, Buffer, Buffer_Size);
}
public Int64 Open_Buffer_Continue_GoTo_Get()
{
if (Handle == (IntPtr)0) return 0; return (Int64)MediaInfo_Open_Buffer_Continue_GoTo_Get(Handle);
}
public int Open_Buffer_Finalize()
{
if (Handle == (IntPtr)0) return 0; return (int)MediaInfo_Open_Buffer_Finalize(Handle);
}
public void Close() { if (Handle == (IntPtr)0) return; MediaInfo_Close(Handle); }
public String Inform()
{
if (Handle == (IntPtr)0)
return "Unable to load MediaInfo library";
if (MustUseAnsi)
return Marshal.PtrToStringAnsi(MediaInfoA_Inform(Handle, (IntPtr)0));
else
return Marshal.PtrToStringUni(MediaInfo_Inform(Handle, (IntPtr)0));
}
public String Get(StreamKind StreamKind, int StreamNumber, String Parameter, InfoKind KindOfInfo, InfoKind KindOfSearch)
{
if (Handle == (IntPtr)0)
return "Unable to load MediaInfo library";
if (MustUseAnsi)
{
IntPtr Parameter_Ptr = Marshal.StringToHGlobalAnsi(Parameter);
String ToReturn = Marshal.PtrToStringAnsi(MediaInfoA_Get(Handle, (IntPtr)StreamKind, (IntPtr)StreamNumber, Parameter_Ptr, (IntPtr)KindOfInfo, (IntPtr)KindOfSearch));
Marshal.FreeHGlobal(Parameter_Ptr);
return ToReturn;
}
else
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, (IntPtr)StreamKind, (IntPtr)StreamNumber, Parameter, (IntPtr)KindOfInfo, (IntPtr)KindOfSearch));
}
public String Get(StreamKind StreamKind, int StreamNumber, int Parameter, InfoKind KindOfInfo)
{
if (Handle == (IntPtr)0)
return "Unable to load MediaInfo library";
if (MustUseAnsi)
return Marshal.PtrToStringAnsi(MediaInfoA_GetI(Handle, (IntPtr)StreamKind, (IntPtr)StreamNumber, (IntPtr)Parameter, (IntPtr)KindOfInfo));
else
return Marshal.PtrToStringUni(MediaInfo_GetI(Handle, (IntPtr)StreamKind, (IntPtr)StreamNumber, (IntPtr)Parameter, (IntPtr)KindOfInfo));
}
public String Option(String Option, String Value)
{
if (Handle == (IntPtr)0)
return "Unable to load MediaInfo library";
if (MustUseAnsi)
{
IntPtr Option_Ptr = Marshal.StringToHGlobalAnsi(Option);
IntPtr Value_Ptr = Marshal.StringToHGlobalAnsi(Value);
String ToReturn = Marshal.PtrToStringAnsi(MediaInfoA_Option(Handle, Option_Ptr, Value_Ptr));
Marshal.FreeHGlobal(Option_Ptr);
Marshal.FreeHGlobal(Value_Ptr);
return ToReturn;
}
else
return Marshal.PtrToStringUni(MediaInfo_Option(Handle, Option, Value));
}
public int State_Get() { if (Handle == (IntPtr)0) return 0; return (int)MediaInfo_State_Get(Handle); }
public int Count_Get(StreamKind StreamKind, int StreamNumber) { if (Handle == (IntPtr)0) return 0; return (int)MediaInfo_Count_Get(Handle, (IntPtr)StreamKind, (IntPtr)StreamNumber); }
private IntPtr Handle;
private bool MustUseAnsi;
//Default values, if you know how to set default values in C#, say me
public String Get(StreamKind StreamKind, int StreamNumber, String Parameter, InfoKind KindOfInfo) { return Get(StreamKind, StreamNumber, Parameter, KindOfInfo, InfoKind.Name); }
public String Get(StreamKind StreamKind, int StreamNumber, String Parameter) { return Get(StreamKind, StreamNumber, Parameter, InfoKind.Text, InfoKind.Name); }
public String Get(StreamKind StreamKind, int StreamNumber, int Parameter) { return Get(StreamKind, StreamNumber, Parameter, InfoKind.Text); }
public String Option(String Option_) { return Option(Option_, ""); }
public int Count_Get(StreamKind StreamKind) { return Count_Get(StreamKind, -1); }
private bool isdisposed = false;
public void Dispose()
{
if (!this.isdisposed)
{
// Clean-up Unmanaged
if (Handle != (IntPtr)0)
{
MediaInfo_Delete(Handle);
Handle = IntPtr.Zero;
}
this.isdisposed = true;
}
}
}
public class MediaInfoList
{
//Import of DLL functions. DO NOT USE until you know what you do (MediaInfo DLL do NOT use CoTaskMemAlloc to allocate memory)
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_New();
[DllImport("MediaInfo.dll")]
private static extern void MediaInfoList_Delete(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_Open(IntPtr Handle, [MarshalAs(UnmanagedType.LPWStr)] string FileName, IntPtr Options);
[DllImport("MediaInfo.dll")]
private static extern void MediaInfoList_Close(IntPtr Handle, IntPtr FilePos);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_Inform(IntPtr Handle, IntPtr FilePos, IntPtr Reserved);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_GetI(IntPtr Handle, IntPtr FilePos, IntPtr StreamKind, IntPtr StreamNumber, IntPtr Parameter, IntPtr KindOfInfo);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_Get(IntPtr Handle, IntPtr FilePos, IntPtr StreamKind, IntPtr StreamNumber, [MarshalAs(UnmanagedType.LPWStr)] string Parameter, IntPtr KindOfInfo, IntPtr KindOfSearch);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_Option(IntPtr Handle, [MarshalAs(UnmanagedType.LPWStr)] string Option, [MarshalAs(UnmanagedType.LPWStr)] string Value);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_State_Get(IntPtr Handle);
[DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfoList_Count_Get(IntPtr Handle, IntPtr FilePos, IntPtr StreamKind, IntPtr StreamNumber);
//MediaInfo class
public MediaInfoList() { Handle = MediaInfoList_New(); }
~MediaInfoList() { MediaInfoList_Delete(Handle); }
public int Open(String FileName, InfoFileOptions Options) { return (int)MediaInfoList_Open(Handle, FileName, (IntPtr)Options); }
public void Close(int FilePos) { MediaInfoList_Close(Handle, (IntPtr)FilePos); }
public String Inform(int FilePos) { return Marshal.PtrToStringUni(MediaInfoList_Inform(Handle, (IntPtr)FilePos, (IntPtr)0)); }
public String Get(int FilePos, StreamKind StreamKind, int StreamNumber, String Parameter, InfoKind KindOfInfo, InfoKind KindOfSearch) { return Marshal.PtrToStringUni(MediaInfoList_Get(Handle, (IntPtr)FilePos, (IntPtr)StreamKind, (IntPtr)StreamNumber, Parameter, (IntPtr)KindOfInfo, (IntPtr)KindOfSearch)); }
public String Get(int FilePos, StreamKind StreamKind, int StreamNumber, int Parameter, InfoKind KindOfInfo) { return Marshal.PtrToStringUni(MediaInfoList_GetI(Handle, (IntPtr)FilePos, (IntPtr)StreamKind, (IntPtr)StreamNumber, (IntPtr)Parameter, (IntPtr)KindOfInfo)); }
public String Option(String Option, String Value) { return Marshal.PtrToStringUni(MediaInfoList_Option(Handle, Option, Value)); }
public int State_Get() { return (int)MediaInfoList_State_Get(Handle); }
public int Count_Get(int FilePos, StreamKind StreamKind, int StreamNumber) { return (int)MediaInfoList_Count_Get(Handle, (IntPtr)FilePos, (IntPtr)StreamKind, (IntPtr)StreamNumber); }
private IntPtr Handle;
//Default values, if you know how to set default values in C#, say me
public void Open(String FileName) { Open(FileName, 0); }
public void Close() { Close(-1); }
public String Get(int FilePos, StreamKind StreamKind, int StreamNumber, String Parameter, InfoKind KindOfInfo) { return Get(FilePos, StreamKind, StreamNumber, Parameter, KindOfInfo, InfoKind.Name); }
public String Get(int FilePos, StreamKind StreamKind, int StreamNumber, String Parameter) { return Get(FilePos, StreamKind, StreamNumber, Parameter, InfoKind.Text, InfoKind.Name); }
public String Get(int FilePos, StreamKind StreamKind, int StreamNumber, int Parameter) { return Get(FilePos, StreamKind, StreamNumber, Parameter, InfoKind.Text); }
public String Option(String Option_) { return Option(Option_, ""); }
public int Count_Get(int FilePos, StreamKind StreamKind) { return Count_Get(FilePos, StreamKind, -1); }
}
} //NameSpace
SO is not letting me paste the entire MediaInfo.cs file here due to character limits. To make it use the new wrapper, add using MediaInfoWrapper = MediaInfoLib.MediaInfo; to the top using statements and change occurances of new MediaInfoWrapper(someStringVar) to new MediaInfoWrapper(). After that, you should be set!
I need to use win32 NetLocalGroupGetMembers in C#. I found and tested three solutions. All three fail with an FatalExecutionEngineError. The framework is .net 4.0
Here is a full example:
Reference to the api:
static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static int NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public int lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
}
calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
int Resume;
IntPtr bufPtr;
string groupName = "Administrators";
NetworkAPI.NetLocalGroupGetMembers(null, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
// EntriesRead has the correct quantity of members of the group, so the group is found
for (int i = 0; i < EntriesRead; i++)
{
// --------------------------------------------------
// ==> here the FatalExecutionEngineError happens:
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//
// --------------------------------------------------
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}
I see the following errors:
The resume handle is a pointer. Use ref IntPtr resumehandle for that parameter, and pass IntPtr.Zero on the first call. Or if you don't need to use a resume handle declare the parameter as IntPtr resumehandle and pass IntPtr.Zero. Consult the function documentation on MSDN for the full details.
The lgrmi1_sid member of the struct is a pointer. Declare it as such: public IntPtr lgrmi1_sid.
Casting an IntPtr to an int will lead to pointer truncation on 64 bit. Either use arithmetic directly on the IntPtr, or for older C# versions cast to long. The former is better, like so: iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));.
You do not check the return value for errors.
Fix those errors and your program will run correctly.
For the sake of completeness, here is the code how to pinvoke
NetLocalGroupGetMembers.
I corrected the code as David suggested. There is also a suggestion from Martin Liversage which I didn't implement. But it maybe usefull.
If you like it, please do not upvode this answer but upvote Davids answer, who found the errors.
Reference to the api:
public static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static uint NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out IntPtr resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public IntPtr lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
// documented in MSDN
public const uint ERROR_ACCESS_DENIED = 0x0000005;
public const uint ERROR_MORE_DATA = 0x00000EA;
public const uint ERROR_NO_SUCH_ALIAS = 0x0000560;
public const uint NERR_InvalidComputer = 0x000092F;
// found by testing
public const uint NERR_GroupNotFound = 0x00008AC;
public const uint SERVER_UNAVAILABLE = 0x0006BA;
}
Calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
IntPtr Resume;
IntPtr bufPtr;
string groupName = "Administratoren";
string computerName = null; // null for the local machine
uint retVal = NetworkAPI.NetLocalGroupGetMembers(computerName, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if(retVal != 0)
{
if (retVal == NetworkAPI.ERROR_ACCESS_DENIED) { Console.WriteLine("Access denied"); return; }
if (retVal == NetworkAPI.ERROR_MORE_DATA) { Console.WriteLine("ERROR_MORE_DATA"); return; }
if (retVal == NetworkAPI.ERROR_NO_SUCH_ALIAS) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.NERR_InvalidComputer) { Console.WriteLine("Invalid computer name"); return; }
if (retVal == NetworkAPI.NERR_GroupNotFound) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.SERVER_UNAVAILABLE) { Console.WriteLine("Server unavailable"); return; }
Console.WriteLine("Unexpected NET_API_STATUS: " + retVal.ToString());
return;
}
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
for (int i = 0; i < EntriesRead; i++)
{
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//x64 safe
iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}
I need to programmatically detect whether my computer (Windows 7 / 8) supports wake timers. So far I have done the following:
Guid activePowerScheme = GetActivePowerSchemeGuid();
IntPtr ptrActiveGuid = IntPtr.Zero;
uint buffSize = 0;
uint res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, IntPtr.Zero, ref buffSize);
if (res == 0)
{
IntPtr ptrName = IntPtr.Zero;
try
{
ptrName = Marshal.AllocHGlobal((int)buffSize);
res = PowerReadACValue(IntPtr.Zero, ref activePowerScheme, ref ApplicationConstants.SLEEPGUID, ref ApplicationConstants.WAKETIMERGUID, IntPtr.Zero, ptrName, ref buffSize);
byte[] ba = new byte[buffSize];
Marshal.Copy(ptrName, ba, 0, (int)buffSize);
int retVal = BitConverter.ToInt32(ba, 0);
if (retVal == 0)
{
return true;
}
else
{
return false;
}
}
catch(Exception exp)
{
Logger.LogException(exp);
return false;
}
finally
{
if (ptrName != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptrName);
}
}
}
return false;
This works most of the time, but when I reset my power plan settings, this doesn't work well (inconsistent). I also tried the following:
Guid currentPowerSchemeGuid = GetActivePowerSchemeGuid();
RegistryKey currentPowerSchemeKey = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + currentPowerSchemeGuid.ToString());
if (currentPowerSchemeKey != null)
{
RegistryKey sleepRegKey = currentPowerSchemeKey.OpenSubKey(ApplicationConstants.SLEEPGUID.ToString());
currentPowerSchemeKey.Close();
if (sleepRegKey != null)
{
RegistryKey wakeTimerRegKey = sleepRegey.OpenSubKey(ApplicationConstants.WAKETIMERGUID.ToString());
sleepRegKey.Close();
if (wakeTimerRegKey != null)
{
wakeTimerRegKey.Close();
currentPowerSchemeKey.Close();
return true;
}
else
{
currentPowerSchemeKey.Close();
return false;
}
}
else
{
currentPowerSchemeKey.Close();
return false;
}
}
else
{
return false;
}
This doesn't work on reset of power plan settings, the wake timer GUID registry key gets cleared. Is there a proper way I can detect if my system supports wake timers?
According to arx, tried the following code and it works.
public static bool IsWakeTimerSupported()
{
IntPtr timerHandle = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1");
uint retVal = GetLastError();
if (timerHandle != IntPtr.Zero)
{
CancelWaitableTimer(timerHandle);
CloseHandle(timerHandle);
timerHandle = IntPtr.Zero;
}
//SUCCESS
if (retVal == 0)
{
return true;
}
else
{
return false;
}
}
The CancelWaitableTimer(timerHandle) can be ignored as the MSDN documentation says to use CloseHandle.
EDIT:
public static bool IsWakeTimerSupported()
{
IntPtr timerHandle = CreateWaitableTimer(IntPtr.Zero, true, "Wait Timer 1");
long interval = 0;
int retVal = 0;
if (timerHandle != IntPtr.Zero)
{
SetWaitableTimer(timerHandle, ref interval, 0, IntPtr.Zero, IntPtr.Zero, true);
retVal = Marshal.GetLastWin32Error();
WaitableTimer.CancelWaitableTimer(timerHandle);
try
{
Win32.CloseHandle(timerHandle);
}
catch (Exception exp)
{
Logger.LogException(exp);
}
timerHandle = IntPtr.Zero;
}
//SUCCESS
if (retVal == 0)
{
return true;
}
else
{
return false;
}
}
According to this article, http://blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx we shoud never use GetLastError through PInvoke.
Using the powrprof.dll library worked for me:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Namespace {
public static class PowerOptions {
// src: https://msdn.microsoft.com/en-us/library/windows/desktop/hh448380%28v=vs.85%29.aspx
private readonly static Guid HIGH_PERFORMANCE = new Guid("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"); // aka MIN_POWER_SAVINGS
private readonly static Guid BALANCED = new Guid("381b4222-f694-41f0-9685-ff5bb260df2e"); // aka TYPICAL_POWER_SAVINGS
private readonly static Guid POWER_SAVER = new Guid("a1841308-3541-4fab-bc81-f71556f20b4a"); // aka MAX_POWER_SAVINGS
private readonly static Guid ACDC_POWER_SOURCE = new Guid("5d3e9a59-e9D5-4b00-a6bd-ff34ff516548");
private readonly static Guid SLEEP_SUBCATEGORY = new Guid("238C9FA8-0AAD-41ED-83F4-97BE242C8F20");
private readonly static Guid WAKE_TIMERS = new Guid("bd3b718a-0680-4d9d-8ab2-e1d2b4ac806d");
public static String GetCurrentPowerPlanFriendlyName() {
IntPtr ptrActiveGuid = IntPtr.Zero;
int ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrActiveGuid);
if (ret == 0) {
uint buffSize = 0;
ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref buffSize);
if (ret == 0) {
if (buffSize == 0)
return "";
IntPtr ptrName = Marshal.AllocHGlobal((int) buffSize);
ret = PowerReadFriendlyName(IntPtr.Zero, ptrActiveGuid, IntPtr.Zero, IntPtr.Zero, ptrName, ref buffSize);
if (ret == 0) {
String name = Marshal.PtrToStringUni(ptrName);
Marshal.FreeHGlobal(ptrName);
return name;
}
Marshal.FreeHGlobal(ptrName);
}
}
throw new Win32Exception(ret, "GetCurrentPowerPlanFriendlyName");
}
public static PowerStatus GetPowerStatus() {
PowerStatus ps = new PowerStatus();
if (!GetSystemPowerStatus(ref ps))
throw new Win32Exception(Marshal.GetLastWin32Error(), "GetPowerStatus");
return ps;
}
public static bool GetWakeTimersEnabled(PowerSource powerSource = PowerSource.Current, PowerPlan powerPlan = PowerPlan.Current) {
int ret = 0;
if (powerSource == PowerSource.Current) {
PowerStatus ps = GetPowerStatus();
if (ps.ACLineStatus == PowerLineStatus.Online)
powerSource = PowerSource.PluggedIn;
else
powerSource = PowerSource.OnBattery;
}
if (ret == 0) {
if (powerPlan == PowerPlan.Current) {
IntPtr ptrPowerPlan = IntPtr.Zero;
ret = PowerGetActiveScheme(IntPtr.Zero, ref ptrPowerPlan);
if (ret == 0) {
uint value = 0;
if (powerSource == PowerSource.PluggedIn)
ret = PowerReadACValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
else
ret = PowerReadDCValueIndex(IntPtr.Zero, ptrPowerPlan, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
if (ret == 0) {
return (value == 1);
}
}
}
else {
Guid guid = GetGuid(powerPlan);
uint value = 0;
if (powerSource == PowerSource.PluggedIn)
ret = PowerReadACValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
else
ret = PowerReadDCValueIndex(IntPtr.Zero, guid, SLEEP_SUBCATEGORY, WAKE_TIMERS, ref value);
if (ret == 0) {
return (value == 1);
}
}
}
throw new Win32Exception(ret, "GetWakeTimersEnabled");
}
public static Guid GetGuid(PowerPlan powerPlan) {
if (powerPlan == PowerPlan.Balanced)
return BALANCED;
if (powerPlan == PowerPlan.HighPerformance)
return HIGH_PERFORMANCE;
if (powerPlan == PowerPlan.PowerSaver)
return POWER_SAVER;
throw new ArgumentException("Not a standard power plan: " + powerPlan);
}
[DllImport("powrprof.dll", SetLastError = true)]
public static extern int PowerWriteACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, uint AcValueIndex);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerReadACValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, IntPtr SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerReadDCValueIndex(IntPtr RootPowerKey, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, ref uint AcValueIndex);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);
[DllImport("powrprof.dll", SetLastError = true)]
private static extern int PowerReadFriendlyName(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingsGuid, IntPtr PowerSettingGuid, IntPtr Buffer, ref uint BufferSize);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetSystemPowerStatus(ref PowerStatus lpSystemPowerStatus);
}
public enum PowerPlan {
Current,
HighPerformance,
Balanced,
PowerSaver,
}
public enum PowerSource {
Current,
OnBattery,
PluggedIn
}
public struct PowerStatus {
///<summary>The AC power status.</summary>
public PowerLineStatus ACLineStatus;
///<summary>The battery charge status.</summary>
public PowerChargeStatus BatteryFlag;
///<summary>Returns a value between [0 to 100] or 255 if unknown.</summary>
public byte BatteryLifePercent;
///<summary>Returns a value that indicates if the system is currently conserving power.</summary>
public PowerSaveStatus SystemStatusFlag;
///<summary>Number of seconds of battery life remaining, or -1 if unknown.</summary>
public int BatteryLifeTime;
///<summary>Number of seconds of batter life on a full charge, or -1 if unknown.</summary>
public int BatteryFullLifeTime;
}
public enum PowerLineStatus : byte {
Offline = 0,
Online = 1,
Unknown = 255,
}
[Flags]
public enum PowerChargeStatus : byte {
High = 1,
Low = 2,
Critical = 4,
Charging = 8,
NoBattery = 128,
Unknown = 255,
}
public enum PowerSaveStatus : byte {
Off = 0,
On = 1,
}
}
I created the type IntPtr<T> to act like a generic pointer that c/c++ have:
public struct IntPtr<T> : IDisposable where T : struct
{
private static Dictionary<IntPtr, GCHandle> handles = new Dictionary<IntPtr, GCHandle>();
private IntPtr ptr;
public IntPtr(ref T value)
{
GCHandle gc = GCHandle.Alloc(value, GCHandleType.Pinned);
ptr = gc.AddrOfPinnedObject();
handles.Add(ptr, gc);
}
public IntPtr(ref T[] value)
{
GCHandle gc = GCHandle.Alloc(value, GCHandleType.Pinned);
ptr = gc.AddrOfPinnedObject();
handles.Add(ptr, gc);
}
public IntPtr(IntPtr value)
{ ptr = value; }
public IntPtr(IntPtr<T> value)
{ ptr = value.ptr; }
public void Dispose()
{
if (handles.ContainsKey(ptr))
{
GCHandle gc = handles[ptr];
gc.Free();
handles.Remove(ptr);
ptr = IntPtr.Zero;
}
}
public T? this[int index]
{
get
{
if (ptr == IntPtr.Zero) return null;
if (index < 0) throw new IndexOutOfRangeException();
if (handles.ContainsKey(ptr))
{
GCHandle gc = handles[ptr];
if (gc.Target is Array) return ((T[])gc.Target)[index];
return (T)gc.Target;
}
return null;
}
set
{
if (index < 0) throw new IndexOutOfRangeException();
// not yet implemented
}
}
private T[] getArray()
{
if (handles.ContainsKey(ptr)) return (T[])handles[ptr].Target;
return null;
}
public int Count
{
get
{
if(handles.ContainsKey(ptr))
{
GCHandle gc = handles[ptr];
if (gc.Target is Array) return ((T[])gc.Target).Length;
return 1;
}
return 0;
}
}
public static implicit operator IntPtr(IntPtr<T> value) { return value.ptr; }
public static implicit operator T(IntPtr<T> value) { return (T)value[0]; }
public static implicit operator T[](IntPtr<T> value) { return value.getArray(); ; }
public static implicit operator T?(IntPtr<T> value) { return value[0]; }
}
It's not complete yet but for now it works, the problem is i keep track of GCHandle by storing them in handles now i need to free the GCHandle once it no more needed so i have to declare a destrcutor but c# don't allow struct to have destrcutor or to override 'Finalize' method and if the such variable of type IntPtr<T> goes out of scope the destruction take place but the GCHandle won't be free.
UPDATE
As an example of this class usage, suppose we going to interpo COAUTHIDENTITY and COAUTHINFO from COM, here what it will look like:
[StructLayout(LayoutKind.Sequential)]
struct COAUTHIDENTITY
{
[MarshalAs(UnmanagedType.LPWStr)] string User;
uint UserLength;
[MarshalAs(UnmanagedType.LPWStr)] string Domain;
uint DomainLength;
[MarshalAs(UnmanagedType.LPWStr)] string Password;
uint PasswordLength;
uint Flags;
}
[StructLayout(LayoutKind.Sequential)]
struct COAUTHINFO
{
uint dwAuthnSvc;
uint dwAuthzSvc;
[MarshalAs(UnmanagedType.LPWStr)] string pwszServerPrincName;
uint dwAuthnLevel;
uint dwImpersonationLevel;
IntPtr<COAUTHIDENTITY> pAuthIdentityData;
uint dwCapabilities;
}
Instead to make pAuthIdentityData an IntPtr and use Marshal member functions to get object of type COAUTHIDENTITY, IntPtr<T> will make it more simple.
The question is: where should i write the code to free GCHandle when the IntPtr<T> is released?
You're reinventing the wheel. Look at the SafeHandle class. Use an existing descendant or create your own descendant.
I'm writing a program that needs to be able to extract the thumbnail image from a file. I've gotten a hold of a class, ThumbnailCreator, which is able to do this. The source of this Class is below.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Drawing.Imaging;
internal class ThumbnailCreator : IDisposable
{
// Fields
private IMalloc alloc;
private Size desiredSize;
private bool disposed;
private static readonly string fileExtention = ".jpg";
private Bitmap thumbnail;
// Methods
public ThumbnailCreator()
{
this.desiredSize = new Size(100, 100);
}
public ThumbnailCreator(Size desiredSize)
{
this.desiredSize = new Size(100, 100);
this.DesiredSize = desiredSize;
}
public void Dispose()
{
if (!this.disposed)
{
if (this.alloc != null)
{
Marshal.ReleaseComObject(this.alloc);
}
this.alloc = null;
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
}
this.disposed = true;
}
}
~ThumbnailCreator()
{
this.Dispose();
}
private bool getThumbNail(string file, IntPtr pidl, IShellFolder item)
{
bool CS;
IntPtr hBmp = IntPtr.Zero;
IExtractImage extractImage = null;
try
{
if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
{
int prgf;
IUnknown iunk = null;
Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk);
extractImage = (IExtractImage) iunk;
if (extractImage != null)
{
SIZE sz = new SIZE {
cx = this.desiredSize.Width,
cy = this.desiredSize.Height
};
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT;
int uFlags = (int) flags;
extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
extractImage.Extract(out hBmp);
if (hBmp != IntPtr.Zero)
{
this.thumbnail = Image.FromHbitmap(hBmp);
}
Marshal.ReleaseComObject(extractImage);
extractImage = null;
}
return true;
}
CS = false;
}
catch (Exception)
{
if (hBmp != IntPtr.Zero)
{
UnManagedMethods.DeleteObject(hBmp);
}
if (extractImage != null)
{
Marshal.ReleaseComObject(extractImage);
}
throw;
}
return CS;
}
public Bitmap GetThumbNail(string file)
{
if (!File.Exists(file) && !Directory.Exists(file))
{
throw new FileNotFoundException(string.Format("The file '{0}' does not exist", file), file);
}
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
this.thumbnail = null;
}
IShellFolder folder = getDesktopFolder;
if (folder != null)
{
IntPtr pidlMain;
try
{
int cParsed;
int pdwAttrib;
string filePath = Path.GetDirectoryName(file);
folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
throw;
}
if (pidlMain != IntPtr.Zero)
{
Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
IShellFolder item = null;
try
{
folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (item != null)
{
IEnumIDList idEnum = null;
try
{
item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (idEnum != null)
{
IntPtr pidl = IntPtr.Zero;
bool complete = false;
while (!complete)
{
int fetched;
if (idEnum.Next(1, ref pidl, out fetched) != 0)
{
pidl = IntPtr.Zero;
complete = true;
}
else if (this.getThumbNail(file, pidl, item))
{
complete = true;
}
if (pidl != IntPtr.Zero)
{
this.Allocator.Free(pidl);
}
}
Marshal.ReleaseComObject(idEnum);
}
Marshal.ReleaseComObject(item);
}
this.Allocator.Free(pidlMain);
}
Marshal.ReleaseComObject(folder);
}
return this.thumbnail;
}
private static string PathFromPidl(IntPtr pidl)
{
StringBuilder path = new StringBuilder(260, 260);
if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0)
{
return path.ToString();
}
return string.Empty;
}
// Properties
private IMalloc Allocator
{
get
{
if (!this.disposed && (this.alloc == null))
{
UnManagedMethods.SHGetMalloc(out this.alloc);
}
return this.alloc;
}
}
public Size DesiredSize
{
get
{
return this.desiredSize;
}
set
{
this.desiredSize = value;
}
}
private static IShellFolder getDesktopFolder
{
get
{
IShellFolder ppshf;
UnManagedMethods.SHGetDesktopFolder(out ppshf);
return ppshf;
}
}
public Bitmap ThumbNail
{
get
{
return this.thumbnail;
}
}
// Nested Types
private enum EIEIFLAG
{
IEIFLAG_ASPECT = 4,
IEIFLAG_ASYNC = 1,
IEIFLAG_CACHE = 2,
IEIFLAG_GLEAM = 0x10,
IEIFLAG_NOBORDER = 0x100,
IEIFLAG_NOSTAMP = 0x80,
IEIFLAG_OFFLINE = 8,
IEIFLAG_ORIGSIZE = 0x40,
IEIFLAG_QUALITY = 0x200,
IEIFLAG_SCREEN = 0x20
}
[Flags]
private enum ESFGAO
{
SFGAO_CANCOPY = 1,
SFGAO_CANDELETE = 0x20,
SFGAO_CANLINK = 4,
SFGAO_CANMOVE = 2,
SFGAO_CANRENAME = 0x10,
SFGAO_CAPABILITYMASK = 0x177,
SFGAO_COMPRESSED = 0x4000000,
SFGAO_CONTENTSMASK = -2147483648,
SFGAO_DISPLAYATTRMASK = 0xf0000,
SFGAO_DROPTARGET = 0x100,
SFGAO_FILESYSANCESTOR = 0x10000000,
SFGAO_FILESYSTEM = 0x40000000,
SFGAO_FOLDER = 0x20000000,
SFGAO_GHOSTED = 0x80000,
SFGAO_HASPROPSHEET = 0x40,
SFGAO_HASSUBFOLDER = -2147483648,
SFGAO_LINK = 0x10000,
SFGAO_READONLY = 0x40000,
SFGAO_REMOVABLE = 0x2000000,
SFGAO_SHARE = 0x20000,
SFGAO_VALIDATE = 0x1000000
}
[Flags]
private enum ESHCONTF
{
SHCONTF_FOLDERS = 0x20,
SHCONTF_INCLUDEHIDDEN = 0x80,
SHCONTF_NONFOLDERS = 0x40
}
[Flags]
private enum ESHGDN
{
SHGDN_FORADDRESSBAR = 0x4000,
SHGDN_FORPARSING = 0x8000,
SHGDN_INFOLDER = 1,
SHGDN_NORMAL = 0
}
[Flags]
private enum ESTRRET
{
STRRET_WSTR,
STRRET_OFFSET,
STRRET_CSTR
}
[ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IEnumIDList
{
[PreserveSig]
int Next(int celt, ref IntPtr rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(ref ThumbnailCreator.IEnumIDList ppenum);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
private interface IExtractImage
{
void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);
void Extract(out IntPtr phBmpThumbnail);
}
[ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMalloc
{
[PreserveSig]
IntPtr Alloc(int cb);
[PreserveSig]
IntPtr Realloc(IntPtr pv, int cb);
[PreserveSig]
void Free(IntPtr pv);
[PreserveSig]
int GetSize(IntPtr pv);
[PreserveSig]
int DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")]
private interface IShellFolder
{
void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes);
void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList);
void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.IShellFolder ppvOut);
void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);
[PreserveSig]
int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut);
void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)] ref ThumbnailCreator.ESFGAO rgfInOut);
void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut);
void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName);
void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")]
private interface IUnknown
{
[PreserveSig]
IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);
[PreserveSig]
IntPtr AddRef();
[PreserveSig]
IntPtr Release();
}
[StructLayout(LayoutKind.Sequential)]
private struct SIZE
{
public int cx;
public int cy;
}
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto)]
private struct STRRET_ANY
{
// Fields
[FieldOffset(4)]
public IntPtr pOLEString;
[FieldOffset(0)]
public ThumbnailCreator.ESTRRET uType;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=4)]
private struct STRRET_CSTR
{
public ThumbnailCreator.ESTRRET uType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=520)]
public byte[] cStr;
}
private class UnManagedMethods
{
// Methods
[DllImport("gdi32", CharSet=CharSet.Auto)]
internal static extern int DeleteObject(IntPtr hObject);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
}
}
As a test I created a quick console application, the code of which is below. This test worked fine and was able to extract a thumbnail, and saved it to a PNG file.
static void Main(string[] args)
{
try
{
Console.WriteLine("press a key to extract");
System.Console.ReadKey();
string path = #"C:\somefile.xyz";
ThumbnailCreator creator = new ThumbnailCreator();
creator.DesiredSize = new Size(600, 600);
Bitmap bm = creator.GetThumbNail(path);
bm.Save(#"C:\blah.png", System.Drawing.Imaging.ImageFormat.Png);
Console.WriteLine("press a key to exit");
System.Console.ReadKey();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
My problem is the real application I want to use this in runs as a plug-in for another application. When I try to run the same test code in the plug-in creator.GetThumbNail(path); returns null.
I debugged a little further and found that in the method ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item) the line extractImage.Extract(out hBmp); returns IntPtr.Zero. Whereas in the console application that works this method actually returns a number. Maybe this is the problem? Or maybe this problem happens before this. Honestly, I'm completely lost when it comes to Interop and Windows API stuff.
Does anyone know of any possible reasons why this class would work in a standalone console application, but not as part of a plug-in for another application?
Update
Here's a bit more detail of how this plug-in is created. To add a custom command to this program you create a class that implements an ICommand interface from is API. The interface has a single Execute() method where code is place that should run when the user executes the custom command from the program. The native application, however, is what actualy instantiates my command class and calls the execute method.
What could be different about the way that the native app instantiates my command class and or calls the Execute() method that would prevent the thumbnail extraction from working?
Just ran across this issue myself and I do say, it took some time to figure out that it was a bitness issue. In your scenario your console app was probably compiled as x64 and your Windows app as x86.
The ExtractImage interface (BB2E617C-0920-11d1-9A0B-00C04FC2D6C1) on a 64bit OS is only accessible from 64bit applications (because explorer is 64bit), thus resulting in a "Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))" exception in applications compiled as x86.
Code that calls the Shell API must be in a COM Single Threaded Apartment, try putting [STAThread] attribute on the thread that calls this.