How to update the change time of a file from c#? - c#

Files can have a change date. This date is not the same as the last modified date or the last access date. Change date is not visible through the UI or .NET API. There a two Win32 functions GetFileInformationByHandleEx for reading and SetFileInformationByHandle for writing file information.
I want to read out the change date, add some hours to it, and write the new date back as the change date of the file.
For now I have following code:
class Program
{
static void Main(string[] args)
{
using (var file = new FileStream(#"c:\path\to\file", FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)Marshal.SizeOf(fileInfo));
SetFileInformationByHandle(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
fileInfo,
(uint)Marshal.SizeOf(fileInfo));
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandleEx(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
out FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
private enum FILE_INFO_BY_HANDLE_CLASS
{
FileBasicInfo = 0
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct FILE_BASIC_INFO
{
public LARGE_INTEGER CreationTime;
public LARGE_INTEGER LastAccessTime;
public LARGE_INTEGER LastWriteTime;
public LARGE_INTEGER ChangeTime;
public uint FileAttributes;
}
[StructLayout(LayoutKind.Explicit, Size = 8)]
private struct LARGE_INTEGER
{
[FieldOffset(0)]
public Int64 QuadPart;
[FieldOffset(0)]
public UInt32 LowPart;
[FieldOffset(4)]
public Int32 HighPart;
}
}
I can read out the change date into that awful structure LARGE_INTEGER. What I want to have is a function which can convert that type into a System.DateTime and vice versa.
The second problem that I have is that the siganture of the SetFileInformationByHandle method is wrong. I get a PInvokeStackImbalance with this additional information:
Additional information: A call to PInvoke function 'Program::SetFileInformationByHandle' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
Who can help me?

To answer the first part..On how to convert "that awful Large_Interger" to DateTime..
The below code snippet should help..
using (var file = new System.IO.FileStream(#"sample.log", System.IO.FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(fileInfo));
var changeTime = DateTime.FromFileTime(fileInfo.ChangeTime.QuadPart);
Console.WriteLine(changeTime);
System.TimeSpan changedForHowLong = DateTime.Now.Subtract(changeTime);
Console.WriteLine(changedForHowLong.Days);
}
I tested the above snippet it seems to work fine..
Let me try repro'ing the issue you faced with the PInvokeStackImbalance..
Take Care,

I found this signature on PInvoke
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
IntPtr lpFileInformation,
Int32 dwBufferSize);
Somehow this did not work. I had to change the type of the parameter lpFileInformation to FILE_BASIC_INFO to make it work.
This is the complete C# example called from PowerShell:
$fu = #"
using System;
using System.IO;
using System.Runtime.InteropServices;
public class FileUtility
{
private struct FILE_BASIC_INFO
{
[MarshalAs(UnmanagedType.I8)]
public Int64 CreationTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastAccessTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastWriteTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 ChangeTime;
[MarshalAs(UnmanagedType.U4)]
public UInt32 FileAttributes;
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
FILE_BASIC_INFO lpFileInformation,
Int32 dwBufferSize);
public void SetFileChangeTime()
{
using (FileStream fs = new FileStream(#"c:\path\to\file", FileMode.Open))
{
FILE_BASIC_INFO fileInfo = new FILE_BASIC_INFO();
fileInfo.ChangeTime = 943044610000000;
SetFileInformationByHandle(
fs.Handle,
0, // the same as FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo
fileInfo,
Marshal.SizeOf(fileInfo));
}
}
}
"#
Add-Type -TypeDefinition $fu -IgnoreWarnings
$f = New-Object -TypeName FileUtility
$f.SetFileChangeTime()
I have run the example with the other date properties since they are shown in the explorer and it worked.
Edit
This code does not run in debug mode within VS. As mentioned above it throws the exception. Running the EXE in the command line does not throw an exception. But the change date is not updated. However it works only in PowerShell. Strange.

Related

Getting Symbols from debugged process MainModule

I started writing a debugger in C#, to debug any process on my operating system. For now, it only can handle breakpoints (HW, SW, and Memory), but now I wanted to show the opcode of the process.
My first attempt was with nidsasm (NASM), but this is not suitable, because after startup a.Net Application assembler instructions are different from ndisasm (tested with CheatEngine).
So I searched a while and found some methods from the dbghelp.dll which can be called to list all loaded modules and symbols (plus the base address). Ok, my attempt is, to disassemble all modules separately with SharpDisasm.
I use ProcessModuleCollection modules = ProcessData.Instance.MPMR.ReadProcess.Modules; to get all loaded modules of the process. This works perfectly.
Now I tried to load the symbols of the MainModule, but at this point, I stuck with the implementation. I implemented the SymEnumSymbols Function with p/Invoke and other necessary functions like SymInitialize.
When I call it with a BaseAddress of for example the "User32.dll", all symbols are printed perfectly, but for the MainModule, I didn't get any symbols.
This is a screenshot from CheatEngine:
Symbols gained from Cheat Engine
As you can see, there are symbols like "Form1_Load", which I don't get with my implementation.
This is the necessary code sample:
if (!DebugApi.SymInitialize(ProcessData.Instance.MPMR.M_hProcess, null, false))
{
var err = Marshal.GetLastWin32Error();
//throw new Exception("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
Console.WriteLine("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
return;
}
if (!DebugApi.SymEnumSymbols(ProcessData.Instance.MPMR.M_hProcess, (ulong)ProcessData.Instance.MPMR.ReadProcess.MainModule.BaseAddress, "!", DebugApi.EnumSyms, IntPtr.Zero))
{
var err = Marshal.GetLastWin32Error();
//throw new Exception("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
Console.WriteLine("GetMemoryInfo failed : GetLastError() : " + new Win32Exception(err).Message);
return;
}
DebugApi.SymCleanup(ProcessData.Instance.MPMR.M_hProcess);
And my DebugApi, with all necessary p/Invoke functions.
public class DebugApi
{
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, [MarshalAs(UnmanagedType.Bool)]bool fInvadeProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymCleanup(IntPtr hProcess);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern ulong SymLoadModuleEx(IntPtr hProcess, IntPtr hFile, string ImageName, string ModuleName, long BaseOfDll, int DllSize, IntPtr Data, int Flags);
[DllImport("dbghelp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SymEnumSymbols(IntPtr hProcess, ulong BaseOfDll, string Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, IntPtr UserContext);
public delegate bool PSYM_ENUMERATESYMBOLS_CALLBACK(ref SYMBOL_INFO pSymInfo, uint SymbolSize, IntPtr UserContext);
public static bool EnumSyms(ref SYMBOL_INFO pSymInfo, uint SymbolSize, IntPtr UserContext)
{
Console.Out.WriteLine("Name: " + pSymInfo.Name);
return true;
}
[Flags]
public enum SymFlag : uint
{
VALUEPRESENT = 0x00000001,
REGISTER = 0x00000008,
REGREL = 0x00000010,
FRAMEREL = 0x00000020,
PARAMETER = 0x00000040,
LOCAL = 0x00000080,
CONSTANT = 0x00000100,
EXPORT = 0x00000200,
FORWARDER = 0x00000400,
FUNCTION = 0x00000800,
VIRTUAL = 0x00001000,
THUNK = 0x00002000,
TLSREL = 0x00004000,
}
[Flags]
public enum SymTagEnum : uint
{
Null,
Exe,
Compiland,
CompilandDetails,
CompilandEnv,
Function,
Block,
Data,
Annotation,
Label,
PublicSymbol,
UDT,
Enum,
FunctionType,
PointerType,
ArrayType,
BaseType,
Typedef,
BaseClass,
Friend,
FunctionArgType,
FuncDebugStart,
FuncDebugEnd,
UsingNamespace,
VTableShape,
VTable,
Custom,
Thunk,
CustomType,
ManagedType,
Dimension
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SYMBOL_INFO
{
public uint SizeOfStruct;
public uint TypeIndex;
public ulong Reserved1;
public ulong Reserved2;
public uint Reserved3;
public uint Size;
public ulong ModBase;
public SymFlag Flags;
public ulong Value;
public ulong Address;
public uint Register;
public uint Scope;
public SymTagEnum Tag;
public int NameLen;
public int MaxNameLen;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string Name;
}
}
My Functions should be ok, because it works with other Modules (loaded dll's for example). Maybe I don't understand the concept of symbols of a .Net Executable or missing something.
can it be that you are looking for System.Diagnostics.SymbolStore.ISymbolScope.
Have a look at the class SymbolAccess, you can use it to gain access to ISymbolScope.GetLocals() that returns ISymbolVariable[] and GetChildren() again returning in this time an array called ISymbolVariable[]
Now another interesting set of reference code samples is the Debugger extension lets you "snif" the values as shown here

C# method's type signature is not pinvoke compatible

I want to use API from Omron V4KU, the documentation described like this :
Original c# code :
const string DllLocation = #"..\..\Libs\Omron\OMCR.dll";
[DllImport(DllLocation)]
public static extern LPCOMCR OMCR_OpenDevice(string lpcszDevice, LPCOMCR_OPTION lpcOption);
public void Start()
{
var lpcOption = new LPCOMCR_OPTION();
var result = OMCR_OpenDevice(null, lpcOption); // error method's type signature is not pinvoke compatible
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR
{
public string lpcszDevice;
public IntPtr hDevice;
public uint lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
if I missed or wrong in writing code?
sorry, my english is bad. thanks for help.
Start by defining the union structure correctly:
// OMCR_OPTION.COM
[StructLayout(LayoutKind.Sequential)]
public struct OmcrCom
{
public IntPtr Reserved0;
public uint BaudRate;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
public IntPtr Reserved1;
public IntPtr Reserved2;
}
// OMCR_OPTION.USB
[StructLayout(LayoutKind.Sequential)]
public struct OmcrUsb
{
public uint Reserved0;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
}
// OMCR_OPTION (union of COM and USB)
[StructLayout(LayoutKind.Explicit)]
public struct OmcrOptions
{
[FieldOffset(0)]
public OmcrCom Com;
[FieldOffset(0)]
public OmcrUsb Usb;
}
// OMCR
[StructLayout(LayoutKind.Sequential)]
public struct OmcrDevice
{
public string Device;
public IntPtr DeviceHandle;
public IntPtr DevicePointer;
}
[DllImport(dllName: DllLocation, EntryPoint = "OMCR_OpenDevice"]
public static extern IntPtr OmcrOpenDevice(string type, ref OmcrOptions options);
And then call the method, something like:
var options = new OmcrOptions();
options.Com.BaudRate = 115200; // or whatever you need to set
var type = "COM"; // is this USB/COM? not sure
OmcrDevice device;
var devicePtr = OmcrOpenDevice(type, ref options);
if (devicePtr == IntPtr.Zero)
device = (OmcrDevice)Marshal.PtrToStructure(devicePtr, typeof(OmcrDevice));
Well, for one, the documentation is asking you to pass LPCOMCR_OPTION as a pointer - you're passing it as a value. Using ref should help. There's another problem, though, and that's the return value - again, you're trying to interpret it as a value, while the docs say it's a pointer. However, this is a lot trickier than the first error - as far as I'm aware, your only options are using a C++/CLI interop library, or expecting IntPtr as a return value. In any case, you need to handle proper deallocation of the memory you get this way.
You need to do it like this:
[StructLayout(LayoutKind.Sequential)]
public struct OMCR
{
[MarshalAs(UnmanagedType.LPStr)]
public string lpcszDevice;
public IntPtr hDevice;
public IntPtr lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct OMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
[DllImport(DllLocation, CallingConvention = CallingConvention.???,
SetLastError = true)]
public static extern IntPtr OMCR_OpenDevice(string lpcszDevice,
ref OMCR_OPTION lpcOption);
You need to replace CallingConvention.??? with the appropriate calling convention. We cannot tell from the question what that is. You will have to find out by reading the header file.
The return value is a pointer to OMCR. You need to hold on to this pointer and pass it to OMCR_CloseDevice when you are finished with it.
In order to obtain an OMCR value you would do the following:
OMCR_OPTION Option = new OMCR_OPTION(); // not sure how to initialize this
IntPtr DevicePtr = OMCR_OpenDevice(DeviceType, ref Option);
if (DevicePtr == IntPtr.Zero)
throw new Win32Exception();
OMCR Device = (OMCR)Marshal.PtrToStructure(DevicePtr, typeof(OMCR));

C# How to use CallNtPowerInformation with Interop to get SYSTEM_POWER_INFORMATION

I am trying to write a small program that runs as a service and monitors if a user is active or not. If the user is idle (no mouse/keyboard) for an hour, then certain processes are killed. Got it working if run by a user by using the LASTINPUTINFO from user32.dll, but it won't work as a service. Looking further I ran across someone saying to call CallNtPowerInformation with SystemPowerInformation and examine the TimeRemaining member. I'd like to do this but have little experience with interop and was hoping to get a little help/example:
In C# I would import:
[DllImport("powrprof.dll", SetLastError = true)]
private static extern UInt32 CallNtPowerInformation(
Int32 InformationLevel,
IntPtr lpInputBuffer,
UInt32 nInputBufferSize,
IntPtr lpOutputBuffer,
UInt32 nOutputBufferSize
);
I believe then I would need to create a struct for SYSTEM_POWER_INFORMATION to handle the result?
Apologies for the n00bness
You can get the information you need like this:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const int SystemPowerInformation = 12;
const uint STATUS_SUCCESS = 0;
struct SYSTEM_POWER_INFORMATION
{
public uint MaxIdlenessAllowed;
public uint Idleness;
public uint TimeRemaining;
public byte CoolingMode;
}
[DllImport("powrprof.dll")]
static extern uint CallNtPowerInformation(
int InformationLevel,
IntPtr lpInputBuffer,
int nInputBufferSize,
out SYSTEM_POWER_INFORMATION spi,
int nOutputBufferSize
);
static void Main(string[] args)
{
SYSTEM_POWER_INFORMATION spi;
uint retval = CallNtPowerInformation(
SystemPowerInformation,
IntPtr.Zero,
0,
out spi,
Marshal.SizeOf(typeof(SYSTEM_POWER_INFORMATION))
);
if (retval == STATUS_SUCCESS)
Console.WriteLine(spi.TimeRemaining);
Console.ReadLine();
}
}
}
I cannot tell you whether or not this method will give you the information you need when run from a service.

Using ShellExecuteEx to open file properties sheet under explorer process, not calling process

My issue is with a PropertySheetExtension, but the same behavior seems present with the default file properties sheet. The issue with the following code:
// Snippet from http://stackoverflow.com/a/1936957/124721
using System.Runtime.InteropServices;
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHELLEXECUTEINFO
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpFile;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIcon;
public IntPtr hProcess;
}
private const int SW_SHOW = 5;
private const uint SEE_MASK_INVOKEIDLIST = 12;
public static bool ShowFileProperties(string Filename)
{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = Filename;
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
return ShellExecuteEx(ref info);
}
Is that it opens the file properties sheet under the calling applications process, instead of explorer.exe. Due to this the property sheet closes when the application is closed, which is not the behavior I need. I need the sheet to remain open when the application exits. Another issue is if the application opens the property sheet, and then you right click on the file through explorer and click properties it will open a second, duplicate sheet.
I have tried using ShellExecuteEx and setting the parent window handle with explorer's main window, or from GetShellWindow, but that didn't help.
Is there another way to get this property sheet to open under the explorer.exe process?
Your process is actually supposed to stick around until all shell threads have ended.
To do this you must implement a free threaded interface that supports IUnknown and tell Windows about it by calling SHSetInstanceExplorer.
More information and a C++ example implementation can be found in this blog post.

P/Invoke WlanHostedNetworkQueryStatus from wlanapi.dll

I have mapped C++ function (from WLanapi.dll):
DWORD WINAPI WlanHostedNetworkQueryStatus(
_In_ HANDLE hClientHandle,
_Out_ PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
_Reserved_ PVOID pvReserved
);
To the following C# code:
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out _WLAN_HOSTED_NETWORK_STATUS ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
I have also mapped all the structs and enums required and other stuff (for example to get the clientHandle pointer and to start the hosted network).
The _WLAN_HOSTED_NETWORK_STATUS is mapped like so:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
}
Now when executing that function, I am not sure how to use ppWlanHostedNetworkStatus correctly and such. The function returns ERROR_SUCCESS (0) which means I have called it and passed parameters correctly as far as I am concerned:
_WLAN_HOSTED_NETWORK_STATUS netStatus = new _WLAN_HOSTED_NETWORK_STATUS();
WlanHostedNetworkQueryStatus(clientHandle, out netStatus, IntPtr.Zero);
But while querying ppWlanHostedNetworkStatus for values (like state of the network, or number of connected peers) I am getting just some strange long integers (I would say memory addresses, but I am not sure), for example call:
netStatus.HostedNetworkState.ToString();
Returns
11465720
HostedNetworkState is an enumeration defined like:
public enum _WLAN_HOSTED_NETWORK_STATE
{
wlan_hosted_network_unavailable,
wlan_hosted_network_idle,
wlan_hosted_network_active
}
So .toString() should have returned one of these strings from the enumeration, right?
I am pretty sure it is something to do with pointers etc, since in the documentation of the _WLAN_HOSTED_NETWORK_STATUS ( MS documentation ) it says that before the call to that function, the ppWlanHostedNetworkStatus should be a NULL, and that it is itself a pointer to the structure...
How can I debug it? I am coding in C#, VS 2012...
Thanks for your help.
-----EDIT-----
I further tried to map the function with IntPtr as an argument, pass IntPtr.Zero and Marshal.PtrToStruct, but I am getting AccessViolationException when trying to do that...
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out IntPtr ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
And then:
IntPtr ppStatus = IntPtr.Zero;
WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);
_WLAN_HOSTED_NETWORK_STATUS netStatus = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus, typeof(_WLAN_HOSTED_NETWORK_STATUS));
------EDIT 2-------
Following advice from Fermat2357, I have uncommented part of the struct to map, and change the following to count for a pointer to pointer:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
//public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
}
I call it like this:
IntPtr ppStatus = IntPtr.Zero;
WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);
IntPtr ppStatus2 = new IntPtr(ppStatus.ToInt32());
_WLAN_HOSTED_NETWORK_STATUS stat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus2, typeof(_WLAN_HOSTED_NETWORK_STATUS));
netStatus = stat.HostedNetworkState.ToString();
This finally gives me the correct network status (active after starting it)... Now I have to find a way to marshal that dynamic array...
Thanks so far for help Fermat2357
Your mapping is incorrect. Take a look at the definition of the API function WlanHostedNetworkQueryStatus.
DWORD WINAPI WlanHostedNetworkQueryStatus(
_In_ HANDLE hClientHandle,
_Out_ PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
_Reserved_ PVOID pvReserved
);
Please take care, the parameter ppWlanHostedNetworkStatus is a pointer to a pointer to a WLAN_HOSTED_NETWORK_STATUS structure.
Take a deeper look in the documentation of the function you will find
ppWlanHostedNetworkStatus [out]
On input, this parameter must be NULL.
On output, this parameter receives a pointer to the current status of the wireless Hosted Network, if the call to the WlanHostedNetworkQueryStatus function succeeds. The current status is returned in a WLAN_HOSTED_NETWORK_STATUS structure.
If you give in a NULL pointer (as described in the documentation) the underlying API will allocate a buffer for you holding the structure and initializing the pointer to this buffer. Later dont forget to free it by a call to WlanFreeMemory. Otherwise you will have a resource leak here.
However the documentation seems to be not 100% complete for this function. In my tests (Win7 32Bit) the API will not allocate memory for you if you intitalize the pointer to a enough big memory buffer. In this case a later call to WlanFreeMemory seems not be necessary. But in this case its hard to guess how much memory you need for the following WLAN_HOSTED_NETWORK_PEER_STATE structures. For that reason this seems not to be useable anyway.
Here is the C code I used for testing
#include <Wlanapi.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwRes;
HANDLE hHandle;
DWORD negotiatedVersion;
dwRes = WlanOpenHandle(1, NULL, &negotiatedVersion, &hHandle);
if (ERROR_SUCCESS == dwRes)
{
PWLAN_HOSTED_NETWORK_STATUS pStatus = NULL;
dwRes = WlanHostedNetworkQueryStatus(hHandle, &pStatus, NULL);
if (ERROR_SUCCESS == dwRes)
{
if (wlan_hosted_network_unavailable != pStatus->HostedNetworkState)
{
// Do something with the result
}
WlanFreeMemory(pStatus);
}
else
{
// handle Error
}
WlanCloseHandle(hHandle, NULL);
}
else
{
// handle Error
}
return 0;
}
If you want to make your sample work you need to modify the way you marshal the structure.
Edit
To marshal correctly you can try the following:
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public _WLAN_HOSTED_NETWORK_PEER_STATE PeerList;
}
...
IntPtr ptr = new IntPtr();
uint hostedNetworkQueryStatusSuccess = WlanHostedNetworkQueryStatus(clientHandle, out ptr, IntPtr.Zero);
if (openHandleSuccess == 0)
{
var netStat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ptr, typeof(_WLAN_HOSTED_NETWORK_STATUS));
Console.WriteLine(netStat.HostedNetworkState);
if (netStat.HostedNetworkState != _WLAN_HOSTED_NETWORK_STATE.wlan_hosted_network_unavailable)
{
IntPtr offset = Marshal.OffsetOf(typeof(_WLAN_HOSTED_NETWORK_STATUS), "PeerList");
for (int i = 0; i < netStat.dwNumberOfPeers; i++)
{
var peer = (_WLAN_HOSTED_NETWORK_PEER_STATE)Marshal.PtrToStructure(
new IntPtr(ptr.ToInt64() + offset.ToInt64()),
typeof(_WLAN_HOSTED_NETWORK_PEER_STATE));
System.Console.WriteLine(peer.PeerMacAddress);
offset += Marshal.SizeOf(peer);
}
}
}
The Idea: Im using a structure here that I can be sure it is marshaled correctly all the time. Later with the knowledge of the dwNumberOfPeers member (if it is valid) I get all the inner structures step by step.
EDIT: One thing to try is a trick I use to help debugging - replace your struct with a byte array, and see what you pull back (ok, in this case a uint array):
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U4, SizeConst = 48)]
public uint[] scratch;
}
I get very strange values when I run this locally, however - the first (10x4) = 40 bytes are zeroed out:
0 0 0 0
0 0 0 0
0 0 4D454D4C 28
16DCD870 0 787F6447 80000020
...omitted...
Try this set of P/Invoke declarations:
(done in LINQPad, so replace "Dump" method accordingly)
void Main()
{
IntPtr clientHandle;
int negotiatedVersion;
if(WlanOpenHandle(2, IntPtr.Zero, out negotiatedVersion, out clientHandle) != 0)
{
throw new InvalidOperationException("Could not open handle");
}
Console.WriteLine("Negotiated version:{0}", negotiatedVersion);
IntPtr pNetStatus = IntPtr.Zero;
if(WlanHostedNetworkQueryStatus(clientHandle, out pNetStatus, IntPtr.Zero) != 0)
{
throw new InvalidOperationException("Could not query network status");
}
var netStatus = (WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(pNetStatus, typeof(WLAN_HOSTED_NETWORK_STATUS));
Console.WriteLine(netStatus.PeerList[0]);
WlanFreeMemory(pNetStatus);
WlanCloseHandle(clientHandle, IntPtr.Zero);
}
[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanOpenHandle(
[In] int dwClientVersion,
IntPtr pReserved,
[Out] out int pdwNegotiatedVersion,
[Out] out IntPtr phClientHandle
);
[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanCloseHandle(
[In] IntPtr hClientHandle,
IntPtr pReserved
);
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
[In] IntPtr hClientHandle,
[Out] out IntPtr ppWlanHostedNetworkStatus,
[In, Out] IntPtr pvReserved
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
public Guid IPDeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
public string wlanHostedNetworkBSSID;
public _DOT11_PHY_TYPE dot11PhyType;
public UInt32 ulChannelFrequency;
public UInt32 dwNumberOfPeers;
public IntPtr PeerList;
}
public enum _WLAN_HOSTED_NETWORK_STATE
{
wlan_hosted_network_unavailable,
wlan_hosted_network_idle,
wlan_hosted_network_active
}
public enum _DOT11_PHY_TYPE : uint
{
dot11_phy_type_unknown = 0,
dot11_phy_type_any = 0,
dot11_phy_type_fhss = 1,
dot11_phy_type_dsss = 2,
dot11_phy_type_irbaseband = 3,
dot11_phy_type_ofdm = 4,
dot11_phy_type_hrdsss = 5,
dot11_phy_type_erp = 6,
dot11_phy_type_ht = 7,
dot11_phy_type_IHV_start = 0x80000000,
dot11_phy_type_IHV_end = 0xffffffff
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_PEER_STATE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
public string PeerMacAddress;
_WLAN_HOSTED_NETWORK_PEER_AUTH_STATE PeerAuthState;
}
public enum _WLAN_HOSTED_NETWORK_PEER_AUTH_STATE
{
wlan_hosted_network_peer_state_invalid,
wlan_hosted_network_peer_state_authenticated
}

Categories

Resources