I am using the following line to convert my WTS_PROCESS_INFO object's ProcessName property to a string:
string name = Marshal.PtrToStringUni(processInfos[I].processName)
This seems to be quite reliable, and all the process names are successfully converted.
However, if I make this same call after passing WTS_PROCESS_INFO to another class and method, each attempt at converting to a string results in "" apart from the first attempt.
This only is only happening when using .NET-3.5 but seems to work fine in .NET-4.0
Example:
Working - each name is converted to the correct string before passing the entire list.
public static List<Proc> WTSEnumerateProcesses(string servername)
{
IntPtr pProcessInfo = IntPtr.Zero;
int processCount = 0;
var hServer = OpenServer(servername);
if (!Native.WTSEnumerateProcesses(hServer, 0, 1, ref pProcessInfo, ref processCount))
return null;
IntPtr pMemory = pProcessInfo;
WTS_PROCESS_INFO[] processInfos = new WTS_PROCESS_INFO[processCount];
for (int i = 0; i < processCount; i++)
{
processInfos[i] = (WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo, typeof(WTS_PROCESS_INFO));
pProcessInfo = (IntPtr)((int)pProcessInfo + Marshal.SizeOf(processInfos[i]));
}
List<Proc> asd = new List<Proc>();
foreach (var item in processInfos)
{
if (item.SessionID == 5)
{
string procname = Marshal.PtrToStringUni(item.ProcessName);
var proc = new Proc(servername, 5, item.SessionID, procname);
asd.Add(proc);
}
}
Native.WTSFreeMemory(pMemory);
return asd;
}
Not working - the conversions which take place after passing the WTS_PROCESS_INFOs out result in empty strings (apart from the first one).
public static WTS_PROCESS_INFO[] WTSEnumerateProcesses(string servername)
{
IntPtr pProcessInfo = IntPtr.Zero;
int processCount = 0;
var hServer = OpenServer(servername);
if (!Native.WTSEnumerateProcesses(hServer, 0, 1, ref pProcessInfo, ref processCount))
return null;
IntPtr pMemory = pProcessInfo;
WTS_PROCESS_INFO[] processInfos = new WTS_PROCESS_INFO[processCount];
for (int i = 0; i < processCount; i++)
{
processInfos[i] = (WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo, typeof(WTS_PROCESS_INFO));
pProcessInfo = (IntPtr)((int)pProcessInfo + Marshal.SizeOf(processInfos[i]));
}
Native.WTSFreeMemory(pMemory);
return processInfos;
}
Can anyone explain why this is happening?
Because you are calling Native.WTSFreeMemory(pMemory); that is freeing the memory associated with the strings. This call is freeing both the pMemory that is an array of WTS_PROCESS_INFO AND the memory pointed from the various pProcessName. So the solution is to copy somewhere those strings BEFORE calling Native.WTSFreeMemory(pMemory);
Related
As several blogposts and Stackoverflow answers suggest, it is trivial to get this information via a combination of get-process and Get-NetTCPConnection commandlets. It is possible to execute these commands via .net code, parse the output and retrieve the information.
Is it not possible to get the port number a process is listening on in pure .net code using just the .net libraries? System.Diagnostics.Process.GetProcesByName returns a ton of information via the Process object, except for the port the process is listening on.
Any leads highly appreciated.
Unfortunately, IPGlobalProperties.GetIPGlobalProperties() does not return any information on which process is holding the socket, as it uses GetTcpTable not GetTcpTable2.
You would need to code it yourself. The below code works for TCP over IPv4. You would need similar code for UDP and IPv6.
[DllImport("Iphlpapi.dll", ExactSpelling = true)]
static extern int GetTcpTable2(
IntPtr TcpTable,
ref int SizePointer,
bool Order
);
[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPTABLE
{
public int dwNumEntries;
}
[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPROW2
{
public MIB_TCP_STATE dwState;
public int dwLocalAddr;
public byte localPort1;
public byte localPort2;
// Ports are only 16 bit values (in network WORD order, 3,4,1,2).
// There are reports where the high order bytes have garbage in them.
public byte ignoreLocalPort3;
public byte ignoreLocalPort4;
public int dwRemoteAddr;
public byte remotePort1;
public byte remotePort2;
// Ports are only 16 bit values (in network WORD order, 3,4,1,2).
// There are reports where the high order bytes have garbage in them.
public byte ignoreremotePort3;
public byte ignoreremotePort4;
public int dwOwningPid;
public TCP_CONNECTION_OFFLOAD_STATE dwOffloadState;
}
public enum MIB_TCP_STATE
{
Closed = 1,
Listen,
SynSent,
SynRcvd,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
LastAck,
TimeWait,
DeleteTcb
}
enum TCP_CONNECTION_OFFLOAD_STATE
{
TcpConnectionOffloadStateInHost,
TcpConnectionOffloadStateOffloading,
TcpConnectionOffloadStateOffloaded,
TcpConnectionOffloadStateUploading,
TcpConnectionOffloadStateMax
}
static List<IPEndPoint> GetSocketsForProcess(int pid, MIB_TCP_STATE state = MIB_TCP_STATE.Established)
{
const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
var size = 0;
var result = GetTcpTable2(IntPtr.Zero, ref size, false);
if (result != ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(result);
var ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
result = GetTcpTable2(ptr, ref size, false);
if (result != 0)
throw new Win32Exception(result);
var list = new List<IPEndPoint>();
var count = Marshal.ReadInt32(ptr);
var curPtr = ptr + Marshal.SizeOf<MIB_TCPTABLE>();
var length = Marshal.SizeOf<MIB_TCPROW2>();
for(var i = 0; i < count; i++)
{
var row = Marshal.PtrToStructure<MIB_TCPROW2>(curPtr);
if(row.dwOwningPid == pid && row.dwState == state)
list.Add(new IPEndPoint(row.dwLocalAddr, row.localPort1 << 8 | row.localPort2));
curPtr += length;
}
return list;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
I had similar task not long ago. Here is complete .NET 6 code that you should be able to adopt for your particular needs:
public static int CheckConnection(string[] servers)
{
try
{
var srvs = servers.ToDictionary(k => k.Split("/", StringSplitOptions.RemoveEmptyEntries)[1], v => false);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] endPoints = ipProperties.GetActiveTcpListeners();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
var count = 0;
foreach (var info in tcpConnections)
{
var ep = string.Format("{0}:{1}", info.RemoteEndPoint.Address, info.RemoteEndPoint.Port);
if (info.State.ToString().ToUpper() != "ESTABLISHED")
continue;
if (srvs.ContainsKey(ep))
{
count++;
srvs[ep] = true;
}
}
var sb = new StringBuilder();
foreach (var kvp in srvs)
{
sb.AppendFormat("{0,-21}: {1}", kvp.Key, kvp.Value ? "OK" : "FAIL");
sb.AppendLine();
}
Program.logger.Trace($"ZMQF. Connection check:\n{sb}");
return count;
}
catch (Exception ex)
{
Program.logger.Fatal(ex, $"ZMQF. CheckConnection exception. Servers: {(string.Join(",", servers))}");
return -1;
}
}
I am trying to convert the dataObject's data to CF_HDROP format when a drag operation starts. I was able to read the byteArray, which I will store at some point, but for now I am hard-coding the path to a file that actually exists on my drive.
I have used this C# drag-drop problem as a reference.
Here my implementation for OleDragEnter function:
public int OleDragEnter(object pDataObj, int grfKeyState, long pt, ref int pdwEffect)
{
if (pDataObj is IDataObject dataOb)
{
IEnumFORMATETC formatetc = dataOb.EnumFormatEtc(DATADIR.DATADIR_GET);
List<FORMATETC> formatetcList = GetFormatEtcList(formatetc);
if (formatetcList.Exists(f => f.tymed == TYMED.TYMED_ISTREAM))
{
FORMATETC iStreamFormatetc = formatetcList.Find(f => f.tymed == TYMED.TYMED_ISTREAM);
dataOb.GetData(ref iStreamFormatetc, out var stgmedium);
MemoryStream memStream = GetIStream(stgmedium);
byte[] rawData = memStream.ToArray(); //I will store this on the drive at somepoint
var fileName = StreamHelpers.GetFullFileName(); //Constant for now
String[] strFiles = {fileName};
_DROPFILES dropFiles = new _DROPFILES();
int intChar, intFile, intDataLen, intPos;
IntPtr ipGlobal = IntPtr.Zero;
intDataLen = 0;
for (intFile = 0; intFile <= strFiles.GetUpperBound(0); intFile++)
{
intDataLen += strFiles[intFile].Length + 1;
}
intDataLen++;
var byteData = new Byte[intDataLen];
intPos = 0;
for (intFile = 0; intFile <= strFiles.GetUpperBound(0); intFile++)
{
for (intChar = 0; intChar < strFiles[intFile].Length; intChar++)
{
byteData[intPos++] = (byte)strFiles[intFile][intChar];
}
byteData[intPos++] = 0;
}
byteData[intPos++] = 0;
int intTotalLen = Marshal.SizeOf(dropFiles) + intDataLen;
ipGlobal = Marshal.AllocHGlobal(intTotalLen);
if (ipGlobal == IntPtr.Zero)
{
return -1;
}
dropFiles.pFiles = Marshal.SizeOf(dropFiles);
dropFiles.fWide = false;
Marshal.StructureToPtr(dropFiles, ipGlobal, false);
IntPtr ipNew = new IntPtr(ipGlobal.ToInt32() + Marshal.SizeOf(dropFiles));
Marshal.Copy(byteData, 0, ipNew, intDataLen);
var formatEtc = new FORMATETC
{
cfFormat = (short) CLIPFORMAT.CF_HDROP, //15
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = -1,
tymed = TYMED.TYMED_HGLOBAL,
ptd = IntPtr.Zero
};
var stgMedium = new STGMEDIUM
{
unionmember = ipGlobal,
tymed = TYMED.TYMED_HGLOBAL,
pUnkForRelease = IntPtr.Zero
};
dataOb.SetData(ref formatEtc, ref stgMedium, false);
}
}
return 0;
}
I keep getting "Invalid FORMATETC structure" error on:
dataOb.SetData(ref formatEtc, ref stgMedium, false);
I have tried running the application in non-debug mode & verified the integrity of the file. I noticed that DATADIR.DATADIR_SET does not return any available formats. Could that be the problem?
Edit: It seems to also be happening when I try to use the same formatEtc Object and stgMedium Object used for reading the data without any change.
[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
double X;
double Y;
}
var data = new Demo[128];
FillWithMeaningfulValues(data);
double[] doubles;
Copy(data, out doubles); // ?
How do I copy the demo array into the doubles array without having to for(...) through each element? In C++, I would use memcpy, but in C# I did not find what I need in Marshal.Copy.
void MethodIDoNotWantToUse(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
for(int i = 0, j = 0; i < demo.Length; ++i)
{
doubles[j++] = demo[i].X;
doubles[j++] = demo[i].Y;
}
}
void MethodIWouldPreferToUse(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
memcopy(doubles, demo, demo.Length * 2 * sizeof(double));
}
You'll do something like this. Marshal.Copy do provides you what you need.
Demo[] array = new Demo[2];
array[0] = new Demo {X = 5.6, Y= 6.6};
array[1] = new Demo {X = 7.6, Y = 8.6};
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
IntPtr pointer = handle.AddrOfPinnedObject();
double[] copy = new double[array.Length*2];//This length may be calculated
Marshal.Copy(pointer, copy, 0, copy.Length);
}
finally
{
if (handle.IsAllocated)
handle.Free();
}
Since the struct is blittable, the array of the struct is blittable. Therefore you can pin the array of struct and copy into the double array with Marshal.Copy.
void CopyDemoArrayToDoubleArray(Demo[] demo, out double[] doubles)
{
doubles = new double[demo.Length * 2];
GCHandle gch = GCHandle.Alloc(demo, GCHandleType.Pinned);
try
{
IntPtr demoPtr = gch.AddrOfPinnedObject();
Marshal.Copy(demoPtr, doubles, 0, doubles.Length);
}
finally
{
gch.Free();
}
}
You might do well to benchmark this against the simpler for loop that you want to avoid. It is plausible that the for loop will perform perfectly adequately.
It's possible to write a generic method that can convert arrays of any compatible type (by "compatible" I mean "elements must be value types and the size of the elements must be compatible").
You can use P/Invoke to call the Windows API CopyMemory() method.
However, bear in mind that there may not be any performance advantage to doing it this way; you should perform careful timings to be sure.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
public TOut[] ConvertArray<TIn, TOut>(TIn[] input) where TIn:struct where TOut:struct
{
if (input == null)
throw new ArgumentNullException("input");
int sizeTIn = Marshal.SizeOf(typeof(TIn));
int sizeTOut = Marshal.SizeOf(typeof(TOut));
int sizeBytes = input.Length*sizeTIn;
if ((sizeBytes % sizeTOut) != 0)
throw new ArgumentException("Size of input type is not compatible with size of output type.");
int sizeOut = sizeBytes/sizeTOut;
var output = new TOut[sizeOut];
GCHandle inHandle = GCHandle.Alloc(input, GCHandleType.Pinned);
GCHandle outHandle = GCHandle.Alloc(output, GCHandleType.Pinned);
try
{
IntPtr inPtr = inHandle.AddrOfPinnedObject();
IntPtr outPtr = outHandle.AddrOfPinnedObject();
CopyMemory(outPtr, inPtr, (uint)sizeBytes);
}
finally
{
outHandle.Free();
inHandle.Free();
}
return output;
}
For your example, you could call this like so:
Demo[] test = new Demo[10];
for (int i = 0; i < 10; ++i)
test[i] = new Demo {X = i, Y = i};
var result = ConvertArray<Demo, double>(test);
for (int i = 0; i < 20; ++i)
Console.WriteLine(result[i]);
I need to use a native dll struct in my application.
struct da_i2k_input_file_info in the dll .h file is
struct DA_I2K_EXPORT_API da_i2k_input_file_info {
const WDCHAR * image_path;
const WDCHAR ** image_files;
int num_images;
};
and this is what user code might look like if written in C++
in_file_info.num_images = 3;
in_file_info.image_files = new const WDCHAR*[in_file_info.num_images];
in_file_info.image_files[0] = WSTR("IMG_8670.JPG");
in_file_info.image_files[1] = WSTR("IMG_8671.JPG");
in_file_info.image_files[2] = WSTR("IMG_8672.JPG");
But this C# code
[StructLayout(LayoutKind.Sequential)]
public struct da_i2k_input_file_info
{
[MarshalAs(UnmanagedType.LPTStr)]
public string image_path;
public IntPtr image_files;
public int num_images;
}
var openFileDialog = new OpenFileDialog{Multiselect = true};
da_i2k_input_file_info in_file_info;
in_file_info.image_path = null; // use full path in .image_files
in_file_info.num_images = openFileDialog.FileNames.Length;
in_file_info.image_files = openFileDialog.FileNames;
Causes this error
Cannot implicitly convert type 'string[]' to 'System.IntPtr'
Casting openFileDialog.FileNames as IntPtr does not help.
How can I load in_file_info.image_files from openFileDialog.FileNames?
Edit: OP cross posted this here
I believe you need to change you struct to be as follows:
[StructLayout(LayoutKind.Sequential)]
public struct da_i2k_input_file_info
{
[MarshalAs(UnmanagedType.LPTStr)]
public string image_path;
[MarshalAs(UnmanagedType.LPArray)]
public string[] image_files;
public int num_images;
}
A working answer was posted here.
Code follows.
(included by tomfanning)
private static void DoTest()
{
var openFileDialog = new OpenFileDialog { Multiselect = true };
da_i2k_input_file_info in_file_info;
openFileDialog.ShowDialog();
in_file_info.image_path = null; // use full path in .image_files
in_file_info.num_images = openFileDialog.FileNames.Length;
// Create an array of IntPtrs.
IntPtr[] image_files_array = new IntPtr[in_file_info.num_images];
// Each IntPtr array element will point to a copy of a
// string element in the openFileDialog.FileNames array.
for (int i = 0; i < openFileDialog.FileNames.Length; i++)
{
image_files_array[i] = Marshal.StringToCoTaskMemUni(openFileDialog.FileNames[i]);
}
// In order to obtain the address of the IntPtr array,
// we must fix it in memory. We do this using GCHandle.
GCHandle gch = GCHandle.Alloc(image_files_array, GCHandleType.Pinned);
// pimage_files will point to the head of the IntPtr array.
IntPtr pimage_files = gch.AddrOfPinnedObject();
// Set pimage_files as the value of the "image_files" field/
in_file_info.image_files = pimage_files;
// Call a Test API.
TestStructure(in_file_info);
// After the API is called, free the GCHandle.
gch.Free();
// Free each string contained in the IntPtr array.
for (int i = 0; i < openFileDialog.FileNames.Length; i++)
{
Marshal.FreeCoTaskMem(image_files_array[i]);
}
}
I have this piece of code and it generates an error:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyItem
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Name;
public int ID;
public double ID1;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MyItem[] items = new MyItem[6];
items[0].Name = "JFK";
items[0].ID = 35;
items[1].Name = "LBJ";
items[1].ID = 36;
items[2].Name = "Tricky Dicky";
items[2].ID = 37;
items[3].Name = "Gerald Ford";
items[3].ID = 38;
items[4].Name = "Jimmy Carter";
items[4].ID = 39;
items[5].Name = "Ronald Reagan";
items[5].ID = 40;
IntPtr itemsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyItem)) *
items.Length);
try
{
IntPtr item = new IntPtr(itemsPtr.ToInt32());
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], item, true);
item = new IntPtr(item.ToInt32() + Marshal.SizeOf(typeof(MyItem)));
}
}
finally
{
Marshal.FreeHGlobal(itemsPtr);
}
When I run this code, I am getting write protection error in
Marshal.StructureToPtr(items[i], item, true);
What is the problem and how do I solve it?
You should be passing false to the fDeleteOld parameter of StructureToPtr().
By passing true you are asking the marshaller to delete the contents of item. Since you are filling this out for the first time, this results in a memory access failure because the memory is not yet valid.
The documentation states:
StructureToPtr copies the contents of
structure to the pre-allocated block of
memory that the ptr parameter points
to. If the fDeleteOld parameter is true,
the pre-allocated buffer is deleted with
the appropriate deletion method on the embedded pointer, but the buffer
must contain valid data.
The emphasis is mine.
Incidentally I think the loop code looks neater like this:
Int32 addr = itemsPtr.ToInt32();
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], new IntPtr(addr), false);
addr += Marshal.SizeOf(typeof(MyItem));
}